diff --git a/.gitignore b/.gitignore index 69504a8c2..59839feff 100644 --- a/.gitignore +++ b/.gitignore @@ -17,4 +17,4 @@ Dockerfile /rocketpool/rocketpool-daemon-darwin-arm64 /rocketpool/rocketpool-daemon-linux-arm64 .vscode-ctags -build/ \ No newline at end of file +build/ diff --git a/Makefile b/Makefile index aebaaf63c..1ddca4d4a 100644 --- a/Makefile +++ b/Makefile @@ -14,6 +14,7 @@ TREEGEN_TARGET_STRINGS:=$(foreach arch,$(ARCHS),${BIN_DIR}/treegen-linux-$(arch) MODULES:=$(foreach path,$(shell find . -name go.mod),$(dir $(path))) MODULE_GLOBS:=$(foreach module,$(MODULES),$(module)...) +TEST_GLOBS:=$(filter-out ./bindings/...,$(MODULE_GLOBS)) define rocketpool-cli-template .PHONY: ${BIN_DIR}/rocketpool-cli-$1-$2 @@ -153,7 +154,7 @@ lint: $(foreach module,$(MODULES),lint-$(module)) .PHONY: test test: - go test -test.timeout 20m $(MODULE_GLOBS) + go test -test.timeout 20m $(TEST_GLOBS) .PHONY: clean clean: diff --git a/bindings/LICENSE b/bindings/LICENSE new file mode 100644 index 000000000..94a9ed024 --- /dev/null +++ b/bindings/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/bindings/README.md b/bindings/README.md new file mode 100644 index 000000000..8a595cc3c --- /dev/null +++ b/bindings/README.md @@ -0,0 +1,2 @@ +# rocketpool-go +A Golang library for interacting with the Rocket Pool network. diff --git a/bindings/auction/auction.go b/bindings/auction/auction.go new file mode 100644 index 000000000..1f750e420 --- /dev/null +++ b/bindings/auction/auction.go @@ -0,0 +1,606 @@ +package auction + +import ( + "fmt" + "math/big" + "sync" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "golang.org/x/sync/errgroup" + + "github.com/rocket-pool/smartnode/bindings/rocketpool" +) + +// Settings +const LotDetailsBatchSize = 10 + +// Lot details +type LotDetails struct { + Index uint64 `json:"index"` + Exists bool `json:"exists"` + StartBlock uint64 `json:"startBlock"` + EndBlock uint64 `json:"endBlock"` + StartPrice *big.Int `json:"startPrice"` + ReservePrice *big.Int `json:"reservePrice"` + PriceAtCurrentBlock *big.Int `json:"priceAtCurrentBlock"` + PriceByTotalBids *big.Int `json:"priceByTotalBids"` + CurrentPrice *big.Int `json:"currentPrice"` + TotalRPLAmount *big.Int `json:"totalRplAmount"` + ClaimedRPLAmount *big.Int `json:"claimedRplAmount"` + RemainingRPLAmount *big.Int `json:"remainingRplAmount"` + TotalBidAmount *big.Int `json:"totalBidAmount"` + AddressBidAmount *big.Int `json:"addressBidAmount"` + Cleared bool `json:"cleared"` + RPLRecovered bool `json:"rplRecovered"` +} + +// Get all lot details +func GetLots(rp *rocketpool.RocketPool, opts *bind.CallOpts) ([]LotDetails, error) { + + // Get lot count + lotCount, err := GetLotCount(rp, opts) + if err != nil { + return []LotDetails{}, err + } + + // Load lot details in batches + details := make([]LotDetails, lotCount) + for bsi := uint64(0); bsi < lotCount; bsi += LotDetailsBatchSize { + + // Get batch start & end index + lsi := bsi + lei := bsi + LotDetailsBatchSize + if lei > lotCount { + lei = lotCount + } + + // Load details + var wg errgroup.Group + for li := lsi; li < lei; li++ { + li := li + wg.Go(func() error { + lotDetails, err := GetLotDetails(rp, li, opts) + if err == nil { + details[li] = lotDetails + } + return err + }) + } + if err := wg.Wait(); err != nil { + return []LotDetails{}, err + } + + } + + // Return + return details, nil + +} + +// Get all lot details with bids from an address +func GetLotsWithBids(rp *rocketpool.RocketPool, bidder common.Address, opts *bind.CallOpts) ([]LotDetails, error) { + + // Get lot count + lotCount, err := GetLotCount(rp, opts) + if err != nil { + return []LotDetails{}, err + } + + // Load lot details in batches + details := make([]LotDetails, lotCount) + for bsi := uint64(0); bsi < lotCount; bsi += LotDetailsBatchSize { + + // Get batch start & end index + lsi := bsi + lei := bsi + LotDetailsBatchSize + if lei > lotCount { + lei = lotCount + } + + // Load details + var wg errgroup.Group + for li := lsi; li < lei; li++ { + li := li + wg.Go(func() error { + lotDetails, err := GetLotDetailsWithBids(rp, li, bidder, opts) + if err == nil { + details[li] = lotDetails + } + return err + }) + } + if err := wg.Wait(); err != nil { + return []LotDetails{}, err + } + + } + + // Return + return details, nil + +} + +// Get a lot's details +func GetLotDetails(rp *rocketpool.RocketPool, lotIndex uint64, opts *bind.CallOpts) (LotDetails, error) { + + // Data + var wg errgroup.Group + var exists bool + var startBlock uint64 + var endBlock uint64 + var startPrice *big.Int + var reservePrice *big.Int + var priceAtCurrentBlock *big.Int + var priceByTotalBids *big.Int + var currentPrice *big.Int + var totalRplAmount *big.Int + var claimedRplAmount *big.Int + var remainingRplAmount *big.Int + var totalBidAmount *big.Int + var cleared bool + var rplRecovered bool + + // Load data + wg.Go(func() error { + var err error + exists, err = GetLotExists(rp, lotIndex, opts) + return err + }) + wg.Go(func() error { + var err error + startBlock, err = GetLotStartBlock(rp, lotIndex, opts) + return err + }) + wg.Go(func() error { + var err error + endBlock, err = GetLotEndBlock(rp, lotIndex, opts) + return err + }) + wg.Go(func() error { + var err error + startPrice, err = GetLotStartPrice(rp, lotIndex, opts) + return err + }) + wg.Go(func() error { + var err error + reservePrice, err = GetLotReservePrice(rp, lotIndex, opts) + return err + }) + wg.Go(func() error { + var err error + priceAtCurrentBlock, err = GetLotPriceAtCurrentBlock(rp, lotIndex, opts) + return err + }) + wg.Go(func() error { + var err error + priceByTotalBids, err = GetLotPriceByTotalBids(rp, lotIndex, opts) + return err + }) + wg.Go(func() error { + var err error + currentPrice, err = GetLotCurrentPrice(rp, lotIndex, opts) + return err + }) + wg.Go(func() error { + var err error + totalRplAmount, err = GetLotTotalRPLAmount(rp, lotIndex, opts) + return err + }) + wg.Go(func() error { + var err error + claimedRplAmount, err = GetLotClaimedRPLAmount(rp, lotIndex, opts) + return err + }) + wg.Go(func() error { + var err error + remainingRplAmount, err = GetLotRemainingRPLAmount(rp, lotIndex, opts) + return err + }) + wg.Go(func() error { + var err error + totalBidAmount, err = GetLotTotalBidAmount(rp, lotIndex, opts) + return err + }) + wg.Go(func() error { + var err error + cleared, err = GetLotIsCleared(rp, lotIndex, opts) + return err + }) + wg.Go(func() error { + var err error + rplRecovered, err = GetLotRPLRecovered(rp, lotIndex, opts) + return err + }) + + // Wait for data + if err := wg.Wait(); err != nil { + return LotDetails{}, err + } + + // Return + return LotDetails{ + Index: lotIndex, + Exists: exists, + StartBlock: startBlock, + EndBlock: endBlock, + StartPrice: startPrice, + ReservePrice: reservePrice, + PriceAtCurrentBlock: priceAtCurrentBlock, + PriceByTotalBids: priceByTotalBids, + CurrentPrice: currentPrice, + TotalRPLAmount: totalRplAmount, + ClaimedRPLAmount: claimedRplAmount, + RemainingRPLAmount: remainingRplAmount, + TotalBidAmount: totalBidAmount, + Cleared: cleared, + RPLRecovered: rplRecovered, + }, nil + +} + +// Get a lot's details with address bid amounts +func GetLotDetailsWithBids(rp *rocketpool.RocketPool, lotIndex uint64, bidder common.Address, opts *bind.CallOpts) (LotDetails, error) { + + // Data + var wg errgroup.Group + var details LotDetails + var addressBidAmount *big.Int + + // Load data + wg.Go(func() error { + var err error + details, err = GetLotDetails(rp, lotIndex, opts) + return err + }) + wg.Go(func() error { + var err error + addressBidAmount, err = GetLotAddressBidAmount(rp, lotIndex, bidder, opts) + return err + }) + + // Wait for data + if err := wg.Wait(); err != nil { + return LotDetails{}, err + } + + // Return + details.AddressBidAmount = addressBidAmount + return details, nil + +} + +// Get the total RPL balance of the auction contract +func GetTotalRPLBalance(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*big.Int, error) { + rocketAuctionManager, err := getRocketAuctionManager(rp, opts) + if err != nil { + return nil, err + } + totalRplBalance := new(*big.Int) + if err := rocketAuctionManager.Call(opts, totalRplBalance, "getTotalRPLBalance"); err != nil { + return nil, fmt.Errorf("error getting auction contract total RPL balance: %w", err) + } + return *totalRplBalance, nil +} + +// Get the allotted RPL balance of the auction contract +func GetAllottedRPLBalance(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*big.Int, error) { + rocketAuctionManager, err := getRocketAuctionManager(rp, opts) + if err != nil { + return nil, err + } + allottedRplBalance := new(*big.Int) + if err := rocketAuctionManager.Call(opts, allottedRplBalance, "getAllottedRPLBalance"); err != nil { + return nil, fmt.Errorf("error getting auction contract allotted RPL balance: %w", err) + } + return *allottedRplBalance, nil +} + +// Get the remaining RPL balance of the auction contract +func GetRemainingRPLBalance(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*big.Int, error) { + rocketAuctionManager, err := getRocketAuctionManager(rp, opts) + if err != nil { + return nil, err + } + remainingRplBalance := new(*big.Int) + if err := rocketAuctionManager.Call(opts, remainingRplBalance, "getRemainingRPLBalance"); err != nil { + return nil, fmt.Errorf("error getting auction contract remaining RPL balance: %w", err) + } + return *remainingRplBalance, nil +} + +// Get the number of lots for auction +func GetLotCount(rp *rocketpool.RocketPool, opts *bind.CallOpts) (uint64, error) { + rocketAuctionManager, err := getRocketAuctionManager(rp, opts) + if err != nil { + return 0, err + } + lotCount := new(*big.Int) + if err := rocketAuctionManager.Call(opts, lotCount, "getLotCount"); err != nil { + return 0, fmt.Errorf("error getting lot count: %w", err) + } + return (*lotCount).Uint64(), nil +} + +// Lot details +func GetLotExists(rp *rocketpool.RocketPool, lotIndex uint64, opts *bind.CallOpts) (bool, error) { + rocketAuctionManager, err := getRocketAuctionManager(rp, opts) + if err != nil { + return false, err + } + lotExists := new(bool) + if err := rocketAuctionManager.Call(opts, lotExists, "getLotExists", big.NewInt(int64(lotIndex))); err != nil { + return false, fmt.Errorf("error getting lot %d exists status: %w", lotIndex, err) + } + return *lotExists, nil +} +func GetLotStartBlock(rp *rocketpool.RocketPool, lotIndex uint64, opts *bind.CallOpts) (uint64, error) { + rocketAuctionManager, err := getRocketAuctionManager(rp, opts) + if err != nil { + return 0, err + } + lotStartBlock := new(*big.Int) + if err := rocketAuctionManager.Call(opts, lotStartBlock, "getLotStartBlock", big.NewInt(int64(lotIndex))); err != nil { + return 0, fmt.Errorf("error getting lot %d start block: %w", lotIndex, err) + } + return (*lotStartBlock).Uint64(), nil +} +func GetLotEndBlock(rp *rocketpool.RocketPool, lotIndex uint64, opts *bind.CallOpts) (uint64, error) { + rocketAuctionManager, err := getRocketAuctionManager(rp, opts) + if err != nil { + return 0, err + } + lotEndBlock := new(*big.Int) + if err := rocketAuctionManager.Call(opts, lotEndBlock, "getLotEndBlock", big.NewInt(int64(lotIndex))); err != nil { + return 0, fmt.Errorf("error getting lot %d end block: %w", lotIndex, err) + } + return (*lotEndBlock).Uint64(), nil +} +func GetLotStartPrice(rp *rocketpool.RocketPool, lotIndex uint64, opts *bind.CallOpts) (*big.Int, error) { + rocketAuctionManager, err := getRocketAuctionManager(rp, opts) + if err != nil { + return nil, err + } + lotStartPrice := new(*big.Int) + if err := rocketAuctionManager.Call(opts, lotStartPrice, "getLotStartPrice", big.NewInt(int64(lotIndex))); err != nil { + return nil, fmt.Errorf("error getting lot %d start price: %w", lotIndex, err) + } + return *lotStartPrice, nil +} +func GetLotReservePrice(rp *rocketpool.RocketPool, lotIndex uint64, opts *bind.CallOpts) (*big.Int, error) { + rocketAuctionManager, err := getRocketAuctionManager(rp, opts) + if err != nil { + return nil, err + } + lotReservePrice := new(*big.Int) + if err := rocketAuctionManager.Call(opts, lotReservePrice, "getLotReservePrice", big.NewInt(int64(lotIndex))); err != nil { + return nil, fmt.Errorf("error getting lot %d reserve price: %w", lotIndex, err) + } + return *lotReservePrice, nil +} +func GetLotTotalRPLAmount(rp *rocketpool.RocketPool, lotIndex uint64, opts *bind.CallOpts) (*big.Int, error) { + rocketAuctionManager, err := getRocketAuctionManager(rp, opts) + if err != nil { + return nil, err + } + lotTotalRplAmount := new(*big.Int) + if err := rocketAuctionManager.Call(opts, lotTotalRplAmount, "getLotTotalRPLAmount", big.NewInt(int64(lotIndex))); err != nil { + return nil, fmt.Errorf("error getting lot %d total RPL amount: %w", lotIndex, err) + } + return *lotTotalRplAmount, nil +} +func GetLotTotalBidAmount(rp *rocketpool.RocketPool, lotIndex uint64, opts *bind.CallOpts) (*big.Int, error) { + rocketAuctionManager, err := getRocketAuctionManager(rp, opts) + if err != nil { + return nil, err + } + lotTotalBidAmount := new(*big.Int) + if err := rocketAuctionManager.Call(opts, lotTotalBidAmount, "getLotTotalBidAmount", big.NewInt(int64(lotIndex))); err != nil { + return nil, fmt.Errorf("error getting lot %d total ETH bid amount: %w", lotIndex, err) + } + return *lotTotalBidAmount, nil +} +func GetLotRPLRecovered(rp *rocketpool.RocketPool, lotIndex uint64, opts *bind.CallOpts) (bool, error) { + rocketAuctionManager, err := getRocketAuctionManager(rp, opts) + if err != nil { + return false, err + } + lotRplRecovered := new(bool) + if err := rocketAuctionManager.Call(opts, lotRplRecovered, "getLotRPLRecovered", big.NewInt(int64(lotIndex))); err != nil { + return false, fmt.Errorf("error getting lot %d RPL recovered status: %w", lotIndex, err) + } + return *lotRplRecovered, nil +} +func GetLotPriceAtCurrentBlock(rp *rocketpool.RocketPool, lotIndex uint64, opts *bind.CallOpts) (*big.Int, error) { + rocketAuctionManager, err := getRocketAuctionManager(rp, opts) + if err != nil { + return nil, err + } + lotPriceAtCurrentBlock := new(*big.Int) + if err := rocketAuctionManager.Call(opts, lotPriceAtCurrentBlock, "getLotPriceAtCurrentBlock", big.NewInt(int64(lotIndex))); err != nil { + return nil, fmt.Errorf("error getting lot %d price by current block: %w", lotIndex, err) + } + return *lotPriceAtCurrentBlock, nil +} +func GetLotPriceByTotalBids(rp *rocketpool.RocketPool, lotIndex uint64, opts *bind.CallOpts) (*big.Int, error) { + rocketAuctionManager, err := getRocketAuctionManager(rp, opts) + if err != nil { + return nil, err + } + lotPriceByTotalBids := new(*big.Int) + if err := rocketAuctionManager.Call(opts, lotPriceByTotalBids, "getLotPriceByTotalBids", big.NewInt(int64(lotIndex))); err != nil { + return nil, fmt.Errorf("error getting lot %d price by total bids: %w", lotIndex, err) + } + return *lotPriceByTotalBids, nil +} +func GetLotCurrentPrice(rp *rocketpool.RocketPool, lotIndex uint64, opts *bind.CallOpts) (*big.Int, error) { + rocketAuctionManager, err := getRocketAuctionManager(rp, opts) + if err != nil { + return nil, err + } + lotCurrentPrice := new(*big.Int) + if err := rocketAuctionManager.Call(opts, lotCurrentPrice, "getLotCurrentPrice", big.NewInt(int64(lotIndex))); err != nil { + return nil, fmt.Errorf("error getting lot %d current price: %w", lotIndex, err) + } + return *lotCurrentPrice, nil +} +func GetLotClaimedRPLAmount(rp *rocketpool.RocketPool, lotIndex uint64, opts *bind.CallOpts) (*big.Int, error) { + rocketAuctionManager, err := getRocketAuctionManager(rp, opts) + if err != nil { + return nil, err + } + lotClaimedRplAmount := new(*big.Int) + if err := rocketAuctionManager.Call(opts, lotClaimedRplAmount, "getLotClaimedRPLAmount", big.NewInt(int64(lotIndex))); err != nil { + return nil, fmt.Errorf("error getting lot %d claimed RPL amount: %w", lotIndex, err) + } + return *lotClaimedRplAmount, nil +} +func GetLotRemainingRPLAmount(rp *rocketpool.RocketPool, lotIndex uint64, opts *bind.CallOpts) (*big.Int, error) { + rocketAuctionManager, err := getRocketAuctionManager(rp, opts) + if err != nil { + return nil, err + } + lotRemainingRplAmount := new(*big.Int) + if err := rocketAuctionManager.Call(opts, lotRemainingRplAmount, "getLotRemainingRPLAmount", big.NewInt(int64(lotIndex))); err != nil { + return nil, fmt.Errorf("error getting lot %d remaining RPL amount: %w", lotIndex, err) + } + return *lotRemainingRplAmount, nil +} +func GetLotIsCleared(rp *rocketpool.RocketPool, lotIndex uint64, opts *bind.CallOpts) (bool, error) { + rocketAuctionManager, err := getRocketAuctionManager(rp, opts) + if err != nil { + return false, err + } + lotIsCleared := new(bool) + if err := rocketAuctionManager.Call(opts, lotIsCleared, "getLotIsCleared", big.NewInt(int64(lotIndex))); err != nil { + return false, fmt.Errorf("error getting lot %d cleared status: %w", lotIndex, err) + } + return *lotIsCleared, nil +} + +// Get the price of a lot at a specific block +func GetLotPriceAtBlock(rp *rocketpool.RocketPool, lotIndex, blockNumber uint64, opts *bind.CallOpts) (*big.Int, error) { + rocketAuctionManager, err := getRocketAuctionManager(rp, opts) + if err != nil { + return nil, err + } + lotPriceAtBlock := new(*big.Int) + if err := rocketAuctionManager.Call(opts, lotPriceAtBlock, "getLotPriceAtBlock", big.NewInt(int64(lotIndex)), big.NewInt(int64(blockNumber))); err != nil { + return nil, fmt.Errorf("error getting lot %d price at block: %w", lotIndex, err) + } + return *lotPriceAtBlock, nil +} + +// Get the ETH amount bid on a lot by an address +func GetLotAddressBidAmount(rp *rocketpool.RocketPool, lotIndex uint64, bidder common.Address, opts *bind.CallOpts) (*big.Int, error) { + rocketAuctionManager, err := getRocketAuctionManager(rp, opts) + if err != nil { + return nil, err + } + lot := new(*big.Int) + if err := rocketAuctionManager.Call(opts, lot, "getLotAddressBidAmount", big.NewInt(int64(lotIndex)), bidder); err != nil { + return nil, fmt.Errorf("error getting lot %d address ETH bid amount: %w", lotIndex, err) + } + return *lot, nil +} + +// Estimate the gas of CreateLot +func EstimateCreateLotGas(rp *rocketpool.RocketPool, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketAuctionManager, err := getRocketAuctionManager(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketAuctionManager.GetTransactionGasInfo(opts, "createLot") +} + +// Create a new lot +func CreateLot(rp *rocketpool.RocketPool, opts *bind.TransactOpts) (uint64, common.Hash, error) { + rocketAuctionManager, err := getRocketAuctionManager(rp, nil) + if err != nil { + return 0, common.Hash{}, err + } + lotCount, err := GetLotCount(rp, nil) + if err != nil { + return 0, common.Hash{}, err + } + tx, err := rocketAuctionManager.Transact(opts, "createLot") + if err != nil { + return 0, common.Hash{}, fmt.Errorf("error creating lot: %w", err) + } + return lotCount, tx.Hash(), nil +} + +// Estimate the gas of PlaceBid +func EstimatePlaceBidGas(rp *rocketpool.RocketPool, lotIndex uint64, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketAuctionManager, err := getRocketAuctionManager(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketAuctionManager.GetTransactionGasInfo(opts, "placeBid", big.NewInt(int64(lotIndex))) +} + +// Place a bid on a lot +func PlaceBid(rp *rocketpool.RocketPool, lotIndex uint64, opts *bind.TransactOpts) (common.Hash, error) { + rocketAuctionManager, err := getRocketAuctionManager(rp, nil) + if err != nil { + return common.Hash{}, err + } + tx, err := rocketAuctionManager.Transact(opts, "placeBid", big.NewInt(int64(lotIndex))) + if err != nil { + return common.Hash{}, fmt.Errorf("error placing bid on lot %d: %w", lotIndex, err) + } + return tx.Hash(), nil +} + +// Estimate the gas of ClaimBid +func EstimateClaimBidGas(rp *rocketpool.RocketPool, lotIndex uint64, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketAuctionManager, err := getRocketAuctionManager(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketAuctionManager.GetTransactionGasInfo(opts, "claimBid", big.NewInt(int64(lotIndex))) +} + +// Claim RPL from a lot that was bid on +func ClaimBid(rp *rocketpool.RocketPool, lotIndex uint64, opts *bind.TransactOpts) (common.Hash, error) { + rocketAuctionManager, err := getRocketAuctionManager(rp, nil) + if err != nil { + return common.Hash{}, err + } + tx, err := rocketAuctionManager.Transact(opts, "claimBid", big.NewInt(int64(lotIndex))) + if err != nil { + return common.Hash{}, fmt.Errorf("error claiming bid from lot %d: %w", lotIndex, err) + } + return tx.Hash(), nil +} + +// Estimate the gas of RecoverUnclaimedRPL +func EstimateRecoverUnclaimedRPLGas(rp *rocketpool.RocketPool, lotIndex uint64, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketAuctionManager, err := getRocketAuctionManager(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketAuctionManager.GetTransactionGasInfo(opts, "recoverUnclaimedRPL", big.NewInt(int64(lotIndex))) +} + +// Recover unclaimed RPL from a lot +func RecoverUnclaimedRPL(rp *rocketpool.RocketPool, lotIndex uint64, opts *bind.TransactOpts) (common.Hash, error) { + rocketAuctionManager, err := getRocketAuctionManager(rp, nil) + if err != nil { + return common.Hash{}, err + } + tx, err := rocketAuctionManager.Transact(opts, "recoverUnclaimedRPL", big.NewInt(int64(lotIndex))) + if err != nil { + return common.Hash{}, fmt.Errorf("error recovering unclaimed RPL from lot %d: %w", lotIndex, err) + } + return tx.Hash(), nil +} + +// Get contracts +var rocketAuctionManagerLock sync.Mutex + +func getRocketAuctionManager(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*rocketpool.Contract, error) { + rocketAuctionManagerLock.Lock() + defer rocketAuctionManagerLock.Unlock() + return rp.GetContract("rocketAuctionManager", opts) +} diff --git a/bindings/azure-pipelines.yml b/bindings/azure-pipelines.yml new file mode 100644 index 000000000..a1338bf3a --- /dev/null +++ b/bindings/azure-pipelines.yml @@ -0,0 +1,36 @@ +# Go +# Build your Go project. +# Add steps that test, save build artifacts, deploy, and more: +# https://docs.microsoft.com/azure/devops/pipelines/languages/go + +trigger: + branches: + include: + - '*' + - refs/tags/* + +pool: + vmImage: ubuntu-latest + +steps: +- task: DownloadSecureFile@1 + name: githubPEM + displayName: 'Download Github PEM' + inputs: + secureFile: 'rp-azure-pipeline-github.pem' +- bash: | + eval $(ruby -e "require 'openssl'; require 'jwt'; private_pem = File.read(ENV['GITHUB_PEM_PATH']); private_key = OpenSSL::PKey::RSA.new(private_pem); payload = { iat: Time.now.to_i - 60, exp: Time.now.to_i + (10 * 60), iss: ENV['GITHUB_APP_ID'] }; jwt = JWT.encode(payload, private_key, 'RS256'); puts 'PUSH_JWT='+jwt;") + TOKEN=$(curl -s -X POST \ + -H "Authorization: Bearer $PUSH_JWT" \ + -H "Accept: application/vnd.github.v3+json" \ + https://api.github.com/app/installations/$GITHUB_APP_INSTALLATION_ID/access_tokens \ + | jq -r '.token') + git remote add github https://x-access-token:$TOKEN@github.com/rocket-pool/$REPO_NAME + git fetch github + git push github HEAD:$(Build.SourceBranch) -f --verbose + git push github HEAD:$(Build.SourceBranch) --tags --verbose + displayName: 'Push to Github' + env: + GITHUB_PEM_PATH: $(githubPEM.secureFilePath) + GITHUB_APP_ID: $(GITHUB_APP_ID) + GITHUB_APP_INSTALLATION_ID: $(GITHUB_APP_INSTALLATION_ID) diff --git a/bindings/contracts/rocket-storage.go b/bindings/contracts/rocket-storage.go new file mode 100644 index 000000000..174c7a8a5 --- /dev/null +++ b/bindings/contracts/rocket-storage.go @@ -0,0 +1,683 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package contracts + +import ( + "math/big" + "strings" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription +) + +// RocketStorageABI is the input ABI used to generate the binding from. +const RocketStorageABI = "[{\"inputs\": [],\"stateMutability\": \"nonpayable\",\"type\": \"constructor\"},{\"anonymous\": false,\"inputs\": [{\"indexed\": false,\"internalType\": \"address\",\"name\": \"oldGuardian\",\"type\": \"address\"},{\"indexed\": false,\"internalType\": \"address\",\"name\": \"newGuardian\",\"type\": \"address\"}],\"name\": \"GuardianChanged\",\"type\": \"event\"},{\"anonymous\": false,\"inputs\": [{\"indexed\": true,\"internalType\": \"address\",\"name\": \"node\",\"type\": \"address\"},{\"indexed\": true,\"internalType\": \"address\",\"name\": \"withdrawalAddress\",\"type\": \"address\"},{\"indexed\": false,\"internalType\": \"uint256\",\"name\": \"time\",\"type\": \"uint256\"}],\"name\": \"NodeWithdrawalAddressSet\",\"type\": \"event\"},{\"inputs\": [],\"name\": \"getGuardian\",\"outputs\": [{\"internalType\": \"address\",\"name\": \"\",\"type\": \"address\"}],\"stateMutability\": \"view\",\"type\": \"function\",\"constant\": true},{\"inputs\": [{\"internalType\": \"address\",\"name\": \"_newAddress\",\"type\": \"address\"}],\"name\": \"setGuardian\",\"outputs\": [],\"stateMutability\": \"nonpayable\",\"type\": \"function\"},{\"inputs\": [],\"name\": \"confirmGuardian\",\"outputs\": [],\"stateMutability\": \"nonpayable\",\"type\": \"function\"},{\"inputs\": [],\"name\": \"getDeployedStatus\",\"outputs\": [{\"internalType\": \"bool\",\"name\": \"\",\"type\": \"bool\"}],\"stateMutability\": \"view\",\"type\": \"function\",\"constant\": true},{\"inputs\": [],\"name\": \"setDeployedStatus\",\"outputs\": [],\"stateMutability\": \"nonpayable\",\"type\": \"function\"},{\"inputs\": [{\"internalType\": \"address\",\"name\": \"_nodeAddress\",\"type\": \"address\"}],\"name\": \"getNodeWithdrawalAddress\",\"outputs\": [{\"internalType\": \"address\",\"name\": \"\",\"type\": \"address\"}],\"stateMutability\": \"view\",\"type\": \"function\",\"constant\": true},{\"inputs\": [{\"internalType\": \"address\",\"name\": \"_nodeAddress\",\"type\": \"address\"}],\"name\": \"getNodePendingWithdrawalAddress\",\"outputs\": [{\"internalType\": \"address\",\"name\": \"\",\"type\": \"address\"}],\"stateMutability\": \"view\",\"type\": \"function\",\"constant\": true},{\"inputs\": [{\"internalType\": \"address\",\"name\": \"_nodeAddress\",\"type\": \"address\"},{\"internalType\": \"address\",\"name\": \"_newWithdrawalAddress\",\"type\": \"address\"},{\"internalType\": \"bool\",\"name\": \"_confirm\",\"type\": \"bool\"}],\"name\": \"setWithdrawalAddress\",\"outputs\": [],\"stateMutability\": \"nonpayable\",\"type\": \"function\"},{\"inputs\": [{\"internalType\": \"address\",\"name\": \"_nodeAddress\",\"type\": \"address\"}],\"name\": \"confirmWithdrawalAddress\",\"outputs\": [],\"stateMutability\": \"nonpayable\",\"type\": \"function\"},{\"inputs\": [{\"internalType\": \"bytes32\",\"name\": \"_key\",\"type\": \"bytes32\"}],\"name\": \"getAddress\",\"outputs\": [{\"internalType\": \"address\",\"name\": \"r\",\"type\": \"address\"}],\"stateMutability\": \"view\",\"type\": \"function\",\"constant\": true},{\"inputs\": [{\"internalType\": \"bytes32\",\"name\": \"_key\",\"type\": \"bytes32\"}],\"name\": \"getUint\",\"outputs\": [{\"internalType\": \"uint256\",\"name\": \"r\",\"type\": \"uint256\"}],\"stateMutability\": \"view\",\"type\": \"function\",\"constant\": true},{\"inputs\": [{\"internalType\": \"bytes32\",\"name\": \"_key\",\"type\": \"bytes32\"}],\"name\": \"getString\",\"outputs\": [{\"internalType\": \"string\",\"name\": \"\",\"type\": \"string\"}],\"stateMutability\": \"view\",\"type\": \"function\",\"constant\": true},{\"inputs\": [{\"internalType\": \"bytes32\",\"name\": \"_key\",\"type\": \"bytes32\"}],\"name\": \"getBytes\",\"outputs\": [{\"internalType\": \"bytes\",\"name\": \"\",\"type\": \"bytes\"}],\"stateMutability\": \"view\",\"type\": \"function\",\"constant\": true},{\"inputs\": [{\"internalType\": \"bytes32\",\"name\": \"_key\",\"type\": \"bytes32\"}],\"name\": \"getBool\",\"outputs\": [{\"internalType\": \"bool\",\"name\": \"r\",\"type\": \"bool\"}],\"stateMutability\": \"view\",\"type\": \"function\",\"constant\": true},{\"inputs\": [{\"internalType\": \"bytes32\",\"name\": \"_key\",\"type\": \"bytes32\"}],\"name\": \"getInt\",\"outputs\": [{\"internalType\": \"int256\",\"name\": \"r\",\"type\": \"int256\"}],\"stateMutability\": \"view\",\"type\": \"function\",\"constant\": true},{\"inputs\": [{\"internalType\": \"bytes32\",\"name\": \"_key\",\"type\": \"bytes32\"}],\"name\": \"getBytes32\",\"outputs\": [{\"internalType\": \"bytes32\",\"name\": \"r\",\"type\": \"bytes32\"}],\"stateMutability\": \"view\",\"type\": \"function\",\"constant\": true},{\"inputs\": [{\"internalType\": \"bytes32\",\"name\": \"_key\",\"type\": \"bytes32\"},{\"internalType\": \"address\",\"name\": \"_value\",\"type\": \"address\"}],\"name\": \"setAddress\",\"outputs\": [],\"stateMutability\": \"nonpayable\",\"type\": \"function\"},{\"inputs\": [{\"internalType\": \"bytes32\",\"name\": \"_key\",\"type\": \"bytes32\"},{\"internalType\": \"uint256\",\"name\": \"_value\",\"type\": \"uint256\"}],\"name\": \"setUint\",\"outputs\": [],\"stateMutability\": \"nonpayable\",\"type\": \"function\"},{\"inputs\": [{\"internalType\": \"bytes32\",\"name\": \"_key\",\"type\": \"bytes32\"},{\"internalType\": \"string\",\"name\": \"_value\",\"type\": \"string\"}],\"name\": \"setString\",\"outputs\": [],\"stateMutability\": \"nonpayable\",\"type\": \"function\"},{\"inputs\": [{\"internalType\": \"bytes32\",\"name\": \"_key\",\"type\": \"bytes32\"},{\"internalType\": \"bytes\",\"name\": \"_value\",\"type\": \"bytes\"}],\"name\": \"setBytes\",\"outputs\": [],\"stateMutability\": \"nonpayable\",\"type\": \"function\"},{\"inputs\": [{\"internalType\": \"bytes32\",\"name\": \"_key\",\"type\": \"bytes32\"},{\"internalType\": \"bool\",\"name\": \"_value\",\"type\": \"bool\"}],\"name\": \"setBool\",\"outputs\": [],\"stateMutability\": \"nonpayable\",\"type\": \"function\"},{\"inputs\": [{\"internalType\": \"bytes32\",\"name\": \"_key\",\"type\": \"bytes32\"},{\"internalType\": \"int256\",\"name\": \"_value\",\"type\": \"int256\"}],\"name\": \"setInt\",\"outputs\": [],\"stateMutability\": \"nonpayable\",\"type\": \"function\"},{\"inputs\": [{\"internalType\": \"bytes32\",\"name\": \"_key\",\"type\": \"bytes32\"},{\"internalType\": \"bytes32\",\"name\": \"_value\",\"type\": \"bytes32\"}],\"name\": \"setBytes32\",\"outputs\": [],\"stateMutability\": \"nonpayable\",\"type\": \"function\"},{\"inputs\": [{\"internalType\": \"bytes32\",\"name\": \"_key\",\"type\": \"bytes32\"}],\"name\": \"deleteAddress\",\"outputs\": [],\"stateMutability\": \"nonpayable\",\"type\": \"function\"},{\"inputs\": [{\"internalType\": \"bytes32\",\"name\": \"_key\",\"type\": \"bytes32\"}],\"name\": \"deleteUint\",\"outputs\": [],\"stateMutability\": \"nonpayable\",\"type\": \"function\"},{\"inputs\": [{\"internalType\": \"bytes32\",\"name\": \"_key\",\"type\": \"bytes32\"}],\"name\": \"deleteString\",\"outputs\": [],\"stateMutability\": \"nonpayable\",\"type\": \"function\"},{\"inputs\": [{\"internalType\": \"bytes32\",\"name\": \"_key\",\"type\": \"bytes32\"}],\"name\": \"deleteBytes\",\"outputs\": [],\"stateMutability\": \"nonpayable\",\"type\": \"function\"},{\"inputs\": [{\"internalType\": \"bytes32\",\"name\": \"_key\",\"type\": \"bytes32\"}],\"name\": \"deleteBool\",\"outputs\": [],\"stateMutability\": \"nonpayable\",\"type\": \"function\"},{\"inputs\": [{\"internalType\": \"bytes32\",\"name\": \"_key\",\"type\": \"bytes32\"}],\"name\": \"deleteInt\",\"outputs\": [],\"stateMutability\": \"nonpayable\",\"type\": \"function\"},{\"inputs\": [{\"internalType\": \"bytes32\",\"name\": \"_key\",\"type\": \"bytes32\"}],\"name\": \"deleteBytes32\",\"outputs\": [],\"stateMutability\": \"nonpayable\",\"type\": \"function\"},{\"inputs\": [{\"internalType\": \"bytes32\",\"name\": \"_key\",\"type\": \"bytes32\"},{\"internalType\": \"uint256\",\"name\": \"_amount\",\"type\": \"uint256\"}],\"name\": \"addUint\",\"outputs\": [],\"stateMutability\": \"nonpayable\",\"type\": \"function\"},{\"inputs\": [{\"internalType\": \"bytes32\",\"name\": \"_key\",\"type\": \"bytes32\"},{\"internalType\": \"uint256\",\"name\": \"_amount\",\"type\": \"uint256\"}],\"name\": \"subUint\",\"outputs\": [],\"stateMutability\": \"nonpayable\",\"type\": \"function\"}]\r\n" + +// RocketStorage is an auto generated Go binding around an Ethereum contract. +type RocketStorage struct { + RocketStorageCaller // Read-only binding to the contract + RocketStorageTransactor // Write-only binding to the contract + RocketStorageFilterer // Log filterer for contract events +} + +// RocketStorageCaller is an auto generated read-only Go binding around an Ethereum contract. +type RocketStorageCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// RocketStorageTransactor is an auto generated write-only Go binding around an Ethereum contract. +type RocketStorageTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// RocketStorageFilterer is an auto generated log filtering Go binding around an Ethereum contract events. +type RocketStorageFilterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// RocketStorageSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type RocketStorageSession struct { + Contract *RocketStorage // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// RocketStorageCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type RocketStorageCallerSession struct { + Contract *RocketStorageCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// RocketStorageTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type RocketStorageTransactorSession struct { + Contract *RocketStorageTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// RocketStorageRaw is an auto generated low-level Go binding around an Ethereum contract. +type RocketStorageRaw struct { + Contract *RocketStorage // Generic contract binding to access the raw methods on +} + +// RocketStorageCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type RocketStorageCallerRaw struct { + Contract *RocketStorageCaller // Generic read-only contract binding to access the raw methods on +} + +// RocketStorageTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type RocketStorageTransactorRaw struct { + Contract *RocketStorageTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewRocketStorage creates a new instance of RocketStorage, bound to a specific deployed contract. +func NewRocketStorage(address common.Address, backend bind.ContractBackend) (*RocketStorage, error) { + contract, err := bindRocketStorage(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &RocketStorage{RocketStorageCaller: RocketStorageCaller{contract: contract}, RocketStorageTransactor: RocketStorageTransactor{contract: contract}, RocketStorageFilterer: RocketStorageFilterer{contract: contract}}, nil +} + +// NewRocketStorageCaller creates a new read-only instance of RocketStorage, bound to a specific deployed contract. +func NewRocketStorageCaller(address common.Address, caller bind.ContractCaller) (*RocketStorageCaller, error) { + contract, err := bindRocketStorage(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &RocketStorageCaller{contract: contract}, nil +} + +// NewRocketStorageTransactor creates a new write-only instance of RocketStorage, bound to a specific deployed contract. +func NewRocketStorageTransactor(address common.Address, transactor bind.ContractTransactor) (*RocketStorageTransactor, error) { + contract, err := bindRocketStorage(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &RocketStorageTransactor{contract: contract}, nil +} + +// NewRocketStorageFilterer creates a new log filterer instance of RocketStorage, bound to a specific deployed contract. +func NewRocketStorageFilterer(address common.Address, filterer bind.ContractFilterer) (*RocketStorageFilterer, error) { + contract, err := bindRocketStorage(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &RocketStorageFilterer{contract: contract}, nil +} + +// bindRocketStorage binds a generic wrapper to an already deployed contract. +func bindRocketStorage(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := abi.JSON(strings.NewReader(RocketStorageABI)) + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, parsed, caller, transactor, filterer), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_RocketStorage *RocketStorageRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _RocketStorage.Contract.RocketStorageCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_RocketStorage *RocketStorageRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _RocketStorage.Contract.RocketStorageTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_RocketStorage *RocketStorageRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _RocketStorage.Contract.RocketStorageTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_RocketStorage *RocketStorageCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _RocketStorage.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_RocketStorage *RocketStorageTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _RocketStorage.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_RocketStorage *RocketStorageTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _RocketStorage.Contract.contract.Transact(opts, method, params...) +} + +// GetAddress is a free data retrieval call binding the contract method 0x21f8a721. +// +// Solidity: function getAddress(bytes32 _key) view returns(address) +func (_RocketStorage *RocketStorageCaller) GetAddress(opts *bind.CallOpts, _key [32]byte) (common.Address, error) { + var out []interface{} + err := _RocketStorage.contract.Call(opts, &out, "getAddress", _key) + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// GetAddress is a free data retrieval call binding the contract method 0x21f8a721. +// +// Solidity: function getAddress(bytes32 _key) view returns(address) +func (_RocketStorage *RocketStorageSession) GetAddress(_key [32]byte) (common.Address, error) { + return _RocketStorage.Contract.GetAddress(&_RocketStorage.CallOpts, _key) +} + +// GetAddress is a free data retrieval call binding the contract method 0x21f8a721. +// +// Solidity: function getAddress(bytes32 _key) view returns(address) +func (_RocketStorage *RocketStorageCallerSession) GetAddress(_key [32]byte) (common.Address, error) { + return _RocketStorage.Contract.GetAddress(&_RocketStorage.CallOpts, _key) +} + +// GetBool is a free data retrieval call binding the contract method 0x7ae1cfca. +// +// Solidity: function getBool(bytes32 _key) view returns(bool) +func (_RocketStorage *RocketStorageCaller) GetBool(opts *bind.CallOpts, _key [32]byte) (bool, error) { + var out []interface{} + err := _RocketStorage.contract.Call(opts, &out, "getBool", _key) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +// GetBool is a free data retrieval call binding the contract method 0x7ae1cfca. +// +// Solidity: function getBool(bytes32 _key) view returns(bool) +func (_RocketStorage *RocketStorageSession) GetBool(_key [32]byte) (bool, error) { + return _RocketStorage.Contract.GetBool(&_RocketStorage.CallOpts, _key) +} + +// GetBool is a free data retrieval call binding the contract method 0x7ae1cfca. +// +// Solidity: function getBool(bytes32 _key) view returns(bool) +func (_RocketStorage *RocketStorageCallerSession) GetBool(_key [32]byte) (bool, error) { + return _RocketStorage.Contract.GetBool(&_RocketStorage.CallOpts, _key) +} + +// GetBytes is a free data retrieval call binding the contract method 0xc031a180. +// +// Solidity: function getBytes(bytes32 _key) view returns(bytes) +func (_RocketStorage *RocketStorageCaller) GetBytes(opts *bind.CallOpts, _key [32]byte) ([]byte, error) { + var out []interface{} + err := _RocketStorage.contract.Call(opts, &out, "getBytes", _key) + + if err != nil { + return *new([]byte), err + } + + out0 := *abi.ConvertType(out[0], new([]byte)).(*[]byte) + + return out0, err + +} + +// GetBytes is a free data retrieval call binding the contract method 0xc031a180. +// +// Solidity: function getBytes(bytes32 _key) view returns(bytes) +func (_RocketStorage *RocketStorageSession) GetBytes(_key [32]byte) ([]byte, error) { + return _RocketStorage.Contract.GetBytes(&_RocketStorage.CallOpts, _key) +} + +// GetBytes is a free data retrieval call binding the contract method 0xc031a180. +// +// Solidity: function getBytes(bytes32 _key) view returns(bytes) +func (_RocketStorage *RocketStorageCallerSession) GetBytes(_key [32]byte) ([]byte, error) { + return _RocketStorage.Contract.GetBytes(&_RocketStorage.CallOpts, _key) +} + +// GetBytes32 is a free data retrieval call binding the contract method 0xa6ed563e. +// +// Solidity: function getBytes32(bytes32 _key) view returns(bytes32) +func (_RocketStorage *RocketStorageCaller) GetBytes32(opts *bind.CallOpts, _key [32]byte) ([32]byte, error) { + var out []interface{} + err := _RocketStorage.contract.Call(opts, &out, "getBytes32", _key) + + if err != nil { + return *new([32]byte), err + } + + out0 := *abi.ConvertType(out[0], new([32]byte)).(*[32]byte) + + return out0, err + +} + +// GetBytes32 is a free data retrieval call binding the contract method 0xa6ed563e. +// +// Solidity: function getBytes32(bytes32 _key) view returns(bytes32) +func (_RocketStorage *RocketStorageSession) GetBytes32(_key [32]byte) ([32]byte, error) { + return _RocketStorage.Contract.GetBytes32(&_RocketStorage.CallOpts, _key) +} + +// GetBytes32 is a free data retrieval call binding the contract method 0xa6ed563e. +// +// Solidity: function getBytes32(bytes32 _key) view returns(bytes32) +func (_RocketStorage *RocketStorageCallerSession) GetBytes32(_key [32]byte) ([32]byte, error) { + return _RocketStorage.Contract.GetBytes32(&_RocketStorage.CallOpts, _key) +} + +// GetInt is a free data retrieval call binding the contract method 0xdc97d962. +// +// Solidity: function getInt(bytes32 _key) view returns(int256) +func (_RocketStorage *RocketStorageCaller) GetInt(opts *bind.CallOpts, _key [32]byte) (*big.Int, error) { + var out []interface{} + err := _RocketStorage.contract.Call(opts, &out, "getInt", _key) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// GetInt is a free data retrieval call binding the contract method 0xdc97d962. +// +// Solidity: function getInt(bytes32 _key) view returns(int256) +func (_RocketStorage *RocketStorageSession) GetInt(_key [32]byte) (*big.Int, error) { + return _RocketStorage.Contract.GetInt(&_RocketStorage.CallOpts, _key) +} + +// GetInt is a free data retrieval call binding the contract method 0xdc97d962. +// +// Solidity: function getInt(bytes32 _key) view returns(int256) +func (_RocketStorage *RocketStorageCallerSession) GetInt(_key [32]byte) (*big.Int, error) { + return _RocketStorage.Contract.GetInt(&_RocketStorage.CallOpts, _key) +} + +// GetString is a free data retrieval call binding the contract method 0x986e791a. +// +// Solidity: function getString(bytes32 _key) view returns(string) +func (_RocketStorage *RocketStorageCaller) GetString(opts *bind.CallOpts, _key [32]byte) (string, error) { + var out []interface{} + err := _RocketStorage.contract.Call(opts, &out, "getString", _key) + + if err != nil { + return *new(string), err + } + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + + return out0, err + +} + +// GetString is a free data retrieval call binding the contract method 0x986e791a. +// +// Solidity: function getString(bytes32 _key) view returns(string) +func (_RocketStorage *RocketStorageSession) GetString(_key [32]byte) (string, error) { + return _RocketStorage.Contract.GetString(&_RocketStorage.CallOpts, _key) +} + +// GetString is a free data retrieval call binding the contract method 0x986e791a. +// +// Solidity: function getString(bytes32 _key) view returns(string) +func (_RocketStorage *RocketStorageCallerSession) GetString(_key [32]byte) (string, error) { + return _RocketStorage.Contract.GetString(&_RocketStorage.CallOpts, _key) +} + +// GetUint is a free data retrieval call binding the contract method 0xbd02d0f5. +// +// Solidity: function getUint(bytes32 _key) view returns(uint256) +func (_RocketStorage *RocketStorageCaller) GetUint(opts *bind.CallOpts, _key [32]byte) (*big.Int, error) { + var out []interface{} + err := _RocketStorage.contract.Call(opts, &out, "getUint", _key) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// GetUint is a free data retrieval call binding the contract method 0xbd02d0f5. +// +// Solidity: function getUint(bytes32 _key) view returns(uint256) +func (_RocketStorage *RocketStorageSession) GetUint(_key [32]byte) (*big.Int, error) { + return _RocketStorage.Contract.GetUint(&_RocketStorage.CallOpts, _key) +} + +// GetUint is a free data retrieval call binding the contract method 0xbd02d0f5. +// +// Solidity: function getUint(bytes32 _key) view returns(uint256) +func (_RocketStorage *RocketStorageCallerSession) GetUint(_key [32]byte) (*big.Int, error) { + return _RocketStorage.Contract.GetUint(&_RocketStorage.CallOpts, _key) +} + +// DeleteAddress is a paid mutator transaction binding the contract method 0x0e14a376. +// +// Solidity: function deleteAddress(bytes32 _key) returns() +func (_RocketStorage *RocketStorageTransactor) DeleteAddress(opts *bind.TransactOpts, _key [32]byte) (*types.Transaction, error) { + return _RocketStorage.contract.Transact(opts, "deleteAddress", _key) +} + +// DeleteAddress is a paid mutator transaction binding the contract method 0x0e14a376. +// +// Solidity: function deleteAddress(bytes32 _key) returns() +func (_RocketStorage *RocketStorageSession) DeleteAddress(_key [32]byte) (*types.Transaction, error) { + return _RocketStorage.Contract.DeleteAddress(&_RocketStorage.TransactOpts, _key) +} + +// DeleteAddress is a paid mutator transaction binding the contract method 0x0e14a376. +// +// Solidity: function deleteAddress(bytes32 _key) returns() +func (_RocketStorage *RocketStorageTransactorSession) DeleteAddress(_key [32]byte) (*types.Transaction, error) { + return _RocketStorage.Contract.DeleteAddress(&_RocketStorage.TransactOpts, _key) +} + +// DeleteBool is a paid mutator transaction binding the contract method 0x2c62ff2d. +// +// Solidity: function deleteBool(bytes32 _key) returns() +func (_RocketStorage *RocketStorageTransactor) DeleteBool(opts *bind.TransactOpts, _key [32]byte) (*types.Transaction, error) { + return _RocketStorage.contract.Transact(opts, "deleteBool", _key) +} + +// DeleteBool is a paid mutator transaction binding the contract method 0x2c62ff2d. +// +// Solidity: function deleteBool(bytes32 _key) returns() +func (_RocketStorage *RocketStorageSession) DeleteBool(_key [32]byte) (*types.Transaction, error) { + return _RocketStorage.Contract.DeleteBool(&_RocketStorage.TransactOpts, _key) +} + +// DeleteBool is a paid mutator transaction binding the contract method 0x2c62ff2d. +// +// Solidity: function deleteBool(bytes32 _key) returns() +func (_RocketStorage *RocketStorageTransactorSession) DeleteBool(_key [32]byte) (*types.Transaction, error) { + return _RocketStorage.Contract.DeleteBool(&_RocketStorage.TransactOpts, _key) +} + +// DeleteBytes is a paid mutator transaction binding the contract method 0x616b59f6. +// +// Solidity: function deleteBytes(bytes32 _key) returns() +func (_RocketStorage *RocketStorageTransactor) DeleteBytes(opts *bind.TransactOpts, _key [32]byte) (*types.Transaction, error) { + return _RocketStorage.contract.Transact(opts, "deleteBytes", _key) +} + +// DeleteBytes is a paid mutator transaction binding the contract method 0x616b59f6. +// +// Solidity: function deleteBytes(bytes32 _key) returns() +func (_RocketStorage *RocketStorageSession) DeleteBytes(_key [32]byte) (*types.Transaction, error) { + return _RocketStorage.Contract.DeleteBytes(&_RocketStorage.TransactOpts, _key) +} + +// DeleteBytes is a paid mutator transaction binding the contract method 0x616b59f6. +// +// Solidity: function deleteBytes(bytes32 _key) returns() +func (_RocketStorage *RocketStorageTransactorSession) DeleteBytes(_key [32]byte) (*types.Transaction, error) { + return _RocketStorage.Contract.DeleteBytes(&_RocketStorage.TransactOpts, _key) +} + +// DeleteBytes32 is a paid mutator transaction binding the contract method 0x0b9adc57. +// +// Solidity: function deleteBytes32(bytes32 _key) returns() +func (_RocketStorage *RocketStorageTransactor) DeleteBytes32(opts *bind.TransactOpts, _key [32]byte) (*types.Transaction, error) { + return _RocketStorage.contract.Transact(opts, "deleteBytes32", _key) +} + +// DeleteBytes32 is a paid mutator transaction binding the contract method 0x0b9adc57. +// +// Solidity: function deleteBytes32(bytes32 _key) returns() +func (_RocketStorage *RocketStorageSession) DeleteBytes32(_key [32]byte) (*types.Transaction, error) { + return _RocketStorage.Contract.DeleteBytes32(&_RocketStorage.TransactOpts, _key) +} + +// DeleteBytes32 is a paid mutator transaction binding the contract method 0x0b9adc57. +// +// Solidity: function deleteBytes32(bytes32 _key) returns() +func (_RocketStorage *RocketStorageTransactorSession) DeleteBytes32(_key [32]byte) (*types.Transaction, error) { + return _RocketStorage.Contract.DeleteBytes32(&_RocketStorage.TransactOpts, _key) +} + +// DeleteInt is a paid mutator transaction binding the contract method 0x8c160095. +// +// Solidity: function deleteInt(bytes32 _key) returns() +func (_RocketStorage *RocketStorageTransactor) DeleteInt(opts *bind.TransactOpts, _key [32]byte) (*types.Transaction, error) { + return _RocketStorage.contract.Transact(opts, "deleteInt", _key) +} + +// DeleteInt is a paid mutator transaction binding the contract method 0x8c160095. +// +// Solidity: function deleteInt(bytes32 _key) returns() +func (_RocketStorage *RocketStorageSession) DeleteInt(_key [32]byte) (*types.Transaction, error) { + return _RocketStorage.Contract.DeleteInt(&_RocketStorage.TransactOpts, _key) +} + +// DeleteInt is a paid mutator transaction binding the contract method 0x8c160095. +// +// Solidity: function deleteInt(bytes32 _key) returns() +func (_RocketStorage *RocketStorageTransactorSession) DeleteInt(_key [32]byte) (*types.Transaction, error) { + return _RocketStorage.Contract.DeleteInt(&_RocketStorage.TransactOpts, _key) +} + +// DeleteString is a paid mutator transaction binding the contract method 0xf6bb3cc4. +// +// Solidity: function deleteString(bytes32 _key) returns() +func (_RocketStorage *RocketStorageTransactor) DeleteString(opts *bind.TransactOpts, _key [32]byte) (*types.Transaction, error) { + return _RocketStorage.contract.Transact(opts, "deleteString", _key) +} + +// DeleteString is a paid mutator transaction binding the contract method 0xf6bb3cc4. +// +// Solidity: function deleteString(bytes32 _key) returns() +func (_RocketStorage *RocketStorageSession) DeleteString(_key [32]byte) (*types.Transaction, error) { + return _RocketStorage.Contract.DeleteString(&_RocketStorage.TransactOpts, _key) +} + +// DeleteString is a paid mutator transaction binding the contract method 0xf6bb3cc4. +// +// Solidity: function deleteString(bytes32 _key) returns() +func (_RocketStorage *RocketStorageTransactorSession) DeleteString(_key [32]byte) (*types.Transaction, error) { + return _RocketStorage.Contract.DeleteString(&_RocketStorage.TransactOpts, _key) +} + +// DeleteUint is a paid mutator transaction binding the contract method 0xe2b202bf. +// +// Solidity: function deleteUint(bytes32 _key) returns() +func (_RocketStorage *RocketStorageTransactor) DeleteUint(opts *bind.TransactOpts, _key [32]byte) (*types.Transaction, error) { + return _RocketStorage.contract.Transact(opts, "deleteUint", _key) +} + +// DeleteUint is a paid mutator transaction binding the contract method 0xe2b202bf. +// +// Solidity: function deleteUint(bytes32 _key) returns() +func (_RocketStorage *RocketStorageSession) DeleteUint(_key [32]byte) (*types.Transaction, error) { + return _RocketStorage.Contract.DeleteUint(&_RocketStorage.TransactOpts, _key) +} + +// DeleteUint is a paid mutator transaction binding the contract method 0xe2b202bf. +// +// Solidity: function deleteUint(bytes32 _key) returns() +func (_RocketStorage *RocketStorageTransactorSession) DeleteUint(_key [32]byte) (*types.Transaction, error) { + return _RocketStorage.Contract.DeleteUint(&_RocketStorage.TransactOpts, _key) +} + +// SetAddress is a paid mutator transaction binding the contract method 0xca446dd9. +// +// Solidity: function setAddress(bytes32 _key, address _value) returns() +func (_RocketStorage *RocketStorageTransactor) SetAddress(opts *bind.TransactOpts, _key [32]byte, _value common.Address) (*types.Transaction, error) { + return _RocketStorage.contract.Transact(opts, "setAddress", _key, _value) +} + +// SetAddress is a paid mutator transaction binding the contract method 0xca446dd9. +// +// Solidity: function setAddress(bytes32 _key, address _value) returns() +func (_RocketStorage *RocketStorageSession) SetAddress(_key [32]byte, _value common.Address) (*types.Transaction, error) { + return _RocketStorage.Contract.SetAddress(&_RocketStorage.TransactOpts, _key, _value) +} + +// SetAddress is a paid mutator transaction binding the contract method 0xca446dd9. +// +// Solidity: function setAddress(bytes32 _key, address _value) returns() +func (_RocketStorage *RocketStorageTransactorSession) SetAddress(_key [32]byte, _value common.Address) (*types.Transaction, error) { + return _RocketStorage.Contract.SetAddress(&_RocketStorage.TransactOpts, _key, _value) +} + +// SetBool is a paid mutator transaction binding the contract method 0xabfdcced. +// +// Solidity: function setBool(bytes32 _key, bool _value) returns() +func (_RocketStorage *RocketStorageTransactor) SetBool(opts *bind.TransactOpts, _key [32]byte, _value bool) (*types.Transaction, error) { + return _RocketStorage.contract.Transact(opts, "setBool", _key, _value) +} + +// SetBool is a paid mutator transaction binding the contract method 0xabfdcced. +// +// Solidity: function setBool(bytes32 _key, bool _value) returns() +func (_RocketStorage *RocketStorageSession) SetBool(_key [32]byte, _value bool) (*types.Transaction, error) { + return _RocketStorage.Contract.SetBool(&_RocketStorage.TransactOpts, _key, _value) +} + +// SetBool is a paid mutator transaction binding the contract method 0xabfdcced. +// +// Solidity: function setBool(bytes32 _key, bool _value) returns() +func (_RocketStorage *RocketStorageTransactorSession) SetBool(_key [32]byte, _value bool) (*types.Transaction, error) { + return _RocketStorage.Contract.SetBool(&_RocketStorage.TransactOpts, _key, _value) +} + +// SetBytes is a paid mutator transaction binding the contract method 0x2e28d084. +// +// Solidity: function setBytes(bytes32 _key, bytes _value) returns() +func (_RocketStorage *RocketStorageTransactor) SetBytes(opts *bind.TransactOpts, _key [32]byte, _value []byte) (*types.Transaction, error) { + return _RocketStorage.contract.Transact(opts, "setBytes", _key, _value) +} + +// SetBytes is a paid mutator transaction binding the contract method 0x2e28d084. +// +// Solidity: function setBytes(bytes32 _key, bytes _value) returns() +func (_RocketStorage *RocketStorageSession) SetBytes(_key [32]byte, _value []byte) (*types.Transaction, error) { + return _RocketStorage.Contract.SetBytes(&_RocketStorage.TransactOpts, _key, _value) +} + +// SetBytes is a paid mutator transaction binding the contract method 0x2e28d084. +// +// Solidity: function setBytes(bytes32 _key, bytes _value) returns() +func (_RocketStorage *RocketStorageTransactorSession) SetBytes(_key [32]byte, _value []byte) (*types.Transaction, error) { + return _RocketStorage.Contract.SetBytes(&_RocketStorage.TransactOpts, _key, _value) +} + +// SetBytes32 is a paid mutator transaction binding the contract method 0x4e91db08. +// +// Solidity: function setBytes32(bytes32 _key, bytes32 _value) returns() +func (_RocketStorage *RocketStorageTransactor) SetBytes32(opts *bind.TransactOpts, _key [32]byte, _value [32]byte) (*types.Transaction, error) { + return _RocketStorage.contract.Transact(opts, "setBytes32", _key, _value) +} + +// SetBytes32 is a paid mutator transaction binding the contract method 0x4e91db08. +// +// Solidity: function setBytes32(bytes32 _key, bytes32 _value) returns() +func (_RocketStorage *RocketStorageSession) SetBytes32(_key [32]byte, _value [32]byte) (*types.Transaction, error) { + return _RocketStorage.Contract.SetBytes32(&_RocketStorage.TransactOpts, _key, _value) +} + +// SetBytes32 is a paid mutator transaction binding the contract method 0x4e91db08. +// +// Solidity: function setBytes32(bytes32 _key, bytes32 _value) returns() +func (_RocketStorage *RocketStorageTransactorSession) SetBytes32(_key [32]byte, _value [32]byte) (*types.Transaction, error) { + return _RocketStorage.Contract.SetBytes32(&_RocketStorage.TransactOpts, _key, _value) +} + +// SetInt is a paid mutator transaction binding the contract method 0x3e49bed0. +// +// Solidity: function setInt(bytes32 _key, int256 _value) returns() +func (_RocketStorage *RocketStorageTransactor) SetInt(opts *bind.TransactOpts, _key [32]byte, _value *big.Int) (*types.Transaction, error) { + return _RocketStorage.contract.Transact(opts, "setInt", _key, _value) +} + +// SetInt is a paid mutator transaction binding the contract method 0x3e49bed0. +// +// Solidity: function setInt(bytes32 _key, int256 _value) returns() +func (_RocketStorage *RocketStorageSession) SetInt(_key [32]byte, _value *big.Int) (*types.Transaction, error) { + return _RocketStorage.Contract.SetInt(&_RocketStorage.TransactOpts, _key, _value) +} + +// SetInt is a paid mutator transaction binding the contract method 0x3e49bed0. +// +// Solidity: function setInt(bytes32 _key, int256 _value) returns() +func (_RocketStorage *RocketStorageTransactorSession) SetInt(_key [32]byte, _value *big.Int) (*types.Transaction, error) { + return _RocketStorage.Contract.SetInt(&_RocketStorage.TransactOpts, _key, _value) +} + +// SetString is a paid mutator transaction binding the contract method 0x6e899550. +// +// Solidity: function setString(bytes32 _key, string _value) returns() +func (_RocketStorage *RocketStorageTransactor) SetString(opts *bind.TransactOpts, _key [32]byte, _value string) (*types.Transaction, error) { + return _RocketStorage.contract.Transact(opts, "setString", _key, _value) +} + +// SetString is a paid mutator transaction binding the contract method 0x6e899550. +// +// Solidity: function setString(bytes32 _key, string _value) returns() +func (_RocketStorage *RocketStorageSession) SetString(_key [32]byte, _value string) (*types.Transaction, error) { + return _RocketStorage.Contract.SetString(&_RocketStorage.TransactOpts, _key, _value) +} + +// SetString is a paid mutator transaction binding the contract method 0x6e899550. +// +// Solidity: function setString(bytes32 _key, string _value) returns() +func (_RocketStorage *RocketStorageTransactorSession) SetString(_key [32]byte, _value string) (*types.Transaction, error) { + return _RocketStorage.Contract.SetString(&_RocketStorage.TransactOpts, _key, _value) +} + +// SetUint is a paid mutator transaction binding the contract method 0xe2a4853a. +// +// Solidity: function setUint(bytes32 _key, uint256 _value) returns() +func (_RocketStorage *RocketStorageTransactor) SetUint(opts *bind.TransactOpts, _key [32]byte, _value *big.Int) (*types.Transaction, error) { + return _RocketStorage.contract.Transact(opts, "setUint", _key, _value) +} + +// SetUint is a paid mutator transaction binding the contract method 0xe2a4853a. +// +// Solidity: function setUint(bytes32 _key, uint256 _value) returns() +func (_RocketStorage *RocketStorageSession) SetUint(_key [32]byte, _value *big.Int) (*types.Transaction, error) { + return _RocketStorage.Contract.SetUint(&_RocketStorage.TransactOpts, _key, _value) +} + +// SetUint is a paid mutator transaction binding the contract method 0xe2a4853a. +// +// Solidity: function setUint(bytes32 _key, uint256 _value) returns() +func (_RocketStorage *RocketStorageTransactorSession) SetUint(_key [32]byte, _value *big.Int) (*types.Transaction, error) { + return _RocketStorage.Contract.SetUint(&_RocketStorage.TransactOpts, _key, _value) +} diff --git a/bindings/dao/claim.go b/bindings/dao/claim.go new file mode 100644 index 000000000..80ee6e7a6 --- /dev/null +++ b/bindings/dao/claim.go @@ -0,0 +1,30 @@ +package dao + +import ( + "fmt" + "sync" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/rocket-pool/smartnode/bindings/rocketpool" +) + +func GetContractExists(rp *rocketpool.RocketPool, contractName string, opts *bind.CallOpts) (bool, error) { + rocketClaimDAO, err := getRocketClaimDAO(rp, opts) + if err != nil { + return false, err + } + result := new(bool) + if err := rocketClaimDAO.Call(opts, result, "getContractExists", contractName); err != nil { + return false, fmt.Errorf("error checking if contract %s exists: %w", contractName, err) + } + return *result, nil +} + +// Get contracts +var rocketClaimDAOLock sync.Mutex + +func getRocketClaimDAO(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*rocketpool.Contract, error) { + rocketClaimDAOLock.Lock() + defer rocketClaimDAOLock.Unlock() + return rp.GetContract("rocketClaimDAO", opts) +} diff --git a/bindings/dao/proposal-payload.go b/bindings/dao/proposal-payload.go new file mode 100644 index 000000000..319d8ab5a --- /dev/null +++ b/bindings/dao/proposal-payload.go @@ -0,0 +1,64 @@ +package dao + +import ( + "encoding/hex" + "fmt" + "strings" + "sync" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + + "github.com/rocket-pool/smartnode/bindings/rocketpool" + strutils "github.com/rocket-pool/smartnode/bindings/utils/strings" +) + +// Get the string representation of a proposal payload +var getProposalPayloadStringLock sync.Mutex + +func GetProposalPayloadString(rp *rocketpool.RocketPool, daoName string, payload []byte, opts *bind.CallOpts) (string, error) { + + // Lock while getting proposal payload string + getProposalPayloadStringLock.Lock() + defer getProposalPayloadStringLock.Unlock() + + // Get proposal DAO contract ABI + daoContractAbi, err := rp.GetABI(daoName, opts) + if err != nil { + return "", fmt.Errorf("error getting '%s' DAO contract ABI: %w", daoName, err) + } + + // Get proposal payload method + method, err := daoContractAbi.MethodById(payload) + if err != nil { + return "", fmt.Errorf("error getting proposal payload method: %w", err) + } + + // Get proposal payload argument values + args, err := method.Inputs.UnpackValues(payload[4:]) + if err != nil { + return "", fmt.Errorf("error getting proposal payload arguments: %w", err) + } + + // Format argument values as strings + argStrs := []string{} + for ai, arg := range args { + switch method.Inputs[ai].Type.T { + case abi.AddressTy: + argStrs = append(argStrs, arg.(common.Address).Hex()) + case abi.HashTy: + argStrs = append(argStrs, arg.(common.Hash).Hex()) + case abi.FixedBytesTy: + fallthrough + case abi.BytesTy: + argStrs = append(argStrs, hex.EncodeToString(arg.([]byte))) + default: + argStrs = append(argStrs, fmt.Sprintf("%v", arg)) + } + } + + // Build & return payload string + return strutils.Sanitize(fmt.Sprintf("%s(%s)", method.RawName, strings.Join(argStrs, ","))), nil + +} diff --git a/bindings/dao/proposals.go b/bindings/dao/proposals.go new file mode 100644 index 000000000..6d71bc60e --- /dev/null +++ b/bindings/dao/proposals.go @@ -0,0 +1,647 @@ +package dao + +import ( + "fmt" + "math/big" + "sync" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "golang.org/x/sync/errgroup" + + "github.com/rocket-pool/smartnode/bindings/rocketpool" + rptypes "github.com/rocket-pool/smartnode/bindings/types" + "github.com/rocket-pool/smartnode/bindings/utils/eth" + "github.com/rocket-pool/smartnode/bindings/utils/strings" +) + +// Settings +const ( + ProposalDAONamesBatchSize = 50 + ProposalDetailsBatchSize = 10 +) + +// Proposal details +type ProposalDetails struct { + ID uint64 `json:"id"` + DAO string `json:"dao"` + ProposerAddress common.Address `json:"proposerAddress"` + Message string `json:"message"` + CreatedTime uint64 `json:"createdTime"` + StartTime uint64 `json:"startTime"` + EndTime uint64 `json:"endTime"` + ExpiryTime uint64 `json:"expiryTime"` + VotesRequired float64 `json:"votesRequired"` + VotesFor float64 `json:"votesFor"` + VotesAgainst float64 `json:"votesAgainst"` + MemberVoted bool `json:"memberVoted"` + MemberSupported bool `json:"memberSupported"` + IsCancelled bool `json:"isCancelled"` + IsExecuted bool `json:"isExecuted"` + Payload []byte `json:"payload"` + PayloadStr string `json:"payloadStr"` + State rptypes.ProposalState `json:"state"` +} + +// Get all proposal details +func GetProposals(rp *rocketpool.RocketPool, opts *bind.CallOpts) ([]ProposalDetails, error) { + + // Get proposal count + proposalCount, err := GetProposalCount(rp, opts) + if err != nil { + return []ProposalDetails{}, err + } + + // Load proposal details in batches + details := make([]ProposalDetails, proposalCount) + for bsi := uint64(0); bsi < proposalCount; bsi += ProposalDetailsBatchSize { + + // Get batch start & end index + psi := bsi + pei := bsi + ProposalDetailsBatchSize + if pei > proposalCount { + pei = proposalCount + } + + // Load details + var wg errgroup.Group + for pi := psi; pi < pei; pi++ { + pi := pi + wg.Go(func() error { + proposalDetails, err := GetProposalDetails(rp, pi+1, opts) // Proposals are 1-indexed + if err == nil { + details[pi] = proposalDetails + } + return err + }) + } + if err := wg.Wait(); err != nil { + return []ProposalDetails{}, err + } + + } + + // Return + return details, nil + +} + +// Get all proposal details with member data +func GetProposalsWithMember(rp *rocketpool.RocketPool, memberAddress common.Address, opts *bind.CallOpts) ([]ProposalDetails, error) { + + // Get proposal count + proposalCount, err := GetProposalCount(rp, opts) + if err != nil { + return []ProposalDetails{}, err + } + + // Load proposal details in batches + details := make([]ProposalDetails, proposalCount) + for bsi := uint64(0); bsi < proposalCount; bsi += ProposalDetailsBatchSize { + + // Get batch start & end index + psi := bsi + pei := bsi + ProposalDetailsBatchSize + if pei > proposalCount { + pei = proposalCount + } + + // Load details + var wg errgroup.Group + for pi := psi; pi < pei; pi++ { + pi := pi + wg.Go(func() error { + proposalDetails, err := GetProposalDetailsWithMember(rp, pi+1, memberAddress, opts) // Proposals are 1-indexed + if err == nil { + details[pi] = proposalDetails + } + return err + }) + } + if err := wg.Wait(); err != nil { + return []ProposalDetails{}, err + } + + } + + // Return + return details, nil + +} + +// Get DAO proposal details +func GetDAOProposals(rp *rocketpool.RocketPool, daoName string, opts *bind.CallOpts) ([]ProposalDetails, error) { + + // Get DAO proposal IDs + proposalIds, err := GetDAOProposalIDs(rp, daoName, opts) + if err != nil { + return []ProposalDetails{}, err + } + + // Load proposal details in batches + details := make([]ProposalDetails, len(proposalIds)) + for bsi := 0; bsi < len(proposalIds); bsi += ProposalDetailsBatchSize { + + // Get batch start & end index + psi := bsi + pei := bsi + ProposalDetailsBatchSize + if pei > len(proposalIds) { + pei = len(proposalIds) + } + + // Load details + var wg errgroup.Group + for pi := psi; pi < pei; pi++ { + pi := pi + wg.Go(func() error { + proposalDetails, err := GetProposalDetails(rp, proposalIds[pi], opts) + if err == nil { + details[pi] = proposalDetails + } + return err + }) + } + if err := wg.Wait(); err != nil { + return []ProposalDetails{}, err + } + + } + + // Return + return details, nil + +} + +// Get DAO proposal details with member data +func GetDAOProposalsWithMember(rp *rocketpool.RocketPool, daoName string, memberAddress common.Address, opts *bind.CallOpts) ([]ProposalDetails, error) { + + // Get DAO proposal IDs + proposalIds, err := GetDAOProposalIDs(rp, daoName, opts) + if err != nil { + return []ProposalDetails{}, err + } + + // Load proposal details in batches + details := make([]ProposalDetails, len(proposalIds)) + for bsi := 0; bsi < len(proposalIds); bsi += ProposalDetailsBatchSize { + + // Get batch start & end index + psi := bsi + pei := bsi + ProposalDetailsBatchSize + if pei > len(proposalIds) { + pei = len(proposalIds) + } + + // Load details + var wg errgroup.Group + for pi := psi; pi < pei; pi++ { + pi := pi + wg.Go(func() error { + proposalDetails, err := GetProposalDetailsWithMember(rp, proposalIds[pi], memberAddress, opts) + if err == nil { + details[pi] = proposalDetails + } + return err + }) + } + if err := wg.Wait(); err != nil { + return []ProposalDetails{}, err + } + + } + + // Return + return details, nil + +} + +// Get the IDs of proposals filtered by a DAO +func GetDAOProposalIDs(rp *rocketpool.RocketPool, daoName string, opts *bind.CallOpts) ([]uint64, error) { + + // Get proposal count + proposalCount, err := GetProposalCount(rp, opts) + if err != nil { + return []uint64{}, err + } + + // Load proposal DAO names in batches + proposalDaoNames := make([]string, proposalCount) + for bsi := uint64(0); bsi < proposalCount; bsi += ProposalDAONamesBatchSize { + + // Get batch start & end index + psi := bsi + pei := bsi + ProposalDAONamesBatchSize + if pei > proposalCount { + pei = proposalCount + } + + // Load details + var wg errgroup.Group + for pi := psi; pi < pei; pi++ { + pi := pi + wg.Go(func() error { + proposalDaoName, err := GetProposalDAO(rp, pi+1, opts) // Proposals are 1-indexed + if err == nil { + proposalDaoNames[pi] = proposalDaoName + } + return err + }) + } + if err := wg.Wait(); err != nil { + return []uint64{}, err + } + + } + + // Get & return IDs for DAO proposals + ids := []uint64{} + for pi, proposalDaoName := range proposalDaoNames { + if proposalDaoName == daoName { + ids = append(ids, uint64(pi+1)) // Proposals are 1-indexed + } + } + return ids, nil + +} + +// Get a proposal's details +func GetProposalDetails(rp *rocketpool.RocketPool, proposalId uint64, opts *bind.CallOpts) (ProposalDetails, error) { + + // Data + var wg errgroup.Group + var dao string + var proposerAddress common.Address + var message string + var createdTime uint64 + var startTime uint64 + var endTime uint64 + var expiryTime uint64 + var votesRequired float64 + var votesFor float64 + var votesAgainst float64 + var isCancelled bool + var isExecuted bool + var payload []byte + var state rptypes.ProposalState + + // Load data + wg.Go(func() error { + var err error + dao, err = GetProposalDAO(rp, proposalId, opts) + return err + }) + wg.Go(func() error { + var err error + proposerAddress, err = GetProposalProposerAddress(rp, proposalId, opts) + return err + }) + wg.Go(func() error { + var err error + message, err = GetProposalMessage(rp, proposalId, opts) + return err + }) + wg.Go(func() error { + var err error + createdTime, err = GetProposalCreatedTime(rp, proposalId, opts) + return err + }) + wg.Go(func() error { + var err error + startTime, err = GetProposalStartTime(rp, proposalId, opts) + return err + }) + wg.Go(func() error { + var err error + endTime, err = GetProposalEndTime(rp, proposalId, opts) + return err + }) + wg.Go(func() error { + var err error + expiryTime, err = GetProposalExpiryTime(rp, proposalId, opts) + return err + }) + wg.Go(func() error { + var err error + votesRequired, err = GetProposalVotesRequired(rp, proposalId, opts) + return err + }) + wg.Go(func() error { + var err error + votesFor, err = GetProposalVotesFor(rp, proposalId, opts) + return err + }) + wg.Go(func() error { + var err error + votesAgainst, err = GetProposalVotesAgainst(rp, proposalId, opts) + return err + }) + wg.Go(func() error { + var err error + isCancelled, err = GetProposalIsCancelled(rp, proposalId, opts) + return err + }) + wg.Go(func() error { + var err error + isExecuted, err = GetProposalIsExecuted(rp, proposalId, opts) + return err + }) + wg.Go(func() error { + var err error + payload, err = GetProposalPayload(rp, proposalId, opts) + return err + }) + wg.Go(func() error { + var err error + state, err = GetProposalState(rp, proposalId, opts) + return err + }) + + // Wait for data + if err := wg.Wait(); err != nil { + return ProposalDetails{}, err + } + + // Get proposal payload string + payloadStr, err := GetProposalPayloadString(rp, dao, payload, opts) + if err != nil { + payloadStr = "(unknown)" + } + + // Return + return ProposalDetails{ + ID: proposalId, + DAO: dao, + ProposerAddress: proposerAddress, + Message: message, + CreatedTime: createdTime, + StartTime: startTime, + EndTime: endTime, + ExpiryTime: expiryTime, + VotesRequired: votesRequired, + VotesFor: votesFor, + VotesAgainst: votesAgainst, + IsCancelled: isCancelled, + IsExecuted: isExecuted, + Payload: payload, + PayloadStr: payloadStr, + State: state, + }, nil + +} + +// Get a proposal's details with member data +func GetProposalDetailsWithMember(rp *rocketpool.RocketPool, proposalId uint64, memberAddress common.Address, opts *bind.CallOpts) (ProposalDetails, error) { + + // Data + var wg errgroup.Group + var details ProposalDetails + var memberVoted bool + var memberSupported bool + + // Load data + wg.Go(func() error { + var err error + details, err = GetProposalDetails(rp, proposalId, opts) + return err + }) + wg.Go(func() error { + var err error + memberVoted, err = GetProposalMemberVoted(rp, proposalId, memberAddress, opts) + return err + }) + wg.Go(func() error { + var err error + memberSupported, err = GetProposalMemberSupported(rp, proposalId, memberAddress, opts) + return err + }) + + // Wait for data + if err := wg.Wait(); err != nil { + return ProposalDetails{}, err + } + + // Return + details.MemberVoted = memberVoted + details.MemberSupported = memberSupported + return details, nil + +} + +// Get the proposal count +func GetProposalCount(rp *rocketpool.RocketPool, opts *bind.CallOpts) (uint64, error) { + rocketDAOProposal, err := getRocketDAOProposal(rp, opts) + if err != nil { + return 0, err + } + proposalCount := new(*big.Int) + if err := rocketDAOProposal.Call(opts, proposalCount, "getTotal"); err != nil { + return 0, fmt.Errorf("error getting proposal count: %w", err) + } + return (*proposalCount).Uint64(), nil +} + +// Proposal details +func GetProposalDAO(rp *rocketpool.RocketPool, proposalId uint64, opts *bind.CallOpts) (string, error) { + rocketDAOProposal, err := getRocketDAOProposal(rp, opts) + if err != nil { + return "", err + } + daoName := new(string) + if err := rocketDAOProposal.Call(opts, daoName, "getDAO", big.NewInt(int64(proposalId))); err != nil { + return "", fmt.Errorf("error getting proposal %d DAO: %w", proposalId, err) + } + return strings.Sanitize(*daoName), nil +} +func GetProposalProposerAddress(rp *rocketpool.RocketPool, proposalId uint64, opts *bind.CallOpts) (common.Address, error) { + rocketDAOProposal, err := getRocketDAOProposal(rp, opts) + if err != nil { + return common.Address{}, err + } + proposerAddress := new(common.Address) + if err := rocketDAOProposal.Call(opts, proposerAddress, "getProposer", big.NewInt(int64(proposalId))); err != nil { + return common.Address{}, fmt.Errorf("error getting proposal %d proposer address: %w", proposalId, err) + } + return *proposerAddress, nil +} +func GetProposalMessage(rp *rocketpool.RocketPool, proposalId uint64, opts *bind.CallOpts) (string, error) { + rocketDAOProposal, err := getRocketDAOProposal(rp, opts) + if err != nil { + return "", err + } + message := new(string) + if err := rocketDAOProposal.Call(opts, message, "getMessage", big.NewInt(int64(proposalId))); err != nil { + return "", fmt.Errorf("error getting proposal %d message: %w", proposalId, err) + } + return strings.Sanitize(*message), nil +} +func GetProposalCreatedTime(rp *rocketpool.RocketPool, proposalId uint64, opts *bind.CallOpts) (uint64, error) { + rocketDAOProposal, err := getRocketDAOProposal(rp, opts) + if err != nil { + return 0, err + } + createdTime := new(*big.Int) + if err := rocketDAOProposal.Call(opts, createdTime, "getCreated", big.NewInt(int64(proposalId))); err != nil { + return 0, fmt.Errorf("error getting proposal %d created time: %w", proposalId, err) + } + return (*createdTime).Uint64(), nil +} +func GetProposalStartTime(rp *rocketpool.RocketPool, proposalId uint64, opts *bind.CallOpts) (uint64, error) { + rocketDAOProposal, err := getRocketDAOProposal(rp, opts) + if err != nil { + return 0, err + } + startTime := new(*big.Int) + if err := rocketDAOProposal.Call(opts, startTime, "getStart", big.NewInt(int64(proposalId))); err != nil { + return 0, fmt.Errorf("error getting proposal %d start time: %w", proposalId, err) + } + return (*startTime).Uint64(), nil +} +func GetProposalEndTime(rp *rocketpool.RocketPool, proposalId uint64, opts *bind.CallOpts) (uint64, error) { + rocketDAOProposal, err := getRocketDAOProposal(rp, opts) + if err != nil { + return 0, err + } + endTime := new(*big.Int) + if err := rocketDAOProposal.Call(opts, endTime, "getEnd", big.NewInt(int64(proposalId))); err != nil { + return 0, fmt.Errorf("error getting proposal %d end time: %w", proposalId, err) + } + return (*endTime).Uint64(), nil +} +func GetProposalExpiryTime(rp *rocketpool.RocketPool, proposalId uint64, opts *bind.CallOpts) (uint64, error) { + rocketDAOProposal, err := getRocketDAOProposal(rp, opts) + if err != nil { + return 0, err + } + expiryTime := new(*big.Int) + if err := rocketDAOProposal.Call(opts, expiryTime, "getExpires", big.NewInt(int64(proposalId))); err != nil { + return 0, fmt.Errorf("error getting proposal %d expiry time: %w", proposalId, err) + } + return (*expiryTime).Uint64(), nil +} +func GetProposalVotesRequired(rp *rocketpool.RocketPool, proposalId uint64, opts *bind.CallOpts) (float64, error) { + rocketDAOProposal, err := getRocketDAOProposal(rp, opts) + if err != nil { + return 0, err + } + votesRequired := new(*big.Int) + if err := rocketDAOProposal.Call(opts, votesRequired, "getVotesRequired", big.NewInt(int64(proposalId))); err != nil { + return 0, fmt.Errorf("error getting proposal %d votes required: %w", proposalId, err) + } + return eth.WeiToEth(*votesRequired), nil +} +func GetProposalVotesFor(rp *rocketpool.RocketPool, proposalId uint64, opts *bind.CallOpts) (float64, error) { + rocketDAOProposal, err := getRocketDAOProposal(rp, opts) + if err != nil { + return 0, err + } + votesFor := new(*big.Int) + if err := rocketDAOProposal.Call(opts, votesFor, "getVotesFor", big.NewInt(int64(proposalId))); err != nil { + return 0, fmt.Errorf("error getting proposal %d votes for: %w", proposalId, err) + } + return eth.WeiToEth(*votesFor), nil +} +func GetProposalVotesAgainst(rp *rocketpool.RocketPool, proposalId uint64, opts *bind.CallOpts) (float64, error) { + rocketDAOProposal, err := getRocketDAOProposal(rp, opts) + if err != nil { + return 0, err + } + votesAgainst := new(*big.Int) + if err := rocketDAOProposal.Call(opts, votesAgainst, "getVotesAgainst", big.NewInt(int64(proposalId))); err != nil { + return 0, fmt.Errorf("error getting proposal %d votes against: %w", proposalId, err) + } + return eth.WeiToEth(*votesAgainst), nil +} +func GetProposalIsCancelled(rp *rocketpool.RocketPool, proposalId uint64, opts *bind.CallOpts) (bool, error) { + rocketDAOProposal, err := getRocketDAOProposal(rp, opts) + if err != nil { + return false, err + } + cancelled := new(bool) + if err := rocketDAOProposal.Call(opts, cancelled, "getCancelled", big.NewInt(int64(proposalId))); err != nil { + return false, fmt.Errorf("error getting proposal %d cancelled status: %w", proposalId, err) + } + return *cancelled, nil +} +func GetProposalIsExecuted(rp *rocketpool.RocketPool, proposalId uint64, opts *bind.CallOpts) (bool, error) { + rocketDAOProposal, err := getRocketDAOProposal(rp, opts) + if err != nil { + return false, err + } + executed := new(bool) + if err := rocketDAOProposal.Call(opts, executed, "getExecuted", big.NewInt(int64(proposalId))); err != nil { + return false, fmt.Errorf("error getting proposal %d executed status: %w", proposalId, err) + } + return *executed, nil +} +func GetProposalPayload(rp *rocketpool.RocketPool, proposalId uint64, opts *bind.CallOpts) ([]byte, error) { + rocketDAOProposal, err := getRocketDAOProposal(rp, opts) + if err != nil { + return []byte{}, err + } + payload := new([]byte) + if err := rocketDAOProposal.Call(opts, payload, "getPayload", big.NewInt(int64(proposalId))); err != nil { + return []byte{}, fmt.Errorf("error getting proposal %d payload: %w", proposalId, err) + } + return *payload, nil +} +func GetProposalPayloadStr(rp *rocketpool.RocketPool, proposalId uint64, opts *bind.CallOpts) (string, error) { + dao, err := GetProposalDAO(rp, proposalId, opts) + if err != nil { + return "", err + } + payload, err := GetProposalPayload(rp, proposalId, opts) + if err != nil { + return "", err + } + payloadStr, err := GetProposalPayloadString(rp, dao, payload, opts) + if err != nil { + payloadStr = "(unknown)" + } + return payloadStr, nil +} +func GetProposalState(rp *rocketpool.RocketPool, proposalId uint64, opts *bind.CallOpts) (rptypes.ProposalState, error) { + rocketDAOProposal, err := getRocketDAOProposal(rp, opts) + if err != nil { + return 0, err + } + state := new(uint8) + if err := rocketDAOProposal.Call(opts, state, "getState", big.NewInt(int64(proposalId))); err != nil { + return 0, fmt.Errorf("error getting proposal %d state: %w", proposalId, err) + } + return rptypes.ProposalState(*state), nil +} + +// Get whether a member has voted on a proposal +func GetProposalMemberVoted(rp *rocketpool.RocketPool, proposalId uint64, memberAddress common.Address, opts *bind.CallOpts) (bool, error) { + rocketDAOProposal, err := getRocketDAOProposal(rp, opts) + if err != nil { + return false, err + } + voted := new(bool) + if err := rocketDAOProposal.Call(opts, voted, "getReceiptHasVoted", big.NewInt(int64(proposalId)), memberAddress); err != nil { + return false, fmt.Errorf("error getting proposal %d member %s voted status: %w", proposalId, memberAddress.Hex(), err) + } + return *voted, nil +} + +// Get whether a member has voted in support of a proposal +func GetProposalMemberSupported(rp *rocketpool.RocketPool, proposalId uint64, memberAddress common.Address, opts *bind.CallOpts) (bool, error) { + rocketDAOProposal, err := getRocketDAOProposal(rp, opts) + if err != nil { + return false, err + } + supported := new(bool) + if err := rocketDAOProposal.Call(opts, supported, "getReceiptSupported", big.NewInt(int64(proposalId)), memberAddress); err != nil { + return false, fmt.Errorf("error getting proposal %d member %s supported status: %w", proposalId, memberAddress.Hex(), err) + } + return *supported, nil +} + +// Get contracts +var rocketDAOProposalLock sync.Mutex + +func getRocketDAOProposal(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*rocketpool.Contract, error) { + rocketDAOProposalLock.Lock() + defer rocketDAOProposalLock.Unlock() + return rp.GetContract("rocketDAOProposal", opts) +} diff --git a/bindings/dao/protocol/dao.go b/bindings/dao/protocol/dao.go new file mode 100644 index 000000000..b908a20ed --- /dev/null +++ b/bindings/dao/protocol/dao.go @@ -0,0 +1,18 @@ +package protocol + +import ( + "sync" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + + "github.com/rocket-pool/smartnode/bindings/rocketpool" +) + +// Get contracts +var rocketDAOProtocolLock sync.Mutex + +func getRocketDAOProtocol(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*rocketpool.Contract, error) { + rocketDAOProtocolLock.Lock() + defer rocketDAOProtocolLock.Unlock() + return rp.GetContract("rocketDAOProtocol", opts) +} diff --git a/bindings/dao/protocol/proposal.go b/bindings/dao/protocol/proposal.go new file mode 100644 index 000000000..90e62b868 --- /dev/null +++ b/bindings/dao/protocol/proposal.go @@ -0,0 +1,727 @@ +package protocol + +import ( + "context" + "encoding/hex" + "fmt" + "math/big" + "strings" + "sync" + "time" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/rocket-pool/smartnode/bindings/dao" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/types" + strutils "github.com/rocket-pool/smartnode/bindings/utils/strings" + "golang.org/x/sync/errgroup" +) + +// Settings +const ( + ProposalDAONamesBatchSize = 50 + ProposalDetailsBatchSize = 10 +) + +// ===================== +// === Proposal Info === +// ===================== + +// Proposal details +type ProtocolDaoProposalDetails struct { + ID uint64 `json:"id"` + DAO string `json:"dao"` + ProposerAddress common.Address `json:"proposerAddress"` + TargetBlock uint32 `json:"targetBlock"` + Message string `json:"message"` + CreatedTime time.Time `json:"createdTime"` + ChallengeWindow time.Duration `json:"challengeWindow"` + VotingStartTime time.Time `json:"startTime"` + Phase1EndTime time.Time `json:"phase1EndTime"` + Phase2EndTime time.Time `json:"phase2EndTime"` + ExpiryTime time.Time `json:"expiryTime"` + VotingPowerRequired *big.Int `json:"votingPowerRequired"` + VotingPowerFor *big.Int `json:"votingPowerFor"` + VotingPowerAgainst *big.Int `json:"votingPowerAgainst"` + VotingPowerAbstained *big.Int `json:"votingPowerAbstained"` + VotingPowerToVeto *big.Int `json:"votingPowerVeto"` + IsDestroyed bool `json:"isDestroyed"` + IsFinalized bool `json:"isFinalized"` + IsExecuted bool `json:"isExecuted"` + IsVetoed bool `json:"isVetoed"` + VetoQuorum *big.Int `json:"vetoQuorum"` + Payload []byte `json:"payload"` + PayloadStr string `json:"payloadStr"` + State types.ProtocolDaoProposalState `json:"state"` + ProposalBond *big.Int `json:"proposalBond"` + ChallengeBond *big.Int `json:"challengeBond"` + DefeatIndex uint64 `json:"defeatIndex"` +} + +// Get all proposal details +func GetProposals(rp *rocketpool.RocketPool, opts *bind.CallOpts) ([]ProtocolDaoProposalDetails, error) { + // Get proposal count + proposalCount, err := GetTotalProposalCount(rp, opts) + if err != nil { + return []ProtocolDaoProposalDetails{}, err + } + + // Load proposal details in batches + details := make([]ProtocolDaoProposalDetails, proposalCount) + for bsi := uint64(0); bsi < proposalCount; bsi += ProposalDetailsBatchSize { + + // Get batch start & end index + psi := bsi + pei := bsi + ProposalDetailsBatchSize + if pei > proposalCount { + pei = proposalCount + } + + // Load details + var wg errgroup.Group + for pi := psi; pi < pei; pi++ { + pi := pi + wg.Go(func() error { + proposalDetails, err := GetProposalDetails(rp, pi+1, opts) // Proposals are 1-indexed + if err == nil { + details[pi] = proposalDetails + } + return err + }) + } + if err := wg.Wait(); err != nil { + return []ProtocolDaoProposalDetails{}, err + } + + } + + // Return + return details, nil +} + +// Get a proposal's details +func GetProposalDetails(rp *rocketpool.RocketPool, proposalId uint64, opts *bind.CallOpts) (ProtocolDaoProposalDetails, error) { + var wg errgroup.Group + var prop ProtocolDaoProposalDetails + prop.ID = proposalId + + // Load data + wg.Go(func() error { + var err error + prop.ProposerAddress, err = GetProposalProposer(rp, proposalId, opts) + return err + }) + wg.Go(func() error { + var err error + prop.TargetBlock, err = GetProposalBlock(rp, proposalId, opts) + return err + }) + wg.Go(func() error { + var err error + prop.Message, err = GetProposalMessage(rp, proposalId, opts) + return err + }) + wg.Go(func() error { + var err error + prop.VotingStartTime, err = GetProposalVotingStartTime(rp, proposalId, opts) + return err + }) + wg.Go(func() error { + var err error + prop.Phase1EndTime, err = GetProposalPhase1EndTime(rp, proposalId, opts) + return err + }) + wg.Go(func() error { + var err error + prop.Phase2EndTime, err = GetProposalPhase2EndTime(rp, proposalId, opts) + return err + }) + wg.Go(func() error { + var err error + prop.ExpiryTime, err = GetProposalExpiryTime(rp, proposalId, opts) + return err + }) + wg.Go(func() error { + var err error + prop.CreatedTime, err = GetProposalCreationTime(rp, proposalId, opts) + return err + }) + wg.Go(func() error { + var err error + prop.VotingPowerRequired, err = GetProposalVotingPowerRequired(rp, proposalId, opts) + return err + }) + wg.Go(func() error { + var err error + prop.VotingPowerFor, err = GetProposalVotingPowerFor(rp, proposalId, opts) + return err + }) + wg.Go(func() error { + var err error + prop.VotingPowerAgainst, err = GetProposalVotingPowerAgainst(rp, proposalId, opts) + return err + }) + wg.Go(func() error { + var err error + prop.VotingPowerAbstained, err = GetProposalVotingPowerAbstained(rp, proposalId, opts) + return err + }) + wg.Go(func() error { + var err error + prop.VotingPowerToVeto, err = GetProposalVotingPowerVetoed(rp, proposalId, opts) + return err + }) + wg.Go(func() error { + var err error + prop.IsDestroyed, err = GetProposalIsDestroyed(rp, proposalId, opts) + return err + }) + wg.Go(func() error { + var err error + prop.IsFinalized, err = GetProposalIsFinalized(rp, proposalId, opts) + return err + }) + wg.Go(func() error { + var err error + prop.IsExecuted, err = GetProposalIsExecuted(rp, proposalId, opts) + return err + }) + wg.Go(func() error { + var err error + prop.IsVetoed, err = GetProposalIsVetoed(rp, proposalId, opts) + return err + }) + wg.Go(func() error { + var err error + prop.VetoQuorum, err = GetProposalVetoQuorum(rp, proposalId, opts) + return err + }) + wg.Go(func() error { + var err error + prop.Payload, err = GetProposalPayload(rp, proposalId, opts) + return err + }) + wg.Go(func() error { + var err error + prop.State, err = GetProposalState(rp, proposalId, opts) + return err + }) + wg.Go(func() error { + var err error + prop.DefeatIndex, err = GetDefeatIndex(rp, proposalId, opts) + return err + }) + wg.Go(func() error { + var err error + prop.ProposalBond, err = GetProposalBond(rp, proposalId, opts) + return err + }) + wg.Go(func() error { + var err error + prop.ChallengeBond, err = GetChallengeBond(rp, proposalId, opts) + return err + }) + wg.Go(func() error { + var err error + prop.ChallengeWindow, err = GetChallengePeriod(rp, proposalId, opts) + return err + }) + + // Wait for data + if err := wg.Wait(); err != nil { + return ProtocolDaoProposalDetails{}, err + } + + // Get proposal payload string + payloadStr, err := GetProposalPayloadString(rp, prop.Payload, opts) + if err != nil { + payloadStr = "(unknown)" + } + prop.PayloadStr = payloadStr + return prop, nil +} + +// Get the block that was used for voting power calculation in a proposal +func GetProposalBlock(rp *rocketpool.RocketPool, proposalId uint64, opts *bind.CallOpts) (uint32, error) { + rocketDAOProtocolProposal, err := getRocketDAOProtocolProposal(rp, nil) + if err != nil { + return 0, err + } + value := new(*big.Int) + if err := rocketDAOProtocolProposal.Call(opts, value, "getProposalBlock", big.NewInt(0).SetUint64(proposalId)); err != nil { + return 0, fmt.Errorf("error getting proposal block for proposal %d: %w", proposalId, err) + } + return uint32((*value).Uint64()), nil +} + +// Get the veto quorum required to veto a proposal +func GetProposalVetoQuorum(rp *rocketpool.RocketPool, proposalId uint64, opts *bind.CallOpts) (*big.Int, error) { + rocketDAOProtocolProposal, err := getRocketDAOProtocolProposal(rp, nil) + if err != nil { + return nil, err + } + value := new(*big.Int) + if err := rocketDAOProtocolProposal.Call(opts, value, "getProposalVetoQuorum", big.NewInt(0).SetUint64(proposalId)); err != nil { + return nil, fmt.Errorf("error getting proposal veto quorum for proposal %d: %w", proposalId, err) + } + return *value, nil +} + +// The total number of Protocol DAO proposals +func GetTotalProposalCount(rp *rocketpool.RocketPool, opts *bind.CallOpts) (uint64, error) { + rocketDAOProtocolProposal, err := getRocketDAOProtocolProposal(rp, nil) + if err != nil { + return 0, err + } + value := new(*big.Int) + if err := rocketDAOProtocolProposal.Call(opts, value, "getTotal"); err != nil { + return 0, fmt.Errorf("error getting total proposal count: %w", err) + } + return (*value).Uint64(), nil +} + +// Get the address of the proposer +func GetProposalProposer(rp *rocketpool.RocketPool, proposalId uint64, opts *bind.CallOpts) (common.Address, error) { + rocketDAOProtocolProposal, err := getRocketDAOProtocolProposal(rp, nil) + if err != nil { + return common.Address{}, err + } + value := new(common.Address) + if err := rocketDAOProtocolProposal.Call(opts, value, "getProposer", big.NewInt(0).SetUint64(proposalId)); err != nil { + return common.Address{}, fmt.Errorf("error getting proposer for proposal %d: %w", proposalId, err) + } + return *value, nil +} + +// Get the proposal's message +func GetProposalMessage(rp *rocketpool.RocketPool, proposalId uint64, opts *bind.CallOpts) (string, error) { + rocketDAOProtocolProposal, err := getRocketDAOProtocolProposal(rp, nil) + if err != nil { + return "", err + } + value := new(string) + if err := rocketDAOProtocolProposal.Call(opts, value, "getMessage", big.NewInt(0).SetUint64(proposalId)); err != nil { + return "", fmt.Errorf("error getting message for proposal %d: %w", proposalId, err) + } + return *value, nil +} + +// Get the start time of this proposal, when voting begins +func GetProposalVotingStartTime(rp *rocketpool.RocketPool, proposalId uint64, opts *bind.CallOpts) (time.Time, error) { + rocketDAOProtocolProposal, err := getRocketDAOProtocolProposal(rp, nil) + if err != nil { + return time.Time{}, err + } + value := new(*big.Int) + if err := rocketDAOProtocolProposal.Call(opts, value, "getStart", big.NewInt(0).SetUint64(proposalId)); err != nil { + return time.Time{}, fmt.Errorf("error getting start block for proposal %d: %w", proposalId, err) + } + return time.Unix((*value).Int64(), 0), nil +} + +// Get the phase 1 end time of this proposal +func GetProposalPhase1EndTime(rp *rocketpool.RocketPool, proposalId uint64, opts *bind.CallOpts) (time.Time, error) { + rocketDAOProtocolProposal, err := getRocketDAOProtocolProposal(rp, nil) + if err != nil { + return time.Time{}, err + } + value := new(*big.Int) + if err := rocketDAOProtocolProposal.Call(opts, value, "getPhase1End", big.NewInt(0).SetUint64(proposalId)); err != nil { + return time.Time{}, fmt.Errorf("error getting phase 1 end time for proposal %d: %w", proposalId, err) + } + return time.Unix((*value).Int64(), 0), nil +} + +// Get the phase 2 end time of this proposal +func GetProposalPhase2EndTime(rp *rocketpool.RocketPool, proposalId uint64, opts *bind.CallOpts) (time.Time, error) { + rocketDAOProtocolProposal, err := getRocketDAOProtocolProposal(rp, nil) + if err != nil { + return time.Time{}, err + } + value := new(*big.Int) + if err := rocketDAOProtocolProposal.Call(opts, value, "getPhase2End", big.NewInt(0).SetUint64(proposalId)); err != nil { + return time.Time{}, fmt.Errorf("error getting phase 2 end time for proposal %d: %w", proposalId, err) + } + return time.Unix((*value).Int64(), 0), nil +} + +// Get the time where the proposal expires and can no longer be executed if it is successful +func GetProposalExpiryTime(rp *rocketpool.RocketPool, proposalId uint64, opts *bind.CallOpts) (time.Time, error) { + rocketDAOProtocolProposal, err := getRocketDAOProtocolProposal(rp, nil) + if err != nil { + return time.Time{}, err + } + value := new(*big.Int) + if err := rocketDAOProtocolProposal.Call(opts, value, "getExpires", big.NewInt(0).SetUint64(proposalId)); err != nil { + return time.Time{}, fmt.Errorf("error getting expiry time for proposal %d: %w", proposalId, err) + } + return time.Unix((*value).Int64(), 0), nil +} + +// Get the time the proposal was created +func GetProposalCreationTime(rp *rocketpool.RocketPool, proposalId uint64, opts *bind.CallOpts) (time.Time, error) { + rocketDAOProtocolProposal, err := getRocketDAOProtocolProposal(rp, nil) + if err != nil { + return time.Time{}, err + } + value := new(*big.Int) + if err := rocketDAOProtocolProposal.Call(opts, value, "getCreated", big.NewInt(0).SetUint64(proposalId)); err != nil { + return time.Time{}, fmt.Errorf("error getting creation time for proposal %d: %w", proposalId, err) + } + return time.Unix((*value).Int64(), 0), nil +} + +// Get the cumulative amount of voting power voting in favor of this proposal +func GetProposalVotingPowerFor(rp *rocketpool.RocketPool, proposalId uint64, opts *bind.CallOpts) (*big.Int, error) { + rocketDAOProtocolProposal, err := getRocketDAOProtocolProposal(rp, nil) + if err != nil { + return nil, err + } + value := new(*big.Int) + if err := rocketDAOProtocolProposal.Call(opts, value, "getVotingPowerFor", big.NewInt(0).SetUint64(proposalId)); err != nil { + return nil, fmt.Errorf("error getting total 'for' voting power for proposal %d: %w", proposalId, err) + } + return *value, nil +} + +// Get the cumulative amount of voting power voting against this proposal +func GetProposalVotingPowerAgainst(rp *rocketpool.RocketPool, proposalId uint64, opts *bind.CallOpts) (*big.Int, error) { + rocketDAOProtocolProposal, err := getRocketDAOProtocolProposal(rp, nil) + if err != nil { + return nil, err + } + value := new(*big.Int) + if err := rocketDAOProtocolProposal.Call(opts, value, "getVotingPowerAgainst", big.NewInt(0).SetUint64(proposalId)); err != nil { + return nil, fmt.Errorf("error getting total 'against' voting power for proposal %d: %w", proposalId, err) + } + return *value, nil +} + +// Get the cumulative amount of voting power that vetoed this proposal +func GetProposalVotingPowerVetoed(rp *rocketpool.RocketPool, proposalId uint64, opts *bind.CallOpts) (*big.Int, error) { + rocketDAOProtocolProposal, err := getRocketDAOProtocolProposal(rp, nil) + if err != nil { + return nil, err + } + value := new(*big.Int) + if err := rocketDAOProtocolProposal.Call(opts, value, "getVotingPowerVeto", big.NewInt(0).SetUint64(proposalId)); err != nil { + return nil, fmt.Errorf("error getting total 'veto' voting power for proposal %d: %w", proposalId, err) + } + return *value, nil +} + +// Get the cumulative amount of voting power that abstained from this proposal +func GetProposalVotingPowerAbstained(rp *rocketpool.RocketPool, proposalId uint64, opts *bind.CallOpts) (*big.Int, error) { + rocketDAOProtocolProposal, err := getRocketDAOProtocolProposal(rp, nil) + if err != nil { + return nil, err + } + value := new(*big.Int) + if err := rocketDAOProtocolProposal.Call(opts, value, "getVotingPowerAbstained", big.NewInt(0).SetUint64(proposalId)); err != nil { + return nil, fmt.Errorf("error getting total 'abstained' voting power for proposal %d: %w", proposalId, err) + } + return *value, nil +} + +// Get the cumulative amount of voting power that must vote on this proposal for it to be eligible for execution if it succeeds +func GetProposalVotingPowerRequired(rp *rocketpool.RocketPool, proposalId uint64, opts *bind.CallOpts) (*big.Int, error) { + rocketDAOProtocolProposal, err := getRocketDAOProtocolProposal(rp, nil) + if err != nil { + return nil, err + } + value := new(*big.Int) + if err := rocketDAOProtocolProposal.Call(opts, value, "getVotingPowerRequired", big.NewInt(0).SetUint64(proposalId)); err != nil { + return nil, fmt.Errorf("error getting required voting power for proposal %d: %w", proposalId, err) + } + return *value, nil +} + +// Get whether or not the proposal has been destroyed +func GetProposalIsDestroyed(rp *rocketpool.RocketPool, proposalId uint64, opts *bind.CallOpts) (bool, error) { + rocketDAOProtocolProposal, err := getRocketDAOProtocolProposal(rp, nil) + if err != nil { + return false, err + } + value := new(bool) + if err := rocketDAOProtocolProposal.Call(opts, value, "getDestroyed", big.NewInt(0).SetUint64(proposalId)); err != nil { + return false, fmt.Errorf("error getting destroyed status of proposal %d: %w", proposalId, err) + } + return *value, nil +} + +// Get whether or not the proposal has been finalized +func GetProposalIsFinalized(rp *rocketpool.RocketPool, proposalId uint64, opts *bind.CallOpts) (bool, error) { + rocketDAOProtocolProposal, err := getRocketDAOProtocolProposal(rp, nil) + if err != nil { + return false, err + } + value := new(bool) + if err := rocketDAOProtocolProposal.Call(opts, value, "getFinalised", big.NewInt(0).SetUint64(proposalId)); err != nil { + return false, fmt.Errorf("error getting finalized status of proposal %d: %w", proposalId, err) + } + return *value, nil +} + +// Get whether or not the proposal has been executed +func GetProposalIsExecuted(rp *rocketpool.RocketPool, proposalId uint64, opts *bind.CallOpts) (bool, error) { + rocketDAOProtocolProposal, err := getRocketDAOProtocolProposal(rp, nil) + if err != nil { + return false, err + } + value := new(bool) + if err := rocketDAOProtocolProposal.Call(opts, value, "getExecuted", big.NewInt(0).SetUint64(proposalId)); err != nil { + return false, fmt.Errorf("error getting executed status of proposal %d: %w", proposalId, err) + } + return *value, nil +} + +// Get whether or not the proposal's veto quorum has been met and it has been vetoed +func GetProposalIsVetoed(rp *rocketpool.RocketPool, proposalId uint64, opts *bind.CallOpts) (bool, error) { + rocketDAOProtocolProposal, err := getRocketDAOProtocolProposal(rp, nil) + if err != nil { + return false, err + } + value := new(bool) + if err := rocketDAOProtocolProposal.Call(opts, value, "getVetoed", big.NewInt(0).SetUint64(proposalId)); err != nil { + return false, fmt.Errorf("error getting veto status of proposal %d: %w", proposalId, err) + } + return *value, nil +} + +// Get the proposal's payload +func GetProposalPayload(rp *rocketpool.RocketPool, proposalId uint64, opts *bind.CallOpts) ([]byte, error) { + rocketDAOProtocolProposal, err := getRocketDAOProtocolProposal(rp, nil) + if err != nil { + return nil, err + } + value := new([]byte) + if err := rocketDAOProtocolProposal.Call(opts, value, "getPayload", big.NewInt(0).SetUint64(proposalId)); err != nil { + return nil, fmt.Errorf("error getting payload of proposal %d: %w", proposalId, err) + } + return *value, nil +} + +// Get a proposal's payload as a human-readable string +func GetProposalPayloadString(rp *rocketpool.RocketPool, payload []byte, opts *bind.CallOpts) (string, error) { + rocketDAOProtocolProposals, err := getRocketDAOProtocolProposals(rp, nil) + if err != nil { + return "", err + } + + // Get proposal DAO contract ABI + daoContractAbi := rocketDAOProtocolProposals.ABI + + // Get proposal payload method + method, err := daoContractAbi.MethodById(payload) + if err != nil { + return "", fmt.Errorf("error getting proposal payload method: %w", err) + } + + // Get proposal payload argument values + args, err := method.Inputs.UnpackValues(payload[4:]) + if err != nil { + return "", fmt.Errorf("error getting proposal payload arguments: %w", err) + } + + // Format argument values as strings + argStrs := []string{} + for ai, arg := range args { + switch method.Inputs[ai].Type.T { + case abi.AddressTy: + argStrs = append(argStrs, arg.(common.Address).Hex()) + case abi.HashTy: + argStrs = append(argStrs, arg.(common.Hash).Hex()) + case abi.FixedBytesTy: + fallthrough + case abi.BytesTy: + argStrs = append(argStrs, hex.EncodeToString(arg.([]byte))) + default: + argStrs = append(argStrs, fmt.Sprintf("%v", arg)) + } + } + + // Build & return payload string + return strutils.Sanitize(fmt.Sprintf("%s(%s)", method.RawName, strings.Join(argStrs, ","))), nil +} + +// Get the proposal's state +func GetProposalState(rp *rocketpool.RocketPool, proposalId uint64, opts *bind.CallOpts) (types.ProtocolDaoProposalState, error) { + rocketDAOProtocolProposal, err := getRocketDAOProtocolProposal(rp, nil) + if err != nil { + return types.ProtocolDaoProposalState_Pending, err + } + value := new(uint8) + if err := rocketDAOProtocolProposal.Call(opts, value, "getState", big.NewInt(0).SetUint64(proposalId)); err != nil { + return types.ProtocolDaoProposalState_Pending, fmt.Errorf("error getting state of proposal %d: %w", proposalId, err) + } + return types.ProtocolDaoProposalState(*value), nil +} + +// Get the option that the address voted on for the proposal, and whether or not it's voted yet +func GetAddressVoteDirection(rp *rocketpool.RocketPool, proposalId uint64, address common.Address, opts *bind.CallOpts) (types.VoteDirection, error) { + rocketDAOProtocolProposal, err := getRocketDAOProtocolProposal(rp, nil) + if err != nil { + return types.VoteDirection_NoVote, err + } + value := new(uint8) + if err := rocketDAOProtocolProposal.Call(opts, value, "getReceiptDirection", big.NewInt(0).SetUint64(proposalId), address); err != nil { + return types.VoteDirection_NoVote, fmt.Errorf("error getting voting status of proposal %d by address %s: %w", proposalId, address.Hex(), err) + } + return types.VoteDirection(*value), nil +} + +// ==================== +// === Transactions === +// ==================== + +// Estimate the gas of a proposal submission +func estimateProposalGas(rp *rocketpool.RocketPool, message string, payload []byte, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketDAOProtocolProposal, err := getRocketDAOProtocolProposal(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + err = simulateProposalExecution(rp, payload) + if err != nil { + return rocketpool.GasInfo{}, fmt.Errorf("error simulating proposal execution: %w", err) + } + return rocketDAOProtocolProposal.GetTransactionGasInfo(opts, "propose", message, payload, blockNumber, treeNodes) +} + +// Submit a trusted node DAO proposal +// Returns the ID of the new proposal +func submitProposal(rp *rocketpool.RocketPool, message string, payload []byte, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + rocketDAOProtocolProposal, err := getRocketDAOProtocolProposal(rp, nil) + if err != nil { + return 0, common.Hash{}, err + } + proposalCount, err := dao.GetProposalCount(rp, nil) + if err != nil { + return 0, common.Hash{}, err + } + tx, err := rocketDAOProtocolProposal.Transact(opts, "propose", message, payload, blockNumber, treeNodes) + if err != nil { + return 0, common.Hash{}, fmt.Errorf("error submitting Protocol DAO proposal: %w", err) + } + return proposalCount + 1, tx.Hash(), nil +} + +// Estimate the gas of VoteOnProposal +func EstimateVoteOnProposalGas(rp *rocketpool.RocketPool, proposalId uint64, voteDirection types.VoteDirection, votingPower *big.Int, nodeIndex uint64, witness []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketDAOProtocolProposal, err := getRocketDAOProtocolProposal(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketDAOProtocolProposal.GetTransactionGasInfo(opts, "vote", big.NewInt(int64(proposalId)), voteDirection, votingPower, big.NewInt(int64(nodeIndex)), witness) +} + +// Vote on a submitted proposal +func VoteOnProposal(rp *rocketpool.RocketPool, proposalId uint64, voteDirection types.VoteDirection, votingPower *big.Int, nodeIndex uint64, witness []types.VotingTreeNode, opts *bind.TransactOpts) (common.Hash, error) { + rocketDAOProtocolProposal, err := getRocketDAOProtocolProposal(rp, nil) + if err != nil { + return common.Hash{}, err + } + tx, err := rocketDAOProtocolProposal.Transact(opts, "vote", big.NewInt(int64(proposalId)), voteDirection, votingPower, big.NewInt(int64(nodeIndex)), witness) + if err != nil { + return common.Hash{}, fmt.Errorf("error voting on Protocol DAO proposal %d: %w", proposalId, err) + } + return tx.Hash(), nil +} + +// Estimate the gas of OverrideVote +func EstimateOverrideVoteGas(rp *rocketpool.RocketPool, proposalId uint64, voteDirection types.VoteDirection, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketDAOProtocolProposal, err := getRocketDAOProtocolProposal(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketDAOProtocolProposal.GetTransactionGasInfo(opts, "overrideVote", big.NewInt(int64(proposalId)), voteDirection) +} + +// Override a delegate's vote during pDAO voting phase 2 +func OverrideVote(rp *rocketpool.RocketPool, proposalId uint64, voteDirection types.VoteDirection, opts *bind.TransactOpts) (common.Hash, error) { + rocketDAOProtocolProposal, err := getRocketDAOProtocolProposal(rp, nil) + if err != nil { + return common.Hash{}, err + } + tx, err := rocketDAOProtocolProposal.Transact(opts, "overrideVote", big.NewInt(int64(proposalId)), voteDirection) + if err != nil { + return common.Hash{}, fmt.Errorf("error overriding vote on Protocol DAO proposal %d: %w", proposalId, err) + } + return tx.Hash(), nil +} + +// Estimate the gas of Finalize +func EstimateFinalizeGas(rp *rocketpool.RocketPool, proposalId uint64, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketDAOProtocolProposal, err := getRocketDAOProtocolProposal(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketDAOProtocolProposal.GetTransactionGasInfo(opts, "finalise", big.NewInt(int64(proposalId))) +} + +// Finalizes a vetoed proposal by burning the proposer's bond +func Finalize(rp *rocketpool.RocketPool, proposalId uint64, opts *bind.TransactOpts) (common.Hash, error) { + rocketDAOProtocolProposal, err := getRocketDAOProtocolProposal(rp, nil) + if err != nil { + return common.Hash{}, err + } + tx, err := rocketDAOProtocolProposal.Transact(opts, "finalise", big.NewInt(int64(proposalId))) + if err != nil { + return common.Hash{}, fmt.Errorf("error finalizing Protocol DAO proposal %d: %w", proposalId, err) + } + return tx.Hash(), nil +} + +// Estimate the gas of ExecuteProposal +func EstimateExecuteProposalGas(rp *rocketpool.RocketPool, proposalId uint64, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketDAOProtocolProposal, err := getRocketDAOProtocolProposal(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketDAOProtocolProposal.GetTransactionGasInfo(opts, "execute", big.NewInt(int64(proposalId))) +} + +// Execute a submitted proposal +func ExecuteProposal(rp *rocketpool.RocketPool, proposalId uint64, opts *bind.TransactOpts) (common.Hash, error) { + rocketDAOProtocolProposal, err := getRocketDAOProtocolProposal(rp, nil) + if err != nil { + return common.Hash{}, err + } + tx, err := rocketDAOProtocolProposal.Transact(opts, "execute", big.NewInt(int64(proposalId))) + if err != nil { + return common.Hash{}, fmt.Errorf("error executing Protocol DAO proposal %d: %w", proposalId, err) + } + return tx.Hash(), nil +} + +// Simulate a proposal's execution to verify it won't revert +func simulateProposalExecution(rp *rocketpool.RocketPool, payload []byte) error { + rocketDAOProtocolProposal, err := getRocketDAOProtocolProposal(rp, nil) + if err != nil { + return err + } + rocketDAOProtocolProposals, err := getRocketDAOProtocolProposals(rp, nil) + if err != nil { + return err + } + + _, err = rp.Client.EstimateGas(context.Background(), ethereum.CallMsg{ + From: *rocketDAOProtocolProposal.Address, + To: rocketDAOProtocolProposals.Address, + GasPrice: big.NewInt(0), + Value: nil, + Data: payload, + }) + return err +} + +// Get contracts +var rocketDAOProtocolProposalLock sync.Mutex + +func getRocketDAOProtocolProposal(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*rocketpool.Contract, error) { + rocketDAOProtocolProposalLock.Lock() + defer rocketDAOProtocolProposalLock.Unlock() + return rp.GetContract("rocketDAOProtocolProposal", opts) +} diff --git a/bindings/dao/protocol/proposals.go b/bindings/dao/protocol/proposals.go new file mode 100644 index 000000000..f205e8e49 --- /dev/null +++ b/bindings/dao/protocol/proposals.go @@ -0,0 +1,393 @@ +package protocol + +import ( + "fmt" + "math/big" + "sync" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/math" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/types" +) + +// Estimate the gas of ProposeSetMulti +func EstimateProposeSetMultiGas(rp *rocketpool.RocketPool, message string, contractNames []string, settingPaths []string, settingTypes []types.ProposalSettingType, values []any, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketDAOProtocolProposals, err := getRocketDAOProtocolProposals(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + encodedValues, err := abiEncodeMultiValues(settingTypes, values) + if err != nil { + return rocketpool.GasInfo{}, fmt.Errorf("error ABI encoding values: %w", err) + } + payload, err := rocketDAOProtocolProposals.ABI.Pack("proposalSettingMulti", contractNames, settingPaths, settingTypes, encodedValues) + if err != nil { + return rocketpool.GasInfo{}, fmt.Errorf("error setting multi-set proposal payload: %w", err) + } + return estimateProposalGas(rp, message, payload, blockNumber, treeNodes, opts) +} + +// Submit a proposal to update multiple Protocol DAO settings at once +func ProposeSetMulti(rp *rocketpool.RocketPool, message string, contractNames []string, settingPaths []string, settingTypes []types.ProposalSettingType, values []any, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + rocketDAOProtocolProposals, err := getRocketDAOProtocolProposals(rp, nil) + if err != nil { + return 0, common.Hash{}, err + } + encodedValues, err := abiEncodeMultiValues(settingTypes, values) + if err != nil { + return 0, common.Hash{}, fmt.Errorf("error ABI encoding values: %w", err) + } + payload, err := rocketDAOProtocolProposals.ABI.Pack("proposalSettingMulti", contractNames, settingPaths, settingTypes, encodedValues) + if err != nil { + return 0, common.Hash{}, fmt.Errorf("error setting multi-set proposal payload: %w", err) + } + return submitProposal(rp, message, payload, blockNumber, treeNodes, opts) +} + +// Estimate the gas of ProposeSetBool +func EstimateProposeSetBoolGas(rp *rocketpool.RocketPool, message, contractName, settingPath string, value bool, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketDAOProtocolProposals, err := getRocketDAOProtocolProposals(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + payload, err := rocketDAOProtocolProposals.ABI.Pack("proposalSettingBool", contractName, settingPath, value) + if err != nil { + return rocketpool.GasInfo{}, fmt.Errorf("error setting bool setting proposal payload: %w", err) + } + return estimateProposalGas(rp, message, payload, blockNumber, treeNodes, opts) +} + +// Submit a proposal to update a bool Protocol DAO setting +func ProposeSetBool(rp *rocketpool.RocketPool, message, contractName, settingPath string, value bool, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + rocketDAOProtocolProposals, err := getRocketDAOProtocolProposals(rp, nil) + if err != nil { + return 0, common.Hash{}, err + } + payload, err := rocketDAOProtocolProposals.ABI.Pack("proposalSettingBool", contractName, settingPath, value) + if err != nil { + return 0, common.Hash{}, fmt.Errorf("error setting bool setting proposal payload: %w", err) + } + return submitProposal(rp, message, payload, blockNumber, treeNodes, opts) +} + +// Estimate the gas of ProposeSetUint +func EstimateProposeSetUintGas(rp *rocketpool.RocketPool, message, contractName, settingPath string, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketDAOProtocolProposals, err := getRocketDAOProtocolProposals(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + payload, err := rocketDAOProtocolProposals.ABI.Pack("proposalSettingUint", contractName, settingPath, value) + if err != nil { + return rocketpool.GasInfo{}, fmt.Errorf("error encoding set uint setting proposal payload: %w", err) + } + return estimateProposalGas(rp, message, payload, blockNumber, treeNodes, opts) +} + +// Submit a proposal to update a uint Protocol DAO setting +func ProposeSetUint(rp *rocketpool.RocketPool, message, contractName, settingPath string, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + rocketDAOProtocolProposals, err := getRocketDAOProtocolProposals(rp, nil) + if err != nil { + return 0, common.Hash{}, err + } + payload, err := rocketDAOProtocolProposals.ABI.Pack("proposalSettingUint", contractName, settingPath, value) + if err != nil { + return 0, common.Hash{}, fmt.Errorf("error encoding set uint setting proposal payload: %w", err) + } + return submitProposal(rp, message, payload, blockNumber, treeNodes, opts) +} + +// Estimate the gas of ProposeSetAddress +func EstimateProposeSetAddressGas(rp *rocketpool.RocketPool, message, contractName, settingPath string, value common.Address, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketDAOProtocolProposals, err := getRocketDAOProtocolProposals(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + payload, err := rocketDAOProtocolProposals.ABI.Pack("proposalSettingAddress", contractName, settingPath, value) + if err != nil { + return rocketpool.GasInfo{}, fmt.Errorf("error encoding set address setting proposal payload: %w", err) + } + return estimateProposalGas(rp, message, payload, blockNumber, treeNodes, opts) +} + +// Submit a proposal to update an address Protocol DAO setting +func ProposeSetAddress(rp *rocketpool.RocketPool, message, contractName, settingPath string, value common.Address, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + rocketDAOProtocolProposals, err := getRocketDAOProtocolProposals(rp, nil) + if err != nil { + return 0, common.Hash{}, err + } + payload, err := rocketDAOProtocolProposals.ABI.Pack("proposalSettingAddress", contractName, settingPath, value) + if err != nil { + return 0, common.Hash{}, fmt.Errorf("error encoding set address setting proposal payload: %w", err) + } + return submitProposal(rp, message, payload, blockNumber, treeNodes, opts) +} + +// Estimate the gas of ProposeSetRewardsPercentage +func EstimateProposeSetRewardsPercentageGas(rp *rocketpool.RocketPool, message string, odaoPercentage *big.Int, pdaoPercentage *big.Int, nodePercentage *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketDAOProtocolProposals, err := getRocketDAOProtocolProposals(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + payload, err := rocketDAOProtocolProposals.ABI.Pack("proposalSettingRewardsClaimers", odaoPercentage, pdaoPercentage, nodePercentage) + if err != nil { + return rocketpool.GasInfo{}, fmt.Errorf("error encoding set rewards-claimers percent proposal payload: %w", err) + } + return estimateProposalGas(rp, message, payload, blockNumber, treeNodes, opts) +} + +// Submit a proposal to update the allocations of RPL rewards +func ProposeSetRewardsPercentage(rp *rocketpool.RocketPool, message string, odaoPercentage *big.Int, pdaoPercentage *big.Int, nodePercentage *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + rocketDAOProtocolProposals, err := getRocketDAOProtocolProposals(rp, nil) + if err != nil { + return 0, common.Hash{}, err + } + payload, err := rocketDAOProtocolProposals.ABI.Pack("proposalSettingRewardsClaimers", odaoPercentage, pdaoPercentage, nodePercentage) + if err != nil { + return 0, common.Hash{}, fmt.Errorf("error encoding set rewards-claimers percent proposal payload: %w", err) + } + return submitProposal(rp, message, payload, blockNumber, treeNodes, opts) +} + +// Estimate the gas of ProposeOneTimeTreasurySpend +func EstimateProposeOneTimeTreasurySpendGas(rp *rocketpool.RocketPool, message, invoiceID string, recipient common.Address, amount *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketDAOProtocolProposals, err := getRocketDAOProtocolProposals(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + payload, err := rocketDAOProtocolProposals.ABI.Pack("proposalTreasuryOneTimeSpend", invoiceID, recipient, amount) + if err != nil { + return rocketpool.GasInfo{}, fmt.Errorf("error encoding set spend-treasury percent proposal payload: %w", err) + } + return estimateProposalGas(rp, message, payload, blockNumber, treeNodes, opts) +} + +// Submit a proposal to spend a portion of the Rocket Pool treasury one time +func ProposeOneTimeTreasurySpend(rp *rocketpool.RocketPool, message, invoiceID string, recipient common.Address, amount *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + rocketDAOProtocolProposals, err := getRocketDAOProtocolProposals(rp, nil) + if err != nil { + return 0, common.Hash{}, err + } + payload, err := rocketDAOProtocolProposals.ABI.Pack("proposalTreasuryOneTimeSpend", invoiceID, recipient, amount) + if err != nil { + return 0, common.Hash{}, fmt.Errorf("error encoding set spend-treasury percent proposal payload: %w", err) + } + return submitProposal(rp, message, payload, blockNumber, treeNodes, opts) +} + +// Estimate the gas of ProposeRecurringTreasurySpend +func EstimateProposeRecurringTreasurySpendGas(rp *rocketpool.RocketPool, message string, contractName string, recipient common.Address, amountPerPeriod *big.Int, periodLength time.Duration, startTime time.Time, numberOfPeriods uint64, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketDAOProtocolProposals, err := getRocketDAOProtocolProposals(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + payload, err := rocketDAOProtocolProposals.ABI.Pack("proposalTreasuryNewContract", contractName, recipient, amountPerPeriod, big.NewInt(int64(periodLength.Seconds())), big.NewInt(startTime.Unix()), big.NewInt(int64(numberOfPeriods))) + if err != nil { + return rocketpool.GasInfo{}, fmt.Errorf("error encoding proposalTreasuryNewContract payload: %w", err) + } + return estimateProposalGas(rp, message, payload, blockNumber, treeNodes, opts) +} + +// Submit a proposal to spend a portion of the Rocket Pool treasury in a recurring manner +func ProposeRecurringTreasurySpend(rp *rocketpool.RocketPool, message string, contractName string, recipient common.Address, amountPerPeriod *big.Int, periodLength time.Duration, startTime time.Time, numberOfPeriods uint64, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + rocketDAOProtocolProposals, err := getRocketDAOProtocolProposals(rp, nil) + if err != nil { + return 0, common.Hash{}, err + } + payload, err := rocketDAOProtocolProposals.ABI.Pack("proposalTreasuryNewContract", contractName, recipient, amountPerPeriod, big.NewInt(int64(periodLength.Seconds())), big.NewInt(startTime.Unix()), big.NewInt(int64(numberOfPeriods))) + if err != nil { + return 0, common.Hash{}, fmt.Errorf("error encoding proposalTreasuryNewContract payload: %w", err) + } + return submitProposal(rp, message, payload, blockNumber, treeNodes, opts) +} + +// Estimate the gas of ProposeRecurringTreasurySpendUpdate +func EstimateProposeRecurringTreasurySpendUpdateGas(rp *rocketpool.RocketPool, message string, contractName string, recipient common.Address, amountPerPeriod *big.Int, periodLength time.Duration, numberOfPeriods uint64, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketDAOProtocolProposals, err := getRocketDAOProtocolProposals(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + payload, err := rocketDAOProtocolProposals.ABI.Pack("proposalTreasuryUpdateContract", contractName, recipient, amountPerPeriod, big.NewInt(int64(periodLength.Seconds())), big.NewInt(int64(numberOfPeriods))) + if err != nil { + return rocketpool.GasInfo{}, fmt.Errorf("error encoding proposalTreasuryUpdateContract payload: %w", err) + } + return estimateProposalGas(rp, message, payload, blockNumber, treeNodes, opts) +} + +// Submit a proposal to update a recurrint Rocket Pool treasury spending plan +func ProposeRecurringTreasurySpendUpdate(rp *rocketpool.RocketPool, message string, contractName string, recipient common.Address, amountPerPeriod *big.Int, periodLength time.Duration, numberOfPeriods uint64, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + rocketDAOProtocolProposals, err := getRocketDAOProtocolProposals(rp, nil) + if err != nil { + return 0, common.Hash{}, err + } + payload, err := rocketDAOProtocolProposals.ABI.Pack("proposalTreasuryUpdateContract", contractName, recipient, amountPerPeriod, big.NewInt(int64(periodLength.Seconds())), big.NewInt(int64(numberOfPeriods))) + if err != nil { + return 0, common.Hash{}, fmt.Errorf("error encoding proposalTreasuryUpdateContract payload: %w", err) + } + return submitProposal(rp, message, payload, blockNumber, treeNodes, opts) +} + +// Estimate the gas of ProposeInviteToSecurityCouncil +func EstimateProposeInviteToSecurityCouncilGas(rp *rocketpool.RocketPool, message string, id string, address common.Address, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketDAOProtocolProposals, err := getRocketDAOProtocolProposals(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + payload, err := rocketDAOProtocolProposals.ABI.Pack("proposalSecurityInvite", id, address) + if err != nil { + return rocketpool.GasInfo{}, fmt.Errorf("error encoding proposalSecurityInvite payload: %w", err) + } + return estimateProposalGas(rp, message, payload, blockNumber, treeNodes, opts) +} + +// Submit a proposal to invite a member to the security council +func ProposeInviteToSecurityCouncil(rp *rocketpool.RocketPool, message string, id string, address common.Address, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + rocketDAOProtocolProposals, err := getRocketDAOProtocolProposals(rp, nil) + if err != nil { + return 0, common.Hash{}, err + } + payload, err := rocketDAOProtocolProposals.ABI.Pack("proposalSecurityInvite", id, address) + if err != nil { + return 0, common.Hash{}, fmt.Errorf("error encoding proposalSecurityInvite payload: %w", err) + } + return submitProposal(rp, message, payload, blockNumber, treeNodes, opts) +} + +// Estimate the gas of ProposeKickFromSecurityCouncil +func EstimateProposeKickFromSecurityCouncilGas(rp *rocketpool.RocketPool, message string, address common.Address, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketDAOProtocolProposals, err := getRocketDAOProtocolProposals(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + payload, err := rocketDAOProtocolProposals.ABI.Pack("proposalSecurityKick", address) + if err != nil { + return rocketpool.GasInfo{}, fmt.Errorf("error encoding proposalSecurityKick payload: %w", err) + } + return estimateProposalGas(rp, message, payload, blockNumber, treeNodes, opts) +} + +// Submit a proposal to kick a member from the security council +func ProposeKickFromSecurityCouncil(rp *rocketpool.RocketPool, message string, address common.Address, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + rocketDAOProtocolProposals, err := getRocketDAOProtocolProposals(rp, nil) + if err != nil { + return 0, common.Hash{}, err + } + payload, err := rocketDAOProtocolProposals.ABI.Pack("proposalSecurityKick", address) + if err != nil { + return 0, common.Hash{}, fmt.Errorf("error encoding proposalSecurityKick payload: %w", err) + } + return submitProposal(rp, message, payload, blockNumber, treeNodes, opts) +} + +// Estimate the gas of ProposeKickMultiFromSecurityCouncil +func EstimateProposeKickMultiFromSecurityCouncilGas(rp *rocketpool.RocketPool, message string, addresses []common.Address, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketDAOProtocolProposals, err := getRocketDAOProtocolProposals(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + payload, err := rocketDAOProtocolProposals.ABI.Pack("proposalSecurityKickMulti", addresses) + if err != nil { + return rocketpool.GasInfo{}, fmt.Errorf("error encoding proposalSecurityKickMulti payload: %w", err) + } + return estimateProposalGas(rp, message, payload, blockNumber, treeNodes, opts) +} + +// Submit a proposal to kick multiple members from the security council +func ProposeKickMultiFromSecurityCouncil(rp *rocketpool.RocketPool, message string, addresses []common.Address, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + rocketDAOProtocolProposals, err := getRocketDAOProtocolProposals(rp, nil) + if err != nil { + return 0, common.Hash{}, err + } + payload, err := rocketDAOProtocolProposals.ABI.Pack("proposalSecurityKickMulti", addresses) + if err != nil { + return 0, common.Hash{}, fmt.Errorf("error encoding proposalSecurityKickMulti payload: %w", err) + } + return submitProposal(rp, message, payload, blockNumber, treeNodes, opts) +} + +// Estimate the gas of ProposeReplaceSecurityCouncilMember +func EstimateProposeReplaceSecurityCouncilMemberGas(rp *rocketpool.RocketPool, message string, existingMemberAddress common.Address, newMemberID string, newMemberAddress common.Address, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketDAOProtocolProposals, err := getRocketDAOProtocolProposals(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + payload, err := rocketDAOProtocolProposals.ABI.Pack("proposalSecurityReplace", existingMemberAddress, newMemberID, newMemberAddress) + if err != nil { + return rocketpool.GasInfo{}, fmt.Errorf("error encoding proposalSecurityReplace payload: %w", err) + } + return estimateProposalGas(rp, message, payload, blockNumber, treeNodes, opts) +} + +// Submit a proposal to replace a member of the security council with another one in a single TX +func ProposeReplaceSecurityCouncilMember(rp *rocketpool.RocketPool, message string, existingMemberAddress common.Address, newMemberID string, newMemberAddress common.Address, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + rocketDAOProtocolProposals, err := getRocketDAOProtocolProposals(rp, nil) + if err != nil { + return 0, common.Hash{}, err + } + payload, err := rocketDAOProtocolProposals.ABI.Pack("proposalSecurityReplace", existingMemberAddress, newMemberID, newMemberAddress) + if err != nil { + return 0, common.Hash{}, fmt.Errorf("error encoding proposalSecurityReplace payload: %w", err) + } + return submitProposal(rp, message, payload, blockNumber, treeNodes, opts) +} + +// Get the ABI encoding of multiple values for a ProposeSettingMulti call +func abiEncodeMultiValues(settingTypes []types.ProposalSettingType, values []any) ([][]byte, error) { + // Sanity check the lengths + settingCount := len(settingTypes) + if settingCount != len(values) { + return nil, fmt.Errorf("settingTypes and values must be the same length") + } + if settingCount == 0 { + return [][]byte{}, nil + } + + // ABI encode each value + results := make([][]byte, settingCount) + for i, settingType := range settingTypes { + var encodedArg []byte + switch settingType { + case types.ProposalSettingType_Uint256: + arg, success := values[i].(*big.Int) + if !success { + return nil, fmt.Errorf("value %d is not a *big.Int, but the setting type is Uint256", i) + } + encodedArg = math.U256Bytes(big.NewInt(0).Set(arg)) + + case types.ProposalSettingType_Bool: + arg, success := values[i].(bool) + if !success { + return nil, fmt.Errorf("value %d is not a bool, but the setting type is Bool", i) + } + if arg { + encodedArg = math.PaddedBigBytes(common.Big1, 32) + } else { + encodedArg = math.PaddedBigBytes(common.Big0, 32) + } + + case types.ProposalSettingType_Address: + arg, success := values[i].(common.Address) + if !success { + return nil, fmt.Errorf("value %d is not an address, but the setting type is Address", i) + } + encodedArg = common.LeftPadBytes(arg.Bytes(), 32) + + default: + return nil, fmt.Errorf("unknown proposal setting type [%v]", settingType) + } + results[i] = encodedArg + } + + return results, nil +} + +// Get contracts +var rocketDAOProtocolProposalsLock sync.Mutex + +func getRocketDAOProtocolProposals(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*rocketpool.Contract, error) { + rocketDAOProtocolProposalsLock.Lock() + defer rocketDAOProtocolProposalsLock.Unlock() + return rp.GetContract("rocketDAOProtocolProposals", opts) +} diff --git a/bindings/dao/protocol/verify.go b/bindings/dao/protocol/verify.go new file mode 100755 index 000000000..55a27f79d --- /dev/null +++ b/bindings/dao/protocol/verify.go @@ -0,0 +1,535 @@ +package protocol + +import ( + "context" + "fmt" + "math/big" + "sync" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/types" + "github.com/rocket-pool/smartnode/bindings/utils/eth" + "github.com/rocket-pool/smartnode/bindings/utils/multicall" + "golang.org/x/sync/errgroup" +) + +const ( + challengeStateBatchSize uint64 = 500 +) + +// Structure of the RootSubmitted event +type RootSubmitted struct { + ProposalID *big.Int `json:"proposalId"` + Proposer common.Address `json:"proposer"` + BlockNumber uint32 `json:"blockNumber"` + Index *big.Int `json:"index"` + Root types.VotingTreeNode `json:"root"` + TreeNodes []types.VotingTreeNode `json:"treeNodes"` + Timestamp time.Time `json:"timestamp"` +} + +// Internal struct - returned by the RootSubmitted event +type rootSubmittedRaw struct { + BlockNumber uint32 `json:"blockNumber"` + Index *big.Int `json:"index"` + Root types.VotingTreeNode `json:"root"` + TreeNodes []types.VotingTreeNode `json:"treeNodes"` + Timestamp *big.Int `json:"timestamp"` +} + +// Structure of the ChallengeSubmitted event +type ChallengeSubmitted struct { + ProposalID *big.Int `json:"proposalId"` + Challenger common.Address `json:"challenger"` + Index *big.Int `json:"index"` + Timestamp time.Time `json:"timestamp"` +} + +// Internal struct - returned by the ChallengeSubmitted event +type challengeSubmittedRaw struct { + Index *big.Int `json:"index"` + Timestamp *big.Int `json:"timestamp"` +} + +// Get the depth-per-round for voting trees +func GetDepthPerRound(rp *rocketpool.RocketPool, opts *bind.CallOpts) (uint64, error) { + rocketDAOProtocolVerifier, err := getRocketDAOProtocolVerifier(rp, opts) + if err != nil { + return 0, err + } + value := new(*big.Int) + if err := rocketDAOProtocolVerifier.Call(opts, value, "getDepthPerRound"); err != nil { + return 0, fmt.Errorf("error getting depth per round: %w", err) + } + return (*value).Uint64(), nil +} + +// Get the node of a proposal at the given index +func GetNode(rp *rocketpool.RocketPool, proposalId uint64, index uint64, opts *bind.CallOpts) (types.VotingTreeNode, error) { + rocketDAOProtocolVerifier, err := getRocketDAOProtocolVerifier(rp, opts) + if err != nil { + return types.VotingTreeNode{}, err + } + // define a struct to unmarshall the VotingTreeNode data from the smart contract call + res := new(struct { + Sum *big.Int `json:"sum"` + Hash [32]byte `json:"hash"` + }) + err = rocketDAOProtocolVerifier.Call(opts, &res, "getNode", big.NewInt(int64(proposalId)), big.NewInt(int64(index))) + if err != nil { + return types.VotingTreeNode{}, fmt.Errorf("error getting proposal %d / index %d node: %w", proposalId, index, err) + } + // convert the [32]byte field into a common.Hash + node := types.VotingTreeNode{ + Sum: res.Sum, + Hash: common.BytesToHash(res.Hash[:]), + } + + return node, nil +} + +// Estimate the gas of CreateChallenge +func EstimateCreateChallengeGas(rp *rocketpool.RocketPool, proposalId uint64, index uint64, node types.VotingTreeNode, witness []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketDAOProtocolVerifier, err := getRocketDAOProtocolVerifier(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketDAOProtocolVerifier.GetTransactionGasInfo(opts, "createChallenge", big.NewInt(int64(proposalId)), big.NewInt((int64(index))), node, witness) +} + +// Challenge a proposal at a specific tree node index, providing a Merkle proof of the node as well +func CreateChallenge(rp *rocketpool.RocketPool, proposalId uint64, index uint64, node types.VotingTreeNode, witness []types.VotingTreeNode, opts *bind.TransactOpts) (common.Hash, error) { + rocketDAOProtocolVerifier, err := getRocketDAOProtocolVerifier(rp, nil) + if err != nil { + return common.Hash{}, err + } + tx, err := rocketDAOProtocolVerifier.Transact(opts, "createChallenge", big.NewInt(int64(proposalId)), big.NewInt((int64(index))), node, witness) + if err != nil { + return common.Hash{}, fmt.Errorf("error creating challenge: %w", err) + } + return tx.Hash(), nil +} + +// Estimate the gas of SubmitRoot +func EstimateSubmitRootGas(rp *rocketpool.RocketPool, proposalId uint64, index uint64, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketDAOProtocolVerifier, err := getRocketDAOProtocolVerifier(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketDAOProtocolVerifier.GetTransactionGasInfo(opts, "submitRoot", big.NewInt(int64(proposalId)), big.NewInt((int64(index))), treeNodes) +} + +// Submit the Merkle root for a proposal at the specific index in response to a challenge +func SubmitRoot(rp *rocketpool.RocketPool, proposalId uint64, index uint64, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (common.Hash, error) { + rocketDAOProtocolVerifier, err := getRocketDAOProtocolVerifier(rp, nil) + if err != nil { + return common.Hash{}, err + } + tx, err := rocketDAOProtocolVerifier.Transact(opts, "submitRoot", big.NewInt(int64(proposalId)), big.NewInt((int64(index))), treeNodes) + if err != nil { + return common.Hash{}, fmt.Errorf("error submitting proposal root: %w", err) + } + return tx.Hash(), nil +} + +// Get the state of a challenge on a proposal and tree node index +func GetChallengeState(rp *rocketpool.RocketPool, proposalId uint64, index uint64, opts *bind.CallOpts) (types.ChallengeState, error) { + rocketDAOProtocolVerifier, err := getRocketDAOProtocolVerifier(rp, opts) + if err != nil { + return types.ChallengeState_Unchallenged, err + } + state := new(uint8) + if err := rocketDAOProtocolVerifier.Call(opts, state, "getChallengeState", big.NewInt(int64(proposalId)), big.NewInt(int64(index))); err != nil { + return types.ChallengeState_Unchallenged, fmt.Errorf("error getting proposal %d / index %d challenge state: %w", proposalId, index, err) + } + challengeState := types.ChallengeState(*state) + return challengeState, nil +} + +// Get the defeat index for a proposal +func GetDefeatIndex(rp *rocketpool.RocketPool, proposalId uint64, opts *bind.CallOpts) (uint64, error) { + rocketDAOProtocolVerifier, err := getRocketDAOProtocolVerifier(rp, opts) + if err != nil { + return 0, err + } + value := new(*big.Int) + if err := rocketDAOProtocolVerifier.Call(opts, value, "getDefeatIndex", big.NewInt(int64(proposalId))); err != nil { + return 0, fmt.Errorf("error getting proposal %d defeat index: %w", proposalId, err) + } + return (*value).Uint64(), nil +} + +// Get the proposal bond for a proposal +func GetProposalBond(rp *rocketpool.RocketPool, proposalId uint64, opts *bind.CallOpts) (*big.Int, error) { + rocketDAOProtocolVerifier, err := getRocketDAOProtocolVerifier(rp, opts) + if err != nil { + return nil, err + } + value := new(*big.Int) + if err := rocketDAOProtocolVerifier.Call(opts, value, "getProposalBond", big.NewInt(int64(proposalId))); err != nil { + return nil, fmt.Errorf("error getting proposal %d proposal bond: %w", proposalId, err) + } + return *value, nil +} + +// Get the challenge bond for a proposal +func GetChallengeBond(rp *rocketpool.RocketPool, proposalId uint64, opts *bind.CallOpts) (*big.Int, error) { + rocketDAOProtocolVerifier, err := getRocketDAOProtocolVerifier(rp, opts) + if err != nil { + return nil, err + } + value := new(*big.Int) + if err := rocketDAOProtocolVerifier.Call(opts, value, "getChallengeBond", big.NewInt(int64(proposalId))); err != nil { + return nil, fmt.Errorf("error getting proposal %d challenge bond: %w", proposalId, err) + } + return *value, nil +} + +// Get the challenge period for a proposal +func GetChallengePeriod(rp *rocketpool.RocketPool, proposalId uint64, opts *bind.CallOpts) (time.Duration, error) { + rocketDAOProtocolVerifier, err := getRocketDAOProtocolVerifier(rp, opts) + if err != nil { + return 0, err + } + value := new(*big.Int) + if err := rocketDAOProtocolVerifier.Call(opts, value, "getChallengePeriod", big.NewInt(int64(proposalId))); err != nil { + return 0, fmt.Errorf("error getting proposal %d challenge period: %w", proposalId, err) + } + return time.Second * time.Duration((*value).Uint64()), nil +} + +// Get the states of multiple challenges using multicall +// NOTE: wen v2... +func GetMultiChallengeStatesFast(rp *rocketpool.RocketPool, multicallAddress common.Address, proposalIds []uint64, challengedIndices []uint64, opts *bind.CallOpts) ([]types.ChallengeState, error) { + rocketDAOProtocolVerifier, err := getRocketDAOProtocolVerifier(rp, opts) + if err != nil { + return nil, err + } + + if opts == nil { + // Get the latest block + blockNum, err := rp.Client.BlockNumber(context.Background()) + if err != nil { + return nil, fmt.Errorf("error getting latest block number: %w", err) + } + opts = &bind.CallOpts{ + BlockNumber: big.NewInt(int64(blockNum)), + } + } + + count := uint64(len(proposalIds)) + if count != uint64(len(challengedIndices)) { + return nil, fmt.Errorf("have %d proposal IDs but %d challenge indices", count, len(challengedIndices)) + } + + // Sync + var wg errgroup.Group + + // Run the getters in batches + rawStates := make([]uint8, count) + for i := uint64(0); i < count; i += challengeStateBatchSize { + i := i + max := i + challengeStateBatchSize + if max > count { + max = count + } + + // Load details + wg.Go(func() error { + var err error + mc, err := multicall.NewMultiCaller(rp.Client, multicallAddress) + if err != nil { + return err + } + for j := i; j < max; j++ { + propID := big.NewInt(int64(proposalIds[j])) + challengedIndex := big.NewInt(int64(challengedIndices[j])) + mc.AddCall(rocketDAOProtocolVerifier, &rawStates[j], "getChallengeState", propID, challengedIndex) + } + _, err = mc.FlexibleCall(true, opts) + if err != nil { + return fmt.Errorf("error executing multicall: %w", err) + } + return nil + }) + } + + // Wait for data + if err := wg.Wait(); err != nil { + return nil, err + } + + // Cast the results + states := make([]types.ChallengeState, count) + for i, state := range rawStates { + states[i] = types.ChallengeState(state) + } + return states, nil +} + +// Get RootSubmitted event info +func GetRootSubmittedEvents(rp *rocketpool.RocketPool, proposalIDs []uint64, intervalSize *big.Int, startBlock *big.Int, endBlock *big.Int, verifierAddresses []common.Address, opts *bind.CallOpts) ([]RootSubmitted, error) { + // Get the contract + rocketDAOProtocolVerifier, err := getRocketDAOProtocolVerifier(rp, opts) + if err != nil { + return nil, err + } + + // Construct a filter query for relevant logs + idBuffers := make([]common.Hash, len(proposalIDs)) + for i, id := range proposalIDs { + proposalIdBig := big.NewInt(0).SetUint64(id) + proposalIdBig.FillBytes(idBuffers[i][:]) + } + + // Create the list of addresses to check + currentAddress := *rocketDAOProtocolVerifier.Address + if verifierAddresses == nil { + verifierAddresses = []common.Address{currentAddress} + } else { + found := false + for _, address := range verifierAddresses { + if address == currentAddress { + found = true + break + } + } + if !found { + verifierAddresses = append(verifierAddresses, currentAddress) + } + } + + rootSubmittedEvent := rocketDAOProtocolVerifier.ABI.Events["RootSubmitted"] + addressFilter := verifierAddresses + topicFilter := [][]common.Hash{{rootSubmittedEvent.ID}, idBuffers} + + // Get the event logs + logs, err := eth.GetLogs(rp, addressFilter, topicFilter, intervalSize, startBlock, endBlock, nil) + if err != nil { + return nil, err + } + if len(logs) == 0 { + return []RootSubmitted{}, nil + } + + events := make([]RootSubmitted, 0, len(logs)) + for _, log := range logs { + // Get the log info values + values, err := rootSubmittedEvent.Inputs.Unpack(log.Data) + if err != nil { + return nil, fmt.Errorf("error unpacking RootSubmitted event data: %w", err) + } + + // Get the topic values + if len(log.Topics) < 3 { + return nil, fmt.Errorf("event had %d topics but at least 3 are required", len(log.Topics)) + } + idHash := log.Topics[1] + proposerHash := log.Topics[2] + propID := big.NewInt(0).SetBytes(idHash.Bytes()) + proposer := common.BytesToAddress(proposerHash.Bytes()) + + // Convert to a native struct + var raw rootSubmittedRaw + err = rootSubmittedEvent.Inputs.Copy(&raw, values) + if err != nil { + return nil, fmt.Errorf("error converting RootSubmitted event data to struct: %w", err) + } + + // Get the decoded data + events = append(events, RootSubmitted{ + ProposalID: propID, + Proposer: proposer, + BlockNumber: raw.BlockNumber, + Index: raw.Index, + Root: raw.Root, + TreeNodes: raw.TreeNodes, + Timestamp: time.Unix(raw.Timestamp.Int64(), 0), + }) + } + + return events, nil +} + +// Get ChallengeSubmitted event info +func GetChallengeSubmittedEvents(rp *rocketpool.RocketPool, proposalIDs []uint64, intervalSize *big.Int, startBlock *big.Int, endBlock *big.Int, verifierAddresses []common.Address, opts *bind.CallOpts) ([]ChallengeSubmitted, error) { + // Get the contract + rocketDAOProtocolVerifier, err := getRocketDAOProtocolVerifier(rp, opts) + if err != nil { + return nil, err + } + + // Construct a filter query for relevant logs + idBuffers := make([]common.Hash, len(proposalIDs)) + for i, id := range proposalIDs { + proposalIdBig := big.NewInt(0).SetUint64(id) + proposalIdBig.FillBytes(idBuffers[i][:]) + } + + // Create the list of addresses to check + currentAddress := *rocketDAOProtocolVerifier.Address + if verifierAddresses == nil { + verifierAddresses = []common.Address{currentAddress} + } else { + found := false + for _, address := range verifierAddresses { + if address == currentAddress { + found = true + break + } + } + if !found { + verifierAddresses = append(verifierAddresses, currentAddress) + } + } + + challengeSubmittedEvent := rocketDAOProtocolVerifier.ABI.Events["ChallengeSubmitted"] + addressFilter := verifierAddresses + topicFilter := [][]common.Hash{{challengeSubmittedEvent.ID}, idBuffers} + + // Get the event logs + logs, err := eth.GetLogs(rp, addressFilter, topicFilter, intervalSize, startBlock, endBlock, nil) + if err != nil { + return nil, err + } + if len(logs) == 0 { + return []ChallengeSubmitted{}, nil + } + + events := make([]ChallengeSubmitted, 0, len(logs)) + for _, log := range logs { + // Get the log info values + values, err := challengeSubmittedEvent.Inputs.Unpack(log.Data) + if err != nil { + return nil, fmt.Errorf("error unpacking ChallengeSubmitted event data: %w", err) + } + + // Get the topic values + if len(log.Topics) < 3 { + return nil, fmt.Errorf("event had %d topics but at least 3 are required", len(log.Topics)) + } + idHash := log.Topics[1] + challengerHash := log.Topics[2] + propID := big.NewInt(0).SetBytes(idHash.Bytes()) + challenger := common.BytesToAddress(challengerHash.Bytes()) + + // Convert to a native struct + var raw challengeSubmittedRaw + err = challengeSubmittedEvent.Inputs.Copy(&raw, values) + if err != nil { + return nil, fmt.Errorf("error converting ChallengeSubmitted event data to struct: %w", err) + } + + // Get the decoded data + events = append(events, ChallengeSubmitted{ + ProposalID: propID, + Challenger: challenger, + Index: raw.Index, + Timestamp: time.Unix(raw.Timestamp.Int64(), 0), + }) + } + + return events, nil +} + +// Estimate the gas of ClaimBondChallenger +func EstimateClaimBondChallengerGas(rp *rocketpool.RocketPool, proposalID uint64, indices []uint64, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketDAOProtocolVerifier, err := getRocketDAOProtocolVerifier(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + // Make the args + proposalIDBig := big.NewInt(int64(proposalID)) + indicesBig := make([]*big.Int, len(indices)) + for i, index := range indices { + indicesBig[i] = big.NewInt(int64(index)) + } + return rocketDAOProtocolVerifier.GetTransactionGasInfo(opts, "claimBondChallenger", proposalIDBig, indicesBig) +} + +// Claim any RPL bond refunds or rewards for a proposal, as a challenger +func ClaimBondChallenger(rp *rocketpool.RocketPool, proposalID uint64, indices []uint64, opts *bind.TransactOpts) (common.Hash, error) { + rocketDAOProtocolVerifier, err := getRocketDAOProtocolVerifier(rp, nil) + if err != nil { + return common.Hash{}, err + } + // Make the args + proposalIDBig := big.NewInt(int64(proposalID)) + indicesBig := make([]*big.Int, len(indices)) + for i, index := range indices { + indicesBig[i] = big.NewInt(int64(index)) + } + tx, err := rocketDAOProtocolVerifier.Transact(opts, "claimBondChallenger", proposalIDBig, indicesBig) + if err != nil { + return common.Hash{}, err + } + return tx.Hash(), nil +} + +// Estimate the gas of ClaimBondProposer +func EstimateClaimBondProposerGas(rp *rocketpool.RocketPool, proposalID uint64, indices []uint64, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketDAOProtocolVerifier, err := getRocketDAOProtocolVerifier(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + // Make the args + proposalIDBig := big.NewInt(int64(proposalID)) + indicesBig := make([]*big.Int, len(indices)) + for i, index := range indices { + indicesBig[i] = big.NewInt(int64(index)) + } + return rocketDAOProtocolVerifier.GetTransactionGasInfo(opts, "claimBondProposer", proposalIDBig, indicesBig) +} + +// Claim any RPL bond refunds or rewards for a proposal, as the proposer +func ClaimBondProposer(rp *rocketpool.RocketPool, proposalID uint64, indices []uint64, opts *bind.TransactOpts) (common.Hash, error) { + rocketDAOProtocolVerifier, err := getRocketDAOProtocolVerifier(rp, nil) + if err != nil { + return common.Hash{}, err + } + // Make the args + proposalIDBig := big.NewInt(int64(proposalID)) + indicesBig := make([]*big.Int, len(indices)) + for i, index := range indices { + indicesBig[i] = big.NewInt(int64(index)) + } + tx, err := rocketDAOProtocolVerifier.Transact(opts, "claimBondProposer", proposalIDBig, indicesBig) + if err != nil { + return common.Hash{}, err + } + return tx.Hash(), nil +} + +// Estimate the gas of DefeatProposal +func EstimateDefeatProposalGas(rp *rocketpool.RocketPool, proposalId uint64, index uint64, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketDAOProtocolVerifier, err := getRocketDAOProtocolVerifier(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketDAOProtocolVerifier.GetTransactionGasInfo(opts, "defeatProposal", big.NewInt(int64(proposalId)), big.NewInt(int64(index))) +} + +// Defeat a proposal if it fails to respond to a challenge within the challenge window, providing the node index that wasn't responded to +func DefeatProposal(rp *rocketpool.RocketPool, proposalId uint64, index uint64, opts *bind.TransactOpts) (common.Hash, error) { + rocketDAOProtocolVerifier, err := getRocketDAOProtocolVerifier(rp, nil) + if err != nil { + return common.Hash{}, err + } + tx, err := rocketDAOProtocolVerifier.Transact(opts, "defeatProposal", big.NewInt(int64(proposalId)), big.NewInt(int64(index))) + if err != nil { + return common.Hash{}, err + } + return tx.Hash(), nil +} + +// Get contracts +var rocketDAOProtocolVerifierLock sync.Mutex + +func getRocketDAOProtocolVerifier(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*rocketpool.Contract, error) { + rocketDAOProtocolVerifierLock.Lock() + defer rocketDAOProtocolVerifierLock.Unlock() + return rp.GetContract("rocketDAOProtocolVerifier", opts) +} diff --git a/bindings/dao/security/actions.go b/bindings/dao/security/actions.go new file mode 100644 index 000000000..4e23ffd46 --- /dev/null +++ b/bindings/dao/security/actions.go @@ -0,0 +1,130 @@ +package security + +import ( + "fmt" + "sync" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/rocket-pool/smartnode/bindings/rocketpool" +) + +// Estimate the gas of Join +func EstimateJoinGas(rp *rocketpool.RocketPool, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketDAOSecurityActions, err := getRocketDAOSecurityActions(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketDAOSecurityActions.GetTransactionGasInfo(opts, "actionJoin") +} + +// Join the security DAO +// Requires an executed invite proposal +func Join(rp *rocketpool.RocketPool, opts *bind.TransactOpts) (common.Hash, error) { + rocketDAOSecurityActions, err := getRocketDAOSecurityActions(rp, nil) + if err != nil { + return common.Hash{}, err + } + tx, err := rocketDAOSecurityActions.Transact(opts, "actionJoin") + if err != nil { + return common.Hash{}, fmt.Errorf("error joining the security DAO: %w", err) + } + return tx.Hash(), nil +} + +// Estimate the gas of Kick +func EstimateKickGas(rp *rocketpool.RocketPool, address common.Address, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketDAOSecurityActions, err := getRocketDAOSecurityActions(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketDAOSecurityActions.GetTransactionGasInfo(opts, "actionKick", address) +} + +// Removes a member from the security DAO +func Kick(rp *rocketpool.RocketPool, address common.Address, opts *bind.TransactOpts) (common.Hash, error) { + rocketDAOSecurityActions, err := getRocketDAOSecurityActions(rp, nil) + if err != nil { + return common.Hash{}, err + } + tx, err := rocketDAOSecurityActions.Transact(opts, "actionKick", address) + if err != nil { + return common.Hash{}, fmt.Errorf("error kicking %s from the security DAO: %w", address.Hex(), err) + } + return tx.Hash(), nil +} + +// Estimate the gas of KickMulti +func EstimateKickMultiGas(rp *rocketpool.RocketPool, addresses []common.Address, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketDAOSecurityActions, err := getRocketDAOSecurityActions(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketDAOSecurityActions.GetTransactionGasInfo(opts, "actionKickMulti", addresses) +} + +// Removes multiple members from the security DAO +func KickMulti(rp *rocketpool.RocketPool, addresses []common.Address, opts *bind.TransactOpts) (common.Hash, error) { + rocketDAOSecurityActions, err := getRocketDAOSecurityActions(rp, nil) + if err != nil { + return common.Hash{}, err + } + tx, err := rocketDAOSecurityActions.Transact(opts, "actionKickMulti", addresses) + if err != nil { + return common.Hash{}, fmt.Errorf("error kicking members from the security DAO: %w", err) + } + return tx.Hash(), nil +} + +// Estimate the gas of RequestLeave +func EstimateRequestLeaveGas(rp *rocketpool.RocketPool, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketDAOSecurityActions, err := getRocketDAOSecurityActions(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketDAOSecurityActions.GetTransactionGasInfo(opts, "actionRequestLeave") +} + +// A member who wishes to leave the security council can call this method to initiate the process +func RequestLeave(rp *rocketpool.RocketPool, opts *bind.TransactOpts) (common.Hash, error) { + rocketDAOSecurityActions, err := getRocketDAOSecurityActions(rp, nil) + if err != nil { + return common.Hash{}, err + } + tx, err := rocketDAOSecurityActions.Transact(opts, "actionRequestLeave") + if err != nil { + return common.Hash{}, fmt.Errorf("error requesting to leave the security DAO: %w", err) + } + return tx.Hash(), nil +} + +// Estimate the gas of Leave +func EstimateLeaveGas(rp *rocketpool.RocketPool, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketDAOSecurityActions, err := getRocketDAOSecurityActions(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketDAOSecurityActions.GetTransactionGasInfo(opts, "actionLeave") +} + +// A member who has asked to leave and waited the required time can call this method to formally leave the security council +func Leave(rp *rocketpool.RocketPool, opts *bind.TransactOpts) (common.Hash, error) { + rocketDAOSecurityActions, err := getRocketDAOSecurityActions(rp, nil) + if err != nil { + return common.Hash{}, err + } + tx, err := rocketDAOSecurityActions.Transact(opts, "actionLeave") + if err != nil { + return common.Hash{}, fmt.Errorf("error leaving the security DAO: %w", err) + } + return tx.Hash(), nil +} + +// Get contracts +var rocketDAOSecurityActionsLock sync.Mutex + +func getRocketDAOSecurityActions(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*rocketpool.Contract, error) { + rocketDAOSecurityActionsLock.Lock() + defer rocketDAOSecurityActionsLock.Unlock() + return rp.GetContract("rocketDAOSecurityActions", opts) +} diff --git a/bindings/dao/security/proposals.go b/bindings/dao/security/proposals.go new file mode 100644 index 000000000..964a44625 --- /dev/null +++ b/bindings/dao/security/proposals.go @@ -0,0 +1,166 @@ +package security + +import ( + "fmt" + "math/big" + "sync" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/rocket-pool/smartnode/bindings/dao" + "github.com/rocket-pool/smartnode/bindings/rocketpool" +) + +// Estimate the gas of ProposeSetUint +func EstimateProposeSetUintGas(rp *rocketpool.RocketPool, message, contractName, settingPath string, value *big.Int, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketDAOSecurityProposals, err := getRocketDAOSecurityProposals(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + payload, err := rocketDAOSecurityProposals.ABI.Pack("proposalSettingUint", contractName, settingPath, value) + if err != nil { + return rocketpool.GasInfo{}, fmt.Errorf("error encoding set uint setting proposal payload: %w", err) + } + return EstimateProposalGas(rp, message, payload, opts) +} + +// Submit a proposal to update a uint trusted node DAO setting +func ProposeSetUint(rp *rocketpool.RocketPool, message, contractName, settingPath string, value *big.Int, opts *bind.TransactOpts) (uint64, common.Hash, error) { + rocketDAOSecurityProposals, err := getRocketDAOSecurityProposals(rp, nil) + if err != nil { + return 0, common.Hash{}, err + } + payload, err := rocketDAOSecurityProposals.ABI.Pack("proposalSettingUint", contractName, settingPath, value) + if err != nil { + return 0, common.Hash{}, fmt.Errorf("error encoding set uint setting proposal payload: %w", err) + } + return SubmitProposal(rp, message, payload, opts) +} + +// Estimate the gas of ProposeSetBool +func EstimateProposeSetBoolGas(rp *rocketpool.RocketPool, message, contractName, settingPath string, value bool, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketDAOSecurityProposals, err := getRocketDAOSecurityProposals(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + payload, err := rocketDAOSecurityProposals.ABI.Pack("proposalSettingBool", contractName, settingPath, value) + if err != nil { + return rocketpool.GasInfo{}, fmt.Errorf("error encoding set bool setting proposal payload: %w", err) + } + return EstimateProposalGas(rp, message, payload, opts) +} + +// Submit a proposal to update a bool trusted node DAO setting +func ProposeSetBool(rp *rocketpool.RocketPool, message, contractName, settingPath string, value bool, opts *bind.TransactOpts) (uint64, common.Hash, error) { + rocketDAOSecurityProposals, err := getRocketDAOSecurityProposals(rp, nil) + if err != nil { + return 0, common.Hash{}, err + } + payload, err := rocketDAOSecurityProposals.ABI.Pack("proposalSettingBool", contractName, settingPath, value) + if err != nil { + return 0, common.Hash{}, fmt.Errorf("error encoding set bool setting proposal payload: %w", err) + } + return SubmitProposal(rp, message, payload, opts) +} + +// Estimate the gas of a proposal submission +func EstimateProposalGas(rp *rocketpool.RocketPool, message string, payload []byte, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketDAOSecurityProposals, err := getRocketDAOSecurityProposals(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketDAOSecurityProposals.GetTransactionGasInfo(opts, "propose", message, payload) +} + +// Submit a security DAO proposal +// Returns the ID of the new proposal +func SubmitProposal(rp *rocketpool.RocketPool, message string, payload []byte, opts *bind.TransactOpts) (uint64, common.Hash, error) { + rocketDAOSecurityProposals, err := getRocketDAOSecurityProposals(rp, nil) + if err != nil { + return 0, common.Hash{}, err + } + proposalCount, err := dao.GetProposalCount(rp, nil) + if err != nil { + return 0, common.Hash{}, err + } + tx, err := rocketDAOSecurityProposals.Transact(opts, "propose", message, payload) + if err != nil { + return 0, common.Hash{}, fmt.Errorf("error submitting security DAO proposal: %w", err) + } + return proposalCount + 1, tx.Hash(), nil +} + +// Estimate the gas of VoteOnProposal +func EstimateVoteOnProposalGas(rp *rocketpool.RocketPool, proposalId uint64, support bool, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketDAOSecurityProposals, err := getRocketDAOSecurityProposals(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketDAOSecurityProposals.GetTransactionGasInfo(opts, "vote", big.NewInt(int64(proposalId)), support) +} + +// Vote on a submitted proposal +func VoteOnProposal(rp *rocketpool.RocketPool, proposalId uint64, support bool, opts *bind.TransactOpts) (common.Hash, error) { + rocketDAOSecurityProposals, err := getRocketDAOSecurityProposals(rp, nil) + if err != nil { + return common.Hash{}, err + } + tx, err := rocketDAOSecurityProposals.Transact(opts, "vote", big.NewInt(int64(proposalId)), support) + if err != nil { + return common.Hash{}, fmt.Errorf("error voting on security DAO proposal %d: %w", proposalId, err) + } + return tx.Hash(), nil +} + +// Estimate the gas of CancelProposal +func EstimateCancelProposalGas(rp *rocketpool.RocketPool, proposalId uint64, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketDAOSecurityProposals, err := getRocketDAOSecurityProposals(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketDAOSecurityProposals.GetTransactionGasInfo(opts, "cancel", big.NewInt(int64(proposalId))) +} + +// Cancel a submitted proposal +func CancelProposal(rp *rocketpool.RocketPool, proposalId uint64, opts *bind.TransactOpts) (common.Hash, error) { + rocketDAOSecurityProposals, err := getRocketDAOSecurityProposals(rp, nil) + if err != nil { + return common.Hash{}, err + } + tx, err := rocketDAOSecurityProposals.Transact(opts, "cancel", big.NewInt(int64(proposalId))) + if err != nil { + return common.Hash{}, fmt.Errorf("error cancelling security DAO proposal %d: %w", proposalId, err) + } + return tx.Hash(), nil +} + +// Estimate the gas of ExecuteProposal +func EstimateExecuteProposalGas(rp *rocketpool.RocketPool, proposalId uint64, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketDAOSecurityProposals, err := getRocketDAOSecurityProposals(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketDAOSecurityProposals.GetTransactionGasInfo(opts, "execute", big.NewInt(int64(proposalId))) +} + +// Execute a submitted proposal +func ExecuteProposal(rp *rocketpool.RocketPool, proposalId uint64, opts *bind.TransactOpts) (common.Hash, error) { + rocketDAOSecurityProposals, err := getRocketDAOSecurityProposals(rp, nil) + if err != nil { + return common.Hash{}, err + } + tx, err := rocketDAOSecurityProposals.Transact(opts, "execute", big.NewInt(int64(proposalId))) + if err != nil { + return common.Hash{}, fmt.Errorf("error executing security DAO proposal %d: %w", proposalId, err) + } + return tx.Hash(), nil +} + +// Get contracts +var rocketDAOSecurityProposalsLock sync.Mutex + +func getRocketDAOSecurityProposals(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*rocketpool.Contract, error) { + rocketDAOSecurityProposalsLock.Lock() + defer rocketDAOSecurityProposalsLock.Unlock() + return rp.GetContract("rocketDAOSecurityProposals", opts) +} diff --git a/bindings/dao/security/security.go b/bindings/dao/security/security.go new file mode 100644 index 000000000..d8f139164 --- /dev/null +++ b/bindings/dao/security/security.go @@ -0,0 +1,249 @@ +package security + +import ( + "fmt" + "math/big" + "sync" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/utils/strings" + "golang.org/x/sync/errgroup" +) + +// Settings +const ( + MemberAddressBatchSize = 50 + MemberDetailsBatchSize = 20 +) + +// Member details +type SecurityDAOMemberDetails struct { + Address common.Address `json:"address"` + Exists bool `json:"exists"` + ID string `json:"id"` + JoinedTime uint64 `json:"joinedTime"` +} + +// Get all member details +func GetMembers(rp *rocketpool.RocketPool, opts *bind.CallOpts) ([]SecurityDAOMemberDetails, error) { + // Get member addresses + memberAddresses, err := GetMemberAddresses(rp, opts) + if err != nil { + return []SecurityDAOMemberDetails{}, err + } + + // Load member details in batches + details := make([]SecurityDAOMemberDetails, len(memberAddresses)) + for bsi := 0; bsi < len(memberAddresses); bsi += MemberDetailsBatchSize { + // Get batch start & end index + msi := bsi + mei := bsi + MemberDetailsBatchSize + if mei > len(memberAddresses) { + mei = len(memberAddresses) + } + + // Load details + var wg errgroup.Group + for mi := msi; mi < mei; mi++ { + mi := mi + wg.Go(func() error { + memberAddress := memberAddresses[mi] + memberDetails, err := GetMemberDetails(rp, memberAddress, opts) + if err == nil { + details[mi] = memberDetails + } + return err + }) + } + if err := wg.Wait(); err != nil { + return []SecurityDAOMemberDetails{}, err + } + } + + // Return + return details, nil +} + +// Get all member addresses +func GetMemberAddresses(rp *rocketpool.RocketPool, opts *bind.CallOpts) ([]common.Address, error) { + // Get member count + memberCount, err := GetMemberCount(rp, opts) + if err != nil { + return []common.Address{}, err + } + + // Load member addresses in batches + addresses := make([]common.Address, memberCount) + for bsi := uint64(0); bsi < memberCount; bsi += MemberAddressBatchSize { + + // Get batch start & end index + msi := bsi + mei := bsi + MemberAddressBatchSize + if mei > memberCount { + mei = memberCount + } + + // Load addresses + var wg errgroup.Group + for mi := msi; mi < mei; mi++ { + mi := mi + wg.Go(func() error { + address, err := GetMemberAt(rp, mi, opts) + if err == nil { + addresses[mi] = address + } + return err + }) + } + if err := wg.Wait(); err != nil { + return []common.Address{}, err + } + + } + + // Return + return addresses, nil +} + +// Get a member's details +func GetMemberDetails(rp *rocketpool.RocketPool, memberAddress common.Address, opts *bind.CallOpts) (SecurityDAOMemberDetails, error) { + // Data + var wg errgroup.Group + var exists bool + var id string + var joinedTime uint64 + + // Load data + wg.Go(func() error { + var err error + exists, err = GetMemberExists(rp, memberAddress, opts) + return err + }) + wg.Go(func() error { + var err error + id, err = GetMemberID(rp, memberAddress, opts) + return err + }) + wg.Go(func() error { + var err error + joinedTime, err = GetMemberJoinedTime(rp, memberAddress, opts) + return err + }) + + // Wait for data + if err := wg.Wait(); err != nil { + return SecurityDAOMemberDetails{}, err + } + + // Return + return SecurityDAOMemberDetails{ + Address: memberAddress, + Exists: exists, + ID: id, + JoinedTime: joinedTime, + }, nil +} + +// Get the amount of member votes need for a proposal to pass (as a fraction of 1e18) +func GetMemberQuorumVotesRequired(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*big.Int, error) { + rocketDAOSecurity, err := getRocketDAOSecurity(rp, opts) + if err != nil { + return nil, err + } + value := new(*big.Int) + if err := rocketDAOSecurity.Call(opts, value, "getMemberQuorumVotesRequired"); err != nil { + return nil, fmt.Errorf("error getting security DAO quorum votes required: %w", err) + } + return *value, nil +} + +// Get the member count +func GetMemberCount(rp *rocketpool.RocketPool, opts *bind.CallOpts) (uint64, error) { + rocketDAOSecurity, err := getRocketDAOSecurity(rp, opts) + if err != nil { + return 0, err + } + memberCount := new(*big.Int) + if err := rocketDAOSecurity.Call(opts, memberCount, "getMemberCount"); err != nil { + return 0, fmt.Errorf("error getting security DAO member count: %w", err) + } + return (*memberCount).Uint64(), nil +} + +// Get a member address by index +func GetMemberAt(rp *rocketpool.RocketPool, index uint64, opts *bind.CallOpts) (common.Address, error) { + rocketDAOSecurity, err := getRocketDAOSecurity(rp, opts) + if err != nil { + return common.Address{}, err + } + memberAddress := new(common.Address) + if err := rocketDAOSecurity.Call(opts, memberAddress, "getMemberAt", big.NewInt(int64(index))); err != nil { + return common.Address{}, fmt.Errorf("error getting security DAO member %d address: %w", index, err) + } + return *memberAddress, nil +} + +// Member details +func GetMemberExists(rp *rocketpool.RocketPool, memberAddress common.Address, opts *bind.CallOpts) (bool, error) { + rocketDAOSecurity, err := getRocketDAOSecurity(rp, opts) + if err != nil { + return false, err + } + exists := new(bool) + if err := rocketDAOSecurity.Call(opts, exists, "getMemberIsValid", memberAddress); err != nil { + return false, fmt.Errorf("error getting security DAO member %s exists status: %w", memberAddress.Hex(), err) + } + return *exists, nil +} +func GetMemberID(rp *rocketpool.RocketPool, memberAddress common.Address, opts *bind.CallOpts) (string, error) { + rocketDAOSecurity, err := getRocketDAOSecurity(rp, opts) + if err != nil { + return "", err + } + id := new(string) + if err := rocketDAOSecurity.Call(opts, id, "getMemberID", memberAddress); err != nil { + return "", fmt.Errorf("error getting security DAO member %s ID: %w", memberAddress.Hex(), err) + } + return strings.Sanitize(*id), nil +} +func GetMemberJoinedTime(rp *rocketpool.RocketPool, memberAddress common.Address, opts *bind.CallOpts) (uint64, error) { + rocketDAOSecurity, err := getRocketDAOSecurity(rp, opts) + if err != nil { + return 0, err + } + joinedTime := new(*big.Int) + if err := rocketDAOSecurity.Call(opts, joinedTime, "getMemberJoinedTime", memberAddress); err != nil { + return 0, fmt.Errorf("error getting security DAO member %s joined time: %w", memberAddress.Hex(), err) + } + return (*joinedTime).Uint64(), nil +} + +// Get the time that a proposal for a member was executed at +func GetMemberInviteProposalExecutedTime(rp *rocketpool.RocketPool, memberAddress common.Address, opts *bind.CallOpts) (uint64, error) { + return GetMemberProposalExecutedTime(rp, "invited", memberAddress, opts) +} +func GetMemberLeaveProposalExecutedTime(rp *rocketpool.RocketPool, memberAddress common.Address, opts *bind.CallOpts) (uint64, error) { + return GetMemberProposalExecutedTime(rp, "leave", memberAddress, opts) +} +func GetMemberProposalExecutedTime(rp *rocketpool.RocketPool, proposalType string, memberAddress common.Address, opts *bind.CallOpts) (uint64, error) { + rocketDAOSecurity, err := getRocketDAOSecurity(rp, opts) + if err != nil { + return 0, err + } + proposalExecutedTime := new(*big.Int) + if err := rocketDAOSecurity.Call(opts, proposalExecutedTime, "getMemberProposalExecutedTime", proposalType, memberAddress); err != nil { + return 0, fmt.Errorf("error getting security DAO %s proposal executed time for member %s: %w", proposalType, memberAddress.Hex(), err) + } + return (*proposalExecutedTime).Uint64(), nil +} + +// Get contracts +var rocketDAOSecurityLock sync.Mutex + +func getRocketDAOSecurity(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*rocketpool.Contract, error) { + rocketDAOSecurityLock.Lock() + defer rocketDAOSecurityLock.Unlock() + return rp.GetContract("rocketDAOSecurity", opts) +} diff --git a/bindings/dao/trustednode/actions.go b/bindings/dao/trustednode/actions.go new file mode 100644 index 000000000..bbb2ce109 --- /dev/null +++ b/bindings/dao/trustednode/actions.go @@ -0,0 +1,147 @@ +package trustednode + +import ( + "fmt" + "math/big" + "sync" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/utils/eth" +) + +// Estimate the gas of Join +func EstimateJoinGas(rp *rocketpool.RocketPool, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketDAONodeTrustedActions, err := getRocketDAONodeTrustedActions(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketDAONodeTrustedActions.GetTransactionGasInfo(opts, "actionJoin") +} + +// Join the trusted node DAO +// Requires an executed invite proposal +func Join(rp *rocketpool.RocketPool, opts *bind.TransactOpts) (common.Hash, error) { + rocketDAONodeTrustedActions, err := getRocketDAONodeTrustedActions(rp, nil) + if err != nil { + return common.Hash{}, err + } + tx, err := rocketDAONodeTrustedActions.Transact(opts, "actionJoin") + if err != nil { + return common.Hash{}, fmt.Errorf("error joining the trusted node DAO: %w", err) + } + return tx.Hash(), nil +} + +// Estimate the gas of Leave +func EstimateLeaveGas(rp *rocketpool.RocketPool, rplBondRefundAddress common.Address, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketDAONodeTrustedActions, err := getRocketDAONodeTrustedActions(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketDAONodeTrustedActions.GetTransactionGasInfo(opts, "actionLeave", rplBondRefundAddress) +} + +// Leave the trusted node DAO +// Requires an executed leave proposal +func Leave(rp *rocketpool.RocketPool, rplBondRefundAddress common.Address, opts *bind.TransactOpts) (common.Hash, error) { + rocketDAONodeTrustedActions, err := getRocketDAONodeTrustedActions(rp, nil) + if err != nil { + return common.Hash{}, err + } + tx, err := rocketDAONodeTrustedActions.Transact(opts, "actionLeave", rplBondRefundAddress) + if err != nil { + return common.Hash{}, fmt.Errorf("error leaving the trusted node DAO: %w", err) + } + return tx.Hash(), nil +} + +// Estimate the gas of MakeChallenge +func EstimateMakeChallengeGas(rp *rocketpool.RocketPool, memberAddress common.Address, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketDAONodeTrustedActions, err := getRocketDAONodeTrustedActions(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketDAONodeTrustedActions.GetTransactionGasInfo(opts, "actionChallengeMake", memberAddress) +} + +// Make a challenge against a node +func MakeChallenge(rp *rocketpool.RocketPool, memberAddress common.Address, opts *bind.TransactOpts) (common.Hash, error) { + rocketDAONodeTrustedActions, err := getRocketDAONodeTrustedActions(rp, nil) + if err != nil { + return common.Hash{}, err + } + tx, err := rocketDAONodeTrustedActions.Transact(opts, "actionChallengeMake", memberAddress) + if err != nil { + return common.Hash{}, fmt.Errorf("error challenging trusted node DAO member %s: %w", memberAddress.Hex(), err) + } + return tx.Hash(), nil +} + +// Estimate the gas of DecideChallenge +func EstimateDecideChallengeGas(rp *rocketpool.RocketPool, memberAddress common.Address, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketDAONodeTrustedActions, err := getRocketDAONodeTrustedActions(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketDAONodeTrustedActions.GetTransactionGasInfo(opts, "actionChallengeDecide", memberAddress) +} + +// Decide a challenge against a node +func DecideChallenge(rp *rocketpool.RocketPool, memberAddress common.Address, opts *bind.TransactOpts) (common.Hash, error) { + rocketDAONodeTrustedActions, err := getRocketDAONodeTrustedActions(rp, nil) + if err != nil { + return common.Hash{}, err + } + tx, err := rocketDAONodeTrustedActions.Transact(opts, "actionChallengeDecide", memberAddress) + if err != nil { + return common.Hash{}, fmt.Errorf("error deciding the challenge against trusted node DAO member %s: %w", memberAddress.Hex(), err) + } + return tx.Hash(), nil +} + +// Returns the most recent block number that the number of trusted nodes changed since fromBlock +func GetLatestMemberCountChangedBlock(rp *rocketpool.RocketPool, fromBlock uint64, intervalSize *big.Int, opts *bind.CallOpts) (uint64, error) { + // Get contracts + rocketDaoNodeTrustedActions, err := getRocketDAONodeTrustedActions(rp, opts) + if err != nil { + return 0, err + } + // Construct a filter query for relevant logs + addressFilter := []common.Address{*rocketDaoNodeTrustedActions.Address} + topicFilter := [][]common.Hash{{rocketDaoNodeTrustedActions.ABI.Events["ActionJoined"].ID, rocketDaoNodeTrustedActions.ABI.Events["ActionLeave"].ID, rocketDaoNodeTrustedActions.ABI.Events["ActionKick"].ID, rocketDaoNodeTrustedActions.ABI.Events["ActionChallengeDecided"].ID}} + + // Get the event logs + logs, err := eth.GetLogs(rp, addressFilter, topicFilter, intervalSize, big.NewInt(int64(fromBlock)), nil, nil) + if err != nil { + return 0, err + } + + for i := range logs { + log := logs[len(logs)-i-1] + if log.Topics[0] == rocketDaoNodeTrustedActions.ABI.Events["ActionChallengeDecided"].ID { + values := make(map[string]interface{}) + // Decode the event + if rocketDaoNodeTrustedActions.ABI.Events["ActionChallengeDecided"].Inputs.UnpackIntoMap(values, log.Data) != nil { + return 0, err + } + if values["success"].(bool) { + return log.BlockNumber, nil + } + } else { + return log.BlockNumber, nil + } + } + return fromBlock, nil +} + +// Get contracts +var rocketDAONodeTrustedActionsLock sync.Mutex + +func getRocketDAONodeTrustedActions(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*rocketpool.Contract, error) { + rocketDAONodeTrustedActionsLock.Lock() + defer rocketDAONodeTrustedActionsLock.Unlock() + return rp.GetContract("rocketDAONodeTrustedActions", opts) +} diff --git a/bindings/dao/trustednode/dao.go b/bindings/dao/trustednode/dao.go new file mode 100644 index 000000000..8ee92ed4f --- /dev/null +++ b/bindings/dao/trustednode/dao.go @@ -0,0 +1,363 @@ +package trustednode + +import ( + "fmt" + "math/big" + "sync" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "golang.org/x/sync/errgroup" + + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/utils/strings" +) + +// Settings +const ( + MemberAddressBatchSize = 50 + MemberDetailsBatchSize = 20 +) + +// Member details +type MemberDetails struct { + Address common.Address `json:"address"` + Exists bool `json:"exists"` + ID string `json:"id"` + Url string `json:"url"` + JoinedTime uint64 `json:"joinedTime"` + LastProposalTime uint64 `json:"lastProposalTime"` + RPLBondAmount *big.Int `json:"rplBondAmount"` + UnbondedValidatorCount uint64 `json:"unbondedValidatorCount"` +} + +// Get all member details +func GetMembers(rp *rocketpool.RocketPool, opts *bind.CallOpts) ([]MemberDetails, error) { + + // Get member addresses + memberAddresses, err := GetMemberAddresses(rp, opts) + if err != nil { + return []MemberDetails{}, err + } + + // Load member details in batches + details := make([]MemberDetails, len(memberAddresses)) + for bsi := 0; bsi < len(memberAddresses); bsi += MemberDetailsBatchSize { + + // Get batch start & end index + msi := bsi + mei := bsi + MemberDetailsBatchSize + if mei > len(memberAddresses) { + mei = len(memberAddresses) + } + + // Load details + var wg errgroup.Group + for mi := msi; mi < mei; mi++ { + mi := mi + wg.Go(func() error { + memberAddress := memberAddresses[mi] + memberDetails, err := GetMemberDetails(rp, memberAddress, opts) + if err == nil { + details[mi] = memberDetails + } + return err + }) + } + if err := wg.Wait(); err != nil { + return []MemberDetails{}, err + } + + } + + // Return + return details, nil + +} + +// Get all member addresses +func GetMemberAddresses(rp *rocketpool.RocketPool, opts *bind.CallOpts) ([]common.Address, error) { + + // Get member count + memberCount, err := GetMemberCount(rp, opts) + if err != nil { + return []common.Address{}, err + } + + // Load member addresses in batches + addresses := make([]common.Address, memberCount) + for bsi := uint64(0); bsi < memberCount; bsi += MemberAddressBatchSize { + + // Get batch start & end index + msi := bsi + mei := bsi + MemberAddressBatchSize + if mei > memberCount { + mei = memberCount + } + + // Load addresses + var wg errgroup.Group + for mi := msi; mi < mei; mi++ { + mi := mi + wg.Go(func() error { + address, err := GetMemberAt(rp, mi, opts) + if err == nil { + addresses[mi] = address + } + return err + }) + } + if err := wg.Wait(); err != nil { + return []common.Address{}, err + } + + } + + // Return + return addresses, nil + +} + +// Get a member's details +func GetMemberDetails(rp *rocketpool.RocketPool, memberAddress common.Address, opts *bind.CallOpts) (MemberDetails, error) { + + // Data + var wg errgroup.Group + var exists bool + var id string + var url string + var joinedTime uint64 + var lastProposalTime uint64 + var rplBondAmount *big.Int + var unbondedValidatorCount uint64 + + // Load data + wg.Go(func() error { + var err error + exists, err = GetMemberExists(rp, memberAddress, opts) + return err + }) + wg.Go(func() error { + var err error + id, err = GetMemberID(rp, memberAddress, opts) + return err + }) + wg.Go(func() error { + var err error + url, err = GetMemberUrl(rp, memberAddress, opts) + return err + }) + wg.Go(func() error { + var err error + joinedTime, err = GetMemberJoinedTime(rp, memberAddress, opts) + return err + }) + wg.Go(func() error { + var err error + lastProposalTime, err = GetMemberLastProposalTime(rp, memberAddress, opts) + return err + }) + wg.Go(func() error { + var err error + rplBondAmount, err = GetMemberRPLBondAmount(rp, memberAddress, opts) + return err + }) + wg.Go(func() error { + var err error + unbondedValidatorCount, err = GetMemberUnbondedValidatorCount(rp, memberAddress, opts) + return err + }) + + // Wait for data + if err := wg.Wait(); err != nil { + return MemberDetails{}, err + } + + // Return + return MemberDetails{ + Address: memberAddress, + Exists: exists, + ID: id, + Url: url, + JoinedTime: joinedTime, + LastProposalTime: lastProposalTime, + RPLBondAmount: rplBondAmount, + UnbondedValidatorCount: unbondedValidatorCount, + }, nil + +} + +// Get the minimum member count +func GetMinimumMemberCount(rp *rocketpool.RocketPool, opts *bind.CallOpts) (uint64, error) { + rocketDAONodeTrusted, err := getRocketDAONodeTrusted(rp, opts) + if err != nil { + return 0, err + } + minMemberCount := new(*big.Int) + if err := rocketDAONodeTrusted.Call(opts, minMemberCount, "getMemberMinRequired"); err != nil { + return 0, fmt.Errorf("error getting trusted node DAO minimum member count: %w", err) + } + return (*minMemberCount).Uint64(), nil +} + +// Get the member count +func GetMemberCount(rp *rocketpool.RocketPool, opts *bind.CallOpts) (uint64, error) { + rocketDAONodeTrusted, err := getRocketDAONodeTrusted(rp, opts) + if err != nil { + return 0, err + } + memberCount := new(*big.Int) + if err := rocketDAONodeTrusted.Call(opts, memberCount, "getMemberCount"); err != nil { + return 0, fmt.Errorf("error getting trusted node DAO member count: %w", err) + } + return (*memberCount).Uint64(), nil +} + +// Get a member address by index +func GetMemberAt(rp *rocketpool.RocketPool, index uint64, opts *bind.CallOpts) (common.Address, error) { + rocketDAONodeTrusted, err := getRocketDAONodeTrusted(rp, opts) + if err != nil { + return common.Address{}, err + } + memberAddress := new(common.Address) + if err := rocketDAONodeTrusted.Call(opts, memberAddress, "getMemberAt", big.NewInt(int64(index))); err != nil { + return common.Address{}, fmt.Errorf("error getting trusted node DAO member %d address: %w", index, err) + } + return *memberAddress, nil +} + +// Member details +func GetMemberExists(rp *rocketpool.RocketPool, memberAddress common.Address, opts *bind.CallOpts) (bool, error) { + rocketDAONodeTrusted, err := getRocketDAONodeTrusted(rp, opts) + if err != nil { + return false, err + } + exists := new(bool) + if err := rocketDAONodeTrusted.Call(opts, exists, "getMemberIsValid", memberAddress); err != nil { + return false, fmt.Errorf("error getting trusted node DAO member %s exists status: %w", memberAddress.Hex(), err) + } + return *exists, nil +} +func GetMemberID(rp *rocketpool.RocketPool, memberAddress common.Address, opts *bind.CallOpts) (string, error) { + rocketDAONodeTrusted, err := getRocketDAONodeTrusted(rp, opts) + if err != nil { + return "", err + } + id := new(string) + if err := rocketDAONodeTrusted.Call(opts, id, "getMemberID", memberAddress); err != nil { + return "", fmt.Errorf("error getting trusted node DAO member %s ID: %w", memberAddress.Hex(), err) + } + return strings.Sanitize(*id), nil +} +func GetMemberUrl(rp *rocketpool.RocketPool, memberAddress common.Address, opts *bind.CallOpts) (string, error) { + rocketDAONodeTrusted, err := getRocketDAONodeTrusted(rp, opts) + if err != nil { + return "", err + } + url := new(string) + if err := rocketDAONodeTrusted.Call(opts, url, "getMemberUrl", memberAddress); err != nil { + return "", fmt.Errorf("error getting trusted node DAO member %s URL: %w", memberAddress.Hex(), err) + } + return strings.Sanitize(*url), nil +} +func GetMemberJoinedTime(rp *rocketpool.RocketPool, memberAddress common.Address, opts *bind.CallOpts) (uint64, error) { + rocketDAONodeTrusted, err := getRocketDAONodeTrusted(rp, opts) + if err != nil { + return 0, err + } + joinedTime := new(*big.Int) + if err := rocketDAONodeTrusted.Call(opts, joinedTime, "getMemberJoinedTime", memberAddress); err != nil { + return 0, fmt.Errorf("error getting trusted node DAO member %s joined time: %w", memberAddress.Hex(), err) + } + return (*joinedTime).Uint64(), nil +} +func GetMemberLastProposalTime(rp *rocketpool.RocketPool, memberAddress common.Address, opts *bind.CallOpts) (uint64, error) { + rocketDAONodeTrusted, err := getRocketDAONodeTrusted(rp, opts) + if err != nil { + return 0, err + } + lastProposalTime := new(*big.Int) + if err := rocketDAONodeTrusted.Call(opts, lastProposalTime, "getMemberLastProposalTime", memberAddress); err != nil { + return 0, fmt.Errorf("error getting trusted node DAO member %s last proposal time: %w", memberAddress.Hex(), err) + } + return (*lastProposalTime).Uint64(), nil +} +func GetMemberRPLBondAmount(rp *rocketpool.RocketPool, memberAddress common.Address, opts *bind.CallOpts) (*big.Int, error) { + rocketDAONodeTrusted, err := getRocketDAONodeTrusted(rp, opts) + if err != nil { + return nil, err + } + rplBondAmount := new(*big.Int) + if err := rocketDAONodeTrusted.Call(opts, rplBondAmount, "getMemberRPLBondAmount", memberAddress); err != nil { + return nil, fmt.Errorf("error getting trusted node DAO member %s RPL bond amount: %w", memberAddress.Hex(), err) + } + return *rplBondAmount, nil +} +func GetMemberUnbondedValidatorCount(rp *rocketpool.RocketPool, memberAddress common.Address, opts *bind.CallOpts) (uint64, error) { + rocketDAONodeTrusted, err := getRocketDAONodeTrusted(rp, opts) + if err != nil { + return 0, err + } + unbondedValidatorCount := new(*big.Int) + if err := rocketDAONodeTrusted.Call(opts, unbondedValidatorCount, "getMemberUnbondedValidatorCount", memberAddress); err != nil { + return 0, fmt.Errorf("error getting trusted node DAO member %s unbonded validator count: %w", memberAddress.Hex(), err) + } + return (*unbondedValidatorCount).Uint64(), nil +} + +// Get the time that a proposal for a member was executed at +func GetMemberInviteProposalExecutedTime(rp *rocketpool.RocketPool, memberAddress common.Address, opts *bind.CallOpts) (uint64, error) { + return GetMemberProposalExecutedTime(rp, "invited", memberAddress, opts) +} +func GetMemberLeaveProposalExecutedTime(rp *rocketpool.RocketPool, memberAddress common.Address, opts *bind.CallOpts) (uint64, error) { + return GetMemberProposalExecutedTime(rp, "leave", memberAddress, opts) +} +func GetMemberReplaceProposalExecutedTime(rp *rocketpool.RocketPool, memberAddress common.Address, opts *bind.CallOpts) (uint64, error) { + return GetMemberProposalExecutedTime(rp, "replace", memberAddress, opts) +} +func GetMemberProposalExecutedTime(rp *rocketpool.RocketPool, proposalType string, memberAddress common.Address, opts *bind.CallOpts) (uint64, error) { + rocketDAONodeTrusted, err := getRocketDAONodeTrusted(rp, opts) + if err != nil { + return 0, err + } + proposalExecutedTime := new(*big.Int) + if err := rocketDAONodeTrusted.Call(opts, proposalExecutedTime, "getMemberProposalExecutedTime", proposalType, memberAddress); err != nil { + return 0, fmt.Errorf("error getting trusted node DAO %s proposal executed time for member %s: %w", proposalType, memberAddress.Hex(), err) + } + return (*proposalExecutedTime).Uint64(), nil +} + +// Get a member's replacement address if being replaced +func GetMemberReplacementAddress(rp *rocketpool.RocketPool, memberAddress common.Address, opts *bind.CallOpts) (common.Address, error) { + rocketDAONodeTrusted, err := getRocketDAONodeTrusted(rp, opts) + if err != nil { + return common.Address{}, err + } + replacementAddress := new(common.Address) + if err := rocketDAONodeTrusted.Call(opts, replacementAddress, "getMemberReplacedAddress", "new", memberAddress); err != nil { + return common.Address{}, fmt.Errorf("error getting trusted node DAO member %s replacement address: %w", memberAddress.Hex(), err) + } + return *replacementAddress, nil +} + +// Get whether a member has an active challenge against them +func GetMemberIsChallenged(rp *rocketpool.RocketPool, memberAddress common.Address, opts *bind.CallOpts) (bool, error) { + rocketDAONodeTrusted, err := getRocketDAONodeTrusted(rp, opts) + if err != nil { + return false, err + } + isChallenged := new(bool) + if err := rocketDAONodeTrusted.Call(opts, isChallenged, "getMemberIsChallenged", memberAddress); err != nil { + return false, fmt.Errorf("error getting trusted node DAO member %s is challenged status: %w", memberAddress.Hex(), err) + } + return *isChallenged, nil +} + +// Get contracts +var rocketDAONodeTrustedLock sync.Mutex + +func getRocketDAONodeTrusted(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*rocketpool.Contract, error) { + rocketDAONodeTrustedLock.Lock() + defer rocketDAONodeTrustedLock.Unlock() + return rp.GetContract("rocketDAONodeTrusted", opts) +} diff --git a/bindings/dao/trustednode/proposals.go b/bindings/dao/trustednode/proposals.go new file mode 100644 index 000000000..899dd3421 --- /dev/null +++ b/bindings/dao/trustednode/proposals.go @@ -0,0 +1,310 @@ +package trustednode + +import ( + "fmt" + "math/big" + "sync" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + + "github.com/rocket-pool/smartnode/bindings/dao" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/utils/strings" +) + +// Estimate the gas of ProposeInviteMember +func EstimateProposeInviteMemberGas(rp *rocketpool.RocketPool, message string, newMemberAddress common.Address, newMemberId, newMemberUrl string, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketDAONodeTrustedProposals, err := getRocketDAONodeTrustedProposals(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + newMemberUrl = strings.Sanitize(newMemberUrl) + payload, err := rocketDAONodeTrustedProposals.ABI.Pack("proposalInvite", newMemberId, newMemberUrl, newMemberAddress) + if err != nil { + return rocketpool.GasInfo{}, fmt.Errorf("error encoding invite member proposal payload: %w", err) + } + return EstimateProposalGas(rp, message, payload, opts) +} + +// Submit a proposal to invite a new member to the trusted node DAO +func ProposeInviteMember(rp *rocketpool.RocketPool, message string, newMemberAddress common.Address, newMemberId, newMemberUrl string, opts *bind.TransactOpts) (uint64, common.Hash, error) { + rocketDAONodeTrustedProposals, err := getRocketDAONodeTrustedProposals(rp, nil) + if err != nil { + return 0, common.Hash{}, err + } + newMemberUrl = strings.Sanitize(newMemberUrl) + payload, err := rocketDAONodeTrustedProposals.ABI.Pack("proposalInvite", newMemberId, newMemberUrl, newMemberAddress) + if err != nil { + return 0, common.Hash{}, fmt.Errorf("error encoding invite member proposal payload: %w", err) + } + return SubmitProposal(rp, message, payload, opts) +} + +// Estimate the gas of ProposeMemberLeave +func EstimateProposeMemberLeaveGas(rp *rocketpool.RocketPool, message string, memberAddress common.Address, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketDAONodeTrustedProposals, err := getRocketDAONodeTrustedProposals(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + payload, err := rocketDAONodeTrustedProposals.ABI.Pack("proposalLeave", memberAddress) + if err != nil { + return rocketpool.GasInfo{}, fmt.Errorf("error encoding member leave proposal payload: %w", err) + } + return EstimateProposalGas(rp, message, payload, opts) +} + +// Submit a proposal for a member to leave the trusted node DAO +func ProposeMemberLeave(rp *rocketpool.RocketPool, message string, memberAddress common.Address, opts *bind.TransactOpts) (uint64, common.Hash, error) { + rocketDAONodeTrustedProposals, err := getRocketDAONodeTrustedProposals(rp, nil) + if err != nil { + return 0, common.Hash{}, err + } + payload, err := rocketDAONodeTrustedProposals.ABI.Pack("proposalLeave", memberAddress) + if err != nil { + return 0, common.Hash{}, fmt.Errorf("error encoding member leave proposal payload: %w", err) + } + return SubmitProposal(rp, message, payload, opts) +} + +// Estimate the gas of ProposeReplaceMember +func EstimateProposeReplaceMemberGas(rp *rocketpool.RocketPool, message string, memberAddress, newMemberAddress common.Address, newMemberId, newMemberUrl string, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketDAONodeTrustedProposals, err := getRocketDAONodeTrustedProposals(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + newMemberUrl = strings.Sanitize(newMemberUrl) + payload, err := rocketDAONodeTrustedProposals.ABI.Pack("proposalReplace", memberAddress, newMemberId, newMemberUrl, newMemberAddress) + if err != nil { + return rocketpool.GasInfo{}, fmt.Errorf("error encoding replace member proposal payload: %w", err) + } + return EstimateProposalGas(rp, message, payload, opts) +} + +// Submit a proposal to replace a member in the trusted node DAO +func ProposeReplaceMember(rp *rocketpool.RocketPool, message string, memberAddress, newMemberAddress common.Address, newMemberId, newMemberUrl string, opts *bind.TransactOpts) (uint64, common.Hash, error) { + rocketDAONodeTrustedProposals, err := getRocketDAONodeTrustedProposals(rp, nil) + if err != nil { + return 0, common.Hash{}, err + } + newMemberUrl = strings.Sanitize(newMemberUrl) + payload, err := rocketDAONodeTrustedProposals.ABI.Pack("proposalReplace", memberAddress, newMemberId, newMemberUrl, newMemberAddress) + if err != nil { + return 0, common.Hash{}, fmt.Errorf("error encoding replace member proposal payload: %w", err) + } + return SubmitProposal(rp, message, payload, opts) +} + +// Estimate the gas of ProposeKickMember +func EstimateProposeKickMemberGas(rp *rocketpool.RocketPool, message string, memberAddress common.Address, rplFineAmount *big.Int, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketDAONodeTrustedProposals, err := getRocketDAONodeTrustedProposals(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + payload, err := rocketDAONodeTrustedProposals.ABI.Pack("proposalKick", memberAddress, rplFineAmount) + if err != nil { + return rocketpool.GasInfo{}, fmt.Errorf("error encoding kick member proposal payload: %w", err) + } + return EstimateProposalGas(rp, message, payload, opts) +} + +// Submit a proposal to kick a member from the trusted node DAO +func ProposeKickMember(rp *rocketpool.RocketPool, message string, memberAddress common.Address, rplFineAmount *big.Int, opts *bind.TransactOpts) (uint64, common.Hash, error) { + rocketDAONodeTrustedProposals, err := getRocketDAONodeTrustedProposals(rp, nil) + if err != nil { + return 0, common.Hash{}, err + } + payload, err := rocketDAONodeTrustedProposals.ABI.Pack("proposalKick", memberAddress, rplFineAmount) + if err != nil { + return 0, common.Hash{}, fmt.Errorf("error encoding kick member proposal payload: %w", err) + } + return SubmitProposal(rp, message, payload, opts) +} + +// Estimate the gas of ProposeSetBool +func EstimateProposeSetBoolGas(rp *rocketpool.RocketPool, message, contractName, settingPath string, value bool, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketDAONodeTrustedProposals, err := getRocketDAONodeTrustedProposals(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + payload, err := rocketDAONodeTrustedProposals.ABI.Pack("proposalSettingBool", contractName, settingPath, value) + if err != nil { + return rocketpool.GasInfo{}, fmt.Errorf("error encoding set bool setting proposal payload: %w", err) + } + return EstimateProposalGas(rp, message, payload, opts) +} + +// Submit a proposal to update a bool trusted node DAO setting +func ProposeSetBool(rp *rocketpool.RocketPool, message, contractName, settingPath string, value bool, opts *bind.TransactOpts) (uint64, common.Hash, error) { + rocketDAONodeTrustedProposals, err := getRocketDAONodeTrustedProposals(rp, nil) + if err != nil { + return 0, common.Hash{}, err + } + payload, err := rocketDAONodeTrustedProposals.ABI.Pack("proposalSettingBool", contractName, settingPath, value) + if err != nil { + return 0, common.Hash{}, fmt.Errorf("error encoding set bool setting proposal payload: %w", err) + } + return SubmitProposal(rp, message, payload, opts) +} + +// Estimate the gas of ProposeSetUint +func EstimateProposeSetUintGas(rp *rocketpool.RocketPool, message, contractName, settingPath string, value *big.Int, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketDAONodeTrustedProposals, err := getRocketDAONodeTrustedProposals(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + payload, err := rocketDAONodeTrustedProposals.ABI.Pack("proposalSettingUint", contractName, settingPath, value) + if err != nil { + return rocketpool.GasInfo{}, fmt.Errorf("error encoding set uint setting proposal payload: %w", err) + } + return EstimateProposalGas(rp, message, payload, opts) +} + +// Submit a proposal to update a uint trusted node DAO setting +func ProposeSetUint(rp *rocketpool.RocketPool, message, contractName, settingPath string, value *big.Int, opts *bind.TransactOpts) (uint64, common.Hash, error) { + rocketDAONodeTrustedProposals, err := getRocketDAONodeTrustedProposals(rp, nil) + if err != nil { + return 0, common.Hash{}, err + } + payload, err := rocketDAONodeTrustedProposals.ABI.Pack("proposalSettingUint", contractName, settingPath, value) + if err != nil { + return 0, common.Hash{}, fmt.Errorf("error encoding set uint setting proposal payload: %w", err) + } + return SubmitProposal(rp, message, payload, opts) +} + +// Estimate the gas of ProposeUpgradeContract +func EstimateProposeUpgradeContractGas(rp *rocketpool.RocketPool, message, upgradeType, contractName, contractAbi string, contractAddress common.Address, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + compressedAbi, err := rocketpool.EncodeAbiStr(contractAbi) + if err != nil { + return rocketpool.GasInfo{}, err + } + rocketDAONodeTrustedProposals, err := getRocketDAONodeTrustedProposals(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + payload, err := rocketDAONodeTrustedProposals.ABI.Pack("proposalUpgrade", upgradeType, contractName, compressedAbi, contractAddress) + if err != nil { + return rocketpool.GasInfo{}, fmt.Errorf("error encoding upgrade contract proposal payload: %w", err) + } + return EstimateProposalGas(rp, message, payload, opts) +} + +// Submit a proposal to upgrade a contract +func ProposeUpgradeContract(rp *rocketpool.RocketPool, message, upgradeType, contractName, contractAbi string, contractAddress common.Address, opts *bind.TransactOpts) (uint64, common.Hash, error) { + compressedAbi, err := rocketpool.EncodeAbiStr(contractAbi) + if err != nil { + return 0, common.Hash{}, err + } + rocketDAONodeTrustedProposals, err := getRocketDAONodeTrustedProposals(rp, nil) + if err != nil { + return 0, common.Hash{}, err + } + payload, err := rocketDAONodeTrustedProposals.ABI.Pack("proposalUpgrade", upgradeType, contractName, compressedAbi, contractAddress) + if err != nil { + return 0, common.Hash{}, fmt.Errorf("error encoding upgrade contract proposal payload: %w", err) + } + return SubmitProposal(rp, message, payload, opts) +} + +// Estimate the gas of a proposal submission +func EstimateProposalGas(rp *rocketpool.RocketPool, message string, payload []byte, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketDAONodeTrustedProposals, err := getRocketDAONodeTrustedProposals(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketDAONodeTrustedProposals.GetTransactionGasInfo(opts, "propose", message, payload) +} + +// Submit a trusted node DAO proposal +// Returns the ID of the new proposal +func SubmitProposal(rp *rocketpool.RocketPool, message string, payload []byte, opts *bind.TransactOpts) (uint64, common.Hash, error) { + rocketDAONodeTrustedProposals, err := getRocketDAONodeTrustedProposals(rp, nil) + if err != nil { + return 0, common.Hash{}, err + } + proposalCount, err := dao.GetProposalCount(rp, nil) + if err != nil { + return 0, common.Hash{}, err + } + tx, err := rocketDAONodeTrustedProposals.Transact(opts, "propose", message, payload) + if err != nil { + return 0, common.Hash{}, fmt.Errorf("error submitting trusted node DAO proposal: %w", err) + } + return proposalCount + 1, tx.Hash(), nil +} + +// Estimate the gas of CancelProposal +func EstimateCancelProposalGas(rp *rocketpool.RocketPool, proposalId uint64, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketDAONodeTrustedProposals, err := getRocketDAONodeTrustedProposals(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketDAONodeTrustedProposals.GetTransactionGasInfo(opts, "cancel", big.NewInt(int64(proposalId))) +} + +// Cancel a submitted proposal +func CancelProposal(rp *rocketpool.RocketPool, proposalId uint64, opts *bind.TransactOpts) (common.Hash, error) { + rocketDAONodeTrustedProposals, err := getRocketDAONodeTrustedProposals(rp, nil) + if err != nil { + return common.Hash{}, err + } + tx, err := rocketDAONodeTrustedProposals.Transact(opts, "cancel", big.NewInt(int64(proposalId))) + if err != nil { + return common.Hash{}, fmt.Errorf("error cancelling trusted node DAO proposal %d: %w", proposalId, err) + } + return tx.Hash(), nil +} + +// Estimate the gas of VoteOnProposal +func EstimateVoteOnProposalGas(rp *rocketpool.RocketPool, proposalId uint64, support bool, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketDAONodeTrustedProposals, err := getRocketDAONodeTrustedProposals(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketDAONodeTrustedProposals.GetTransactionGasInfo(opts, "vote", big.NewInt(int64(proposalId)), support) +} + +// Vote on a submitted proposal +func VoteOnProposal(rp *rocketpool.RocketPool, proposalId uint64, support bool, opts *bind.TransactOpts) (common.Hash, error) { + rocketDAONodeTrustedProposals, err := getRocketDAONodeTrustedProposals(rp, nil) + if err != nil { + return common.Hash{}, err + } + tx, err := rocketDAONodeTrustedProposals.Transact(opts, "vote", big.NewInt(int64(proposalId)), support) + if err != nil { + return common.Hash{}, fmt.Errorf("error voting on trusted node DAO proposal %d: %w", proposalId, err) + } + return tx.Hash(), nil +} + +// Estimate the gas of ExecuteProposal +func EstimateExecuteProposalGas(rp *rocketpool.RocketPool, proposalId uint64, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketDAONodeTrustedProposals, err := getRocketDAONodeTrustedProposals(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketDAONodeTrustedProposals.GetTransactionGasInfo(opts, "execute", big.NewInt(int64(proposalId))) +} + +// Execute a submitted proposal +func ExecuteProposal(rp *rocketpool.RocketPool, proposalId uint64, opts *bind.TransactOpts) (common.Hash, error) { + rocketDAONodeTrustedProposals, err := getRocketDAONodeTrustedProposals(rp, nil) + if err != nil { + return common.Hash{}, err + } + tx, err := rocketDAONodeTrustedProposals.Transact(opts, "execute", big.NewInt(int64(proposalId))) + if err != nil { + return common.Hash{}, fmt.Errorf("error executing trusted node DAO proposal %d: %w", proposalId, err) + } + return tx.Hash(), nil +} + +// Get contracts +var rocketDAONodeTrustedProposalsLock sync.Mutex + +func getRocketDAONodeTrustedProposals(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*rocketpool.Contract, error) { + rocketDAONodeTrustedProposalsLock.Lock() + defer rocketDAONodeTrustedProposalsLock.Unlock() + return rp.GetContract("rocketDAONodeTrustedProposals", opts) +} diff --git a/bindings/deposit/deposit-pool.go b/bindings/deposit/deposit-pool.go new file mode 100644 index 000000000..d44dbfcfa --- /dev/null +++ b/bindings/deposit/deposit-pool.go @@ -0,0 +1,89 @@ +package deposit + +import ( + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/rocket-pool/smartnode/bindings/rocketpool" +) + +// Estimate the gas required to exit the validator queue +func EstimateExitQueueGas(rp *rocketpool.RocketPool, validatorIndex uint64, expressQueue bool, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketDepositPool, err := getRocketDepositPool(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + validatorIndexBig := big.NewInt(int64(validatorIndex)) + return rocketDepositPool.GetTransactionGasInfo(opts, "exitQueue", validatorIndexBig, expressQueue) +} + +// Exit the validator queue +func ExitQueue(rp *rocketpool.RocketPool, validatorIndex uint64, expressQueue bool, opts *bind.TransactOpts) (common.Hash, error) { + rocketDepositPool, err := getRocketDepositPool(rp, nil) + if err != nil { + return common.Hash{}, err + } + validatorIndexBig := big.NewInt(int64(validatorIndex)) + tx, err := rocketDepositPool.Transact(opts, "exitQueue", validatorIndexBig, expressQueue) + if err != nil { + return common.Hash{}, fmt.Errorf("error exiting validator queue: %w", err) + } + return tx.Hash(), nil +} + +// Struct to hold queue top (address of the validator at the top of the queue and a boolean indicating if the assignment is possible) +type QueueTop struct { + Receiver common.Address `abi:"receiver"` + AssignmentPossible bool `abi:"assignmentPossible"` + HeadMovedBlock *big.Int `abi:"headMovedBlock"` +} + +func GetQueueTop(rp *rocketpool.RocketPool, opts *bind.CallOpts) (QueueTop, error) { + rocketDepositPool, err := getRocketDepositPool(rp, opts) + if err != nil { + return QueueTop{}, err + } + queueTop := new(QueueTop) + if err := rocketDepositPool.Call(opts, queueTop, "getQueueTop"); err != nil { + return QueueTop{}, fmt.Errorf("error getting queue top: %w", err) + } + return *queueTop, nil +} + +func GetTotalQueueLength(rp *rocketpool.RocketPool, opts *bind.CallOpts) (uint32, error) { + rocketDepositPool, err := getRocketDepositPool(rp, opts) + if err != nil { + return 0, err + } + totalLength := new(*big.Int) + if err := rocketDepositPool.Call(opts, totalLength, "getTotalQueueLength"); err != nil { + return 0, fmt.Errorf("error getting total queue length: %w", err) + } + return uint32((*totalLength).Uint64()), nil +} + +func GetExpressQueueLength(rp *rocketpool.RocketPool, opts *bind.CallOpts) (uint32, error) { + rocketDepositPool, err := getRocketDepositPool(rp, opts) + if err != nil { + return 0, err + } + length := new(*big.Int) + if err := rocketDepositPool.Call(opts, length, "getExpressQueueLength"); err != nil { + return 0, fmt.Errorf("error getting express queue length: %w", err) + } + return uint32((*length).Uint64()), nil +} + +func GetStandardQueueLength(rp *rocketpool.RocketPool, opts *bind.CallOpts) (uint32, error) { + rocketDepositPool, err := getRocketDepositPool(rp, opts) + if err != nil { + return 0, err + } + length := new(*big.Int) + if err := rocketDepositPool.Call(opts, length, "getStandardQueueLength"); err != nil { + return 0, fmt.Errorf("error getting standard queue length: %w", err) + } + return uint32((*length).Uint64()), nil +} diff --git a/bindings/deposit/deposit.go b/bindings/deposit/deposit.go new file mode 100644 index 000000000..34f4f9893 --- /dev/null +++ b/bindings/deposit/deposit.go @@ -0,0 +1,112 @@ +package deposit + +import ( + "fmt" + "math/big" + "sync" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + + "github.com/rocket-pool/smartnode/bindings/rocketpool" +) + +// Get the deposit pool balance +func GetBalance(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*big.Int, error) { + rocketDepositPool, err := getRocketDepositPool(rp, opts) + if err != nil { + return nil, err + } + balance := new(*big.Int) + if err := rocketDepositPool.Call(opts, balance, "getBalance"); err != nil { + return nil, fmt.Errorf("error getting deposit pool balance: %w", err) + } + return *balance, nil +} + +// Get the deposit pool balance +func GetUserBalance(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*big.Int, error) { + rocketDepositPool, err := getRocketDepositPool(rp, opts) + if err != nil { + return nil, err + } + balance := new(*big.Int) + if err := rocketDepositPool.Call(opts, balance, "getUserBalance"); err != nil { + return nil, fmt.Errorf("error getting deposit pool user balance: %w", err) + } + return *balance, nil +} + +// Get the excess deposit pool balance +func GetExcessBalance(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*big.Int, error) { + rocketDepositPool, err := getRocketDepositPool(rp, opts) + if err != nil { + return nil, err + } + excessBalance := new(*big.Int) + if err := rocketDepositPool.Call(opts, excessBalance, "getExcessBalance"); err != nil { + return nil, fmt.Errorf("error getting deposit pool excess balance: %w", err) + } + return *excessBalance, nil +} + +// Estimate the gas of Deposit +func EstimateDepositGas(rp *rocketpool.RocketPool, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketDepositPool, err := getRocketDepositPool(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketDepositPool.GetTransactionGasInfo(opts, "deposit") +} + +// Make a deposit +func Deposit(rp *rocketpool.RocketPool, opts *bind.TransactOpts) (common.Hash, error) { + rocketDepositPool, err := getRocketDepositPool(rp, nil) + if err != nil { + return common.Hash{}, err + } + tx, err := rocketDepositPool.Transact(opts, "deposit") + if err != nil { + return common.Hash{}, fmt.Errorf("error depositing: %w", err) + } + return tx.Hash(), nil +} + +// Estimate the gas of AssignDeposits +func EstimateAssignDepositsGas(rp *rocketpool.RocketPool, max *big.Int, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketDepositPool, err := getRocketDepositPool(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketDepositPool.GetTransactionGasInfo(opts, "assignDeposits", max) +} + +// Assign deposits +func AssignDeposits(rp *rocketpool.RocketPool, max *big.Int, opts *bind.TransactOpts) (common.Hash, error) { + rocketDepositPool, err := getRocketDepositPool(rp, nil) + if err != nil { + return common.Hash{}, err + } + tx, err := rocketDepositPool.Transact(opts, "assignDeposits", max) + if err != nil { + return common.Hash{}, fmt.Errorf("error assigning deposits: %w", err) + } + return tx.Hash(), nil +} + +// Get contracts +var rocketDepositPoolLock sync.Mutex + +func getRocketDepositPool(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*rocketpool.Contract, error) { + rocketDepositPoolLock.Lock() + defer rocketDepositPoolLock.Unlock() + return rp.GetContract("rocketDepositPool", opts) +} + +func GetRocketDepositPoolVersion(rp *rocketpool.RocketPool, opts *bind.CallOpts) (uint8, error) { + rocketDepositPool, err := getRocketDepositPool(rp, opts) + if err != nil { + return 0, err + } + return rocketpool.GetContractVersion(rp, *rocketDepositPool.Address, opts) +} diff --git a/bindings/docs/docgen.go b/bindings/docs/docgen.go new file mode 100644 index 000000000..06f77312e --- /dev/null +++ b/bindings/docs/docgen.go @@ -0,0 +1,93 @@ +package main + +import ( + "fmt" + "go/build" + "os" + + "github.com/princjef/gomarkdoc" + "github.com/princjef/gomarkdoc/lang" + "github.com/princjef/gomarkdoc/logger" +) + +const repo string = "https://github.com/rocket-pool/rocketpool-go" +const branch string = "release" + +func main() { + + // gomarkdoc's logger + log := logger.New(logger.DebugLevel) + + // Make a new doc renderer + renderer, err := gomarkdoc.NewRenderer() + if err != nil { + fmt.Printf("Error creating renderer: %s\n", err.Error()) + os.Exit(1) + } + + // Get the working directory + wd, err := os.Getwd() + if err != nil { + fmt.Printf("Error getting working directory: %s\n", err.Error()) + os.Exit(1) + } + + // These are all of the packages to generate the source for + packages := map[string]string{ + "auction": "%s/../auction", + "contracts": "%s/../contracts", + "dao": "%s/../dao", + "dao-protocol": "%s/../dao/protocol", + "dao-trustednode": "%s/../dao/trustednode", + "deposit": "%s/../deposit", + "minipool": "%s/../minipool", + "network": "%s/../network", + "node": "%s/../node", + "rewards": "%s/../rewards", + "rocketpool": "%s/../rocketpool", + "settings-protocol": "%s/../settings/protocol", + "settings-trustednode": "%s/../settings/trustednode", + "storage": "%s/../storage", + "tokens": "%s/../tokens", + "types": "%s/../types", + "utils": "%s/../utils", + "utils-eth": "%s/../utils/eth", + "utils-strings": "%s/../utils/strings", + } + + // Build the documentation file for each package + for filename, path := range packages { + + // Load the source dir + builder, err := build.ImportDir(fmt.Sprintf(path, wd), build.ImportComment) + if err != nil { + fmt.Printf("Error loading package builder for %s: %s\n", filename, err.Error()) + os.Exit(1) + } + + // Create a package from the source + pkg, err := lang.NewPackageFromBuild(log, builder, lang.PackageWithRepositoryOverrides(&lang.Repo{ + Remote: repo, + DefaultBranch: branch, + })) + if err != nil { + fmt.Printf("Error creating package %s: %s\n", filename, err.Error()) + os.Exit(1) + } + + // Render the documentation for the package + packageContents, err := renderer.Package(pkg) + if err != nil { + fmt.Printf("Error exporting package %s: %s\n", filename, err.Error()) + os.Exit(1) + } + + // Write the docs out to the appropriate file + err = os.WriteFile(fmt.Sprintf("%s/%s.md", wd, filename), []byte(packageContents), 0644) + if err != nil { + fmt.Printf("Error writing file for package %s: %s\n", filename, err.Error()) + os.Exit(1) + } + } + +} diff --git a/bindings/go.mod b/bindings/go.mod new file mode 100644 index 000000000..285b2f538 --- /dev/null +++ b/bindings/go.mod @@ -0,0 +1,97 @@ +module github.com/rocket-pool/smartnode/bindings + +go 1.21 + +require ( + github.com/ethereum/go-ethereum v1.13.5 + github.com/hashicorp/go-version v1.6.0 + github.com/princjef/gomarkdoc v0.4.1 + github.com/prysmaticlabs/go-ssz v0.0.0-20210121151755-f6208871c388 + golang.org/x/sync v0.8.0 +) + +require ( + github.com/DataDog/zstd v1.5.5 // indirect + github.com/Microsoft/go-winio v0.6.1 // indirect + github.com/VictoriaMetrics/fastcache v1.12.2 // indirect + github.com/VividCortex/ewma v1.2.0 // indirect + github.com/bits-and-blooms/bitset v1.11.0 // indirect + github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect + github.com/cespare/cp v1.1.1 // indirect + github.com/cespare/xxhash v1.1.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/cheggaaa/pb/v3 v3.0.8 // indirect + github.com/cockroachdb/errors v1.11.1 // indirect + github.com/consensys/bavard v0.1.13 // indirect + github.com/consensys/gnark-crypto v0.12.1 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect + github.com/crate-crypto/go-kzg-4844 v0.7.0 // indirect + github.com/deckarep/golang-set/v2 v2.5.0 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect + github.com/dgraph-io/ristretto v0.0.4-0.20210318174700-74754f61e018 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect + github.com/emicklei/dot v1.6.2 // indirect + github.com/emirpasic/gods v1.12.0 // indirect + github.com/ethereum/c-kzg-4844 v0.4.0 // indirect + github.com/fatih/color v1.14.1 // indirect + github.com/ferranbt/fastssz v0.1.4 // indirect + github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 // indirect + github.com/getsentry/sentry-go v0.25.0 // indirect + github.com/go-git/gcfg v1.5.0 // indirect + github.com/go-git/go-billy/v5 v5.3.1 // indirect + github.com/go-git/go-git/v5 v5.3.0 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect + github.com/go-stack/stack v1.8.1 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/gorilla/websocket v1.5.3 // indirect + github.com/holiman/uint256 v1.2.4 // indirect + github.com/imdario/mergo v0.3.12 // indirect + github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect + github.com/kevinburke/ssh_config v1.1.0 // indirect + github.com/klauspost/cpuid/v2 v2.2.8 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-runewidth v0.0.15 // indirect + github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect + github.com/minio/highwayhash v1.0.2 // indirect + github.com/minio/sha256-simd v1.0.1 // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/mmcloughlin/addchain v0.4.0 // indirect + github.com/onsi/ginkgo v1.16.5 // indirect + github.com/onsi/gomega v1.30.0 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/princjef/mageutil v1.0.0 // indirect + github.com/prometheus/client_golang v1.20.0 // indirect + github.com/prysmaticlabs/go-bitfield v0.0.0-20240328144219-a1caa50c3a1e // indirect + github.com/rivo/uniseg v0.4.4 // indirect + github.com/rogpeppe/go-internal v1.11.0 // indirect + github.com/rs/cors v1.8.2 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/sergi/go-diff v1.2.0 // indirect + github.com/shirou/gopsutil v3.21.11+incompatible // indirect + github.com/sirupsen/logrus v1.9.0 // indirect + github.com/spaolacci/murmur3 v1.1.0 // indirect + github.com/stretchr/testify v1.9.0 // indirect + github.com/supranational/blst v0.3.11 // indirect + github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect + github.com/tklauser/go-sysconf v0.3.13 // indirect + github.com/tklauser/numcpus v0.7.0 // indirect + github.com/urfave/cli/v2 v2.26.0 // indirect + github.com/x-cray/logrus-prefixed-formatter v0.5.2 // indirect + github.com/xanzy/ssh-agent v0.3.0 // indirect + github.com/yusufpapurcu/wmi v1.2.3 // indirect + golang.org/x/crypto v0.26.0 // indirect + golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa // indirect + golang.org/x/mod v0.20.0 // indirect + golang.org/x/net v0.28.0 // indirect + golang.org/x/sys v0.24.0 // indirect + golang.org/x/term v0.23.0 // indirect + golang.org/x/time v0.5.0 // indirect + golang.org/x/tools v0.24.0 // indirect + gopkg.in/warnings.v0 v0.1.2 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + mvdan.cc/xurls/v2 v2.2.0 // indirect + rsc.io/tmplfunc v0.0.3 // indirect +) diff --git a/bindings/go.sum b/bindings/go.sum new file mode 100644 index 000000000..052be3ced --- /dev/null +++ b/bindings/go.sum @@ -0,0 +1,181 @@ +github.com/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ= +github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= +github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= +github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= +github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/VictoriaMetrics/fastcache v1.12.2 h1:N0y9ASrJ0F6h0QaC3o6uJb3NIZ9VKLjCM7NQbSmF7WI= +github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA= +github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow= +github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs= +github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= +github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA= +github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/bits-and-blooms/bitset v1.11.0 h1:RMyy2mBBShArUAhfVRZJ2xyBO58KCBCtZFShw3umo6k= +github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= +github.com/cespare/cp v1.1.1 h1:nCb6ZLdB7NRaqsm91JtQTAme2SKJzXVsdPIPkyJr1MU= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cheggaaa/pb v2.0.7+incompatible/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s= +github.com/cheggaaa/pb/v3 v3.0.4/go.mod h1:7rgWxLrAUcFMkvJuv09+DYi7mMUYi8nO9iOWcvGJPfw= +github.com/cheggaaa/pb/v3 v3.0.8 h1:bC8oemdChbke2FHIIGy9mn4DPJ2caZYQnfbRqwmdCoA= +github.com/cockroachdb/errors v1.11.1 h1:xSEW75zKaKCWzR3OfxXUxgrk/NtT4G1MiOv5lWZazG8= +github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= +github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593 h1:aPEJyR4rPBvDmeyi+l/FS/VtA00IWvjeFvjen1m1l1A= +github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= +github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ= +github.com/consensys/gnark-crypto v0.12.1 h1:lHH39WuuFgVHONRl3J0LRBtuYdQTumFSDtJF7HpyG8M= +github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM= +github.com/crate-crypto/go-kzg-4844 v0.7.0 h1:C0vgZRk4q4EZ/JgPfzuSoxdCq3C3mOZMBShovmncxvA= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/deckarep/golang-set/v2 v2.5.0 h1:hn6cEZtQ0h3J8kFrHR/NrzyOoTnjgW1+FmNJzQ7y/sA= +github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg= +github.com/dgraph-io/ristretto v0.0.4-0.20210318174700-74754f61e018 h1:cNcG4c2n5xanQzp2hMyxDxPYVQmZ91y4WN6fJFlndLo= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/emicklei/dot v1.6.2 h1:08GN+DD79cy/tzN6uLCT84+2Wk9u+wvqP+Hkx/dIR8A= +github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg= +github.com/ethereum/c-kzg-4844 v0.4.0 h1:3MS1s4JtA868KpJxroZoepdV0ZKBp3u/O5HcZ7R3nlY= +github.com/ethereum/go-ethereum v1.13.5 h1:U6TCRciCqZRe4FPXmy1sMGxTfuk8P7u2UoinF3VbaFk= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= +github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= +github.com/fatih/color v1.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w= +github.com/ferranbt/fastssz v0.1.4 h1:OCDB+dYDEQDvAgtAGnTSidK1Pe2tW3nFV40XyMkTeDY= +github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 h1:FtmdgXiUlNeRsoNMFlKLDt+S+6hbjVMEW6RGQ7aUf7c= +github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 h1:f6D9Hr8xV8uYKlyuj8XIruxlh9WjVjdh1gIicAS7ays= +github.com/getsentry/sentry-go v0.25.0 h1:q6Eo+hS+yoJlTO3uu/azhQadsD8V+jQn2D8VvX1eOyI= +github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0= +github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= +github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4= +github.com/go-git/go-billy/v5 v5.0.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= +github.com/go-git/go-billy/v5 v5.1.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= +github.com/go-git/go-billy/v5 v5.3.1 h1:CPiOUAzKtMRvolEKw+bG1PLRpT7D3LIs3/3ey4Aiu34= +github.com/go-git/go-git-fixtures/v4 v4.0.2-0.20200613231340-f56387b50c12 h1:PbKy9zOy4aAKrJ5pibIRpVO2BXnK1Tlcg+caKI7Ox5M= +github.com/go-git/go-git-fixtures/v4 v4.0.2-0.20200613231340-f56387b50c12/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw= +github.com/go-git/go-git/v5 v5.3.0 h1:8WKMtJR2j8RntEXR/uvTKagfEt4GYlwQ7mntE4+0GWc= +github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= +github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= +github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= +github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= +github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE= +github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= +github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7 h1:3JQNjnMRil1yD0IfZKHF9GxxWKDJGj8I0IqOUol//sw= +github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= +github.com/holiman/uint256 v1.2.4 h1:jUc4Nk8fm9jZabQuqr2JzednajVmBpC+oiTiXZJEApU= +github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= +github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= +github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= +github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= +github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= +github.com/kevinburke/ssh_config v1.1.0 h1:pH/t1WS9NzT8go394IqZeJTMHVm6Cr6ZJ6AQ+mdNo/o= +github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= +github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= +github.com/matryer/is v1.3.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA= +github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= +github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= +github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= +github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI= +github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g= +github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A= +github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= +github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/princjef/gomarkdoc v0.4.1 h1:Ubt5OiHYi2PdxrDkWMeWM4ROrbvAGkIXBz3PquxglBM= +github.com/princjef/mageutil v1.0.0 h1:1OfZcJUMsooPqieOz2ooLjI+uHUo618pdaJsbCXcFjQ= +github.com/prometheus/client_golang v1.20.0 h1:jBzTZ7B099Rg24tny+qngoynol8LtVYlA2bqx3vEloI= +github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= +github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= +github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= +github.com/prysmaticlabs/go-bitfield v0.0.0-20240328144219-a1caa50c3a1e h1:ATgOe+abbzfx9kCPeXIW4fiWyDdxlwHw07j8UGhdTd4= +github.com/prysmaticlabs/go-ssz v0.0.0-20210121151755-f6208871c388 h1:4bD+ujqGfY4zoDUF3q9MhdmpPXzdp03DYUIlXeQ72kk= +github.com/prysmaticlabs/gohashtree v0.0.4-beta h1:H/EbCuXPeTV3lpKeXGPpEV9gsUpkqOOVnWapUyeWro4= +github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= +github.com/rogpeppe/go-internal v1.5.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/rs/cors v1.8.2 h1:KCooALfAYGs415Cwu5ABvv9n9509fSiG5SQJn/AQo4U= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= +github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= +github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= +github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbeoEm4= +github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDdvS342BElfbETmL1Aiz3i2t0zfRj16Hs= +github.com/tklauser/go-sysconf v0.3.13 h1:GBUpcahXSpR2xN01jhkNAbTLRk2Yzgggk8IM08lq3r4= +github.com/tklauser/numcpus v0.7.0 h1:yjuerZP127QG9m5Zh/mSO4wqurYil27tHrqwRoRjpr4= +github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= +github.com/urfave/cli/v2 v2.26.0 h1:3f3AMg3HpThFNT4I++TKOejZO8yU55t3JnnSr4S4QEI= +github.com/x-cray/logrus-prefixed-formatter v0.5.2 h1:00txxvfBM9muc0jiLIEAkAcIMJzfthRT6usrui8uGmg= +github.com/xanzy/ssh-agent v0.3.0 h1:wUMzuKtKilRgBAD1sUb8gOwwRr2FGoBVumcjoOACClI= +github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= +github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= +golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= +golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa h1:ELnwvuAXPNtPk1TJRuGkI9fDTwym6AYBu0qzT8AcHdI= +golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0= +golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k= +golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= +golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= +golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +gopkg.in/VividCortex/ewma.v1 v1.1.1/go.mod h1:TekXuFipeiHWiAlO1+wSS23vTcyFau5u3rxXUSXj710= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/cheggaaa/pb.v2 v2.0.7/go.mod h1:0CiZ1p8pvtxBlQpLXkHuUTpdJ1shm3OqCF1QugkjHL4= +gopkg.in/fatih/color.v1 v1.7.0/go.mod h1:P7yosIhqIl/sX8J8UypY5M+dDpD2KmyfP5IRs5v/fo0= +gopkg.in/mattn/go-colorable.v0 v0.1.0/go.mod h1:BVJlBXzARQxdi3nZo6f6bnl5yR20/tOL6p+V0KejgSY= +gopkg.in/mattn/go-isatty.v0 v0.0.4/go.mod h1:wt691ab7g0X4ilKZNmMII3egK0bTxl37fEn/Fwbd8gc= +gopkg.in/mattn/go-runewidth.v0 v0.0.4/go.mod h1:BmXejnxvhwdaATwiJbB1vZ2dtXkQKZGu9yLFCZb4msQ= +gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +mvdan.cc/xurls/v2 v2.2.0 h1:NSZPykBXJFCetGZykLAxaL6SIpvbVy/UFEniIfHAa8A= +rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU= diff --git a/bindings/legacy/v1.0.0/minipool/minipool.go b/bindings/legacy/v1.0.0/minipool/minipool.go new file mode 100644 index 000000000..32530c73d --- /dev/null +++ b/bindings/legacy/v1.0.0/minipool/minipool.go @@ -0,0 +1,552 @@ +package minipool + +import ( + "fmt" + "math/big" + "sync" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "golang.org/x/sync/errgroup" + + "github.com/rocket-pool/smartnode/bindings/rocketpool" + rptypes "github.com/rocket-pool/smartnode/bindings/types" +) + +// Settings +const ( + MinipoolPrelaunchBatchSize = 250 + MinipoolAddressBatchSize = 50 + MinipoolDetailsBatchSize = 20 +) + +// Minipool details +type MinipoolDetails struct { + Address common.Address `json:"address"` + Exists bool `json:"exists"` + Pubkey rptypes.ValidatorPubkey `json:"pubkey"` +} + +// The counts of minipools per status +type MinipoolCountsPerStatus struct { + Initialized *big.Int `abi:"initialisedCount"` + Prelaunch *big.Int `abi:"prelaunchCount"` + Staking *big.Int `abi:"stakingCount"` + Withdrawable *big.Int `abi:"withdrawableCount"` + Dissolved *big.Int `abi:"dissolvedCount"` +} + +// Get all minipool details +func GetMinipools(rp *rocketpool.RocketPool, opts *bind.CallOpts, legacyRocketMinipoolManagerAddress *common.Address) ([]MinipoolDetails, error) { + minipoolAddresses, err := GetMinipoolAddresses(rp, opts, legacyRocketMinipoolManagerAddress) + if err != nil { + return []MinipoolDetails{}, err + } + return loadMinipoolDetails(rp, minipoolAddresses, opts, legacyRocketMinipoolManagerAddress) +} + +// Get a node's minipool details +func GetNodeMinipools(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts, legacyRocketMinipoolManagerAddress *common.Address) ([]MinipoolDetails, error) { + minipoolAddresses, err := GetNodeMinipoolAddresses(rp, nodeAddress, opts, legacyRocketMinipoolManagerAddress) + if err != nil { + return []MinipoolDetails{}, err + } + return loadMinipoolDetails(rp, minipoolAddresses, opts, legacyRocketMinipoolManagerAddress) +} + +// Load minipool details +func loadMinipoolDetails(rp *rocketpool.RocketPool, minipoolAddresses []common.Address, opts *bind.CallOpts, legacyRocketMinipoolManagerAddress *common.Address) ([]MinipoolDetails, error) { + + // Load minipool details in batches + details := make([]MinipoolDetails, len(minipoolAddresses)) + for bsi := 0; bsi < len(minipoolAddresses); bsi += MinipoolDetailsBatchSize { + + // Get batch start & end index + msi := bsi + mei := bsi + MinipoolDetailsBatchSize + if mei > len(minipoolAddresses) { + mei = len(minipoolAddresses) + } + + // Load details + var wg errgroup.Group + for mi := msi; mi < mei; mi++ { + mi := mi + wg.Go(func() error { + minipoolAddress := minipoolAddresses[mi] + minipoolDetails, err := GetMinipoolDetails(rp, minipoolAddress, opts, legacyRocketMinipoolManagerAddress) + if err == nil { + details[mi] = minipoolDetails + } + return err + }) + } + if err := wg.Wait(); err != nil { + return []MinipoolDetails{}, err + } + + } + + // Return + return details, nil + +} + +// Get all minipool addresses +func GetMinipoolAddresses(rp *rocketpool.RocketPool, opts *bind.CallOpts, legacyRocketMinipoolManagerAddress *common.Address) ([]common.Address, error) { + + // Get minipool count + minipoolCount, err := GetMinipoolCount(rp, opts, legacyRocketMinipoolManagerAddress) + if err != nil { + return []common.Address{}, err + } + + // Load minipool addresses in batches + addresses := make([]common.Address, minipoolCount) + for bsi := uint64(0); bsi < minipoolCount; bsi += MinipoolAddressBatchSize { + + // Get batch start & end index + msi := bsi + mei := bsi + MinipoolAddressBatchSize + if mei > minipoolCount { + mei = minipoolCount + } + + // Load addresses + var wg errgroup.Group + for mi := msi; mi < mei; mi++ { + mi := mi + wg.Go(func() error { + address, err := GetMinipoolAt(rp, mi, opts, legacyRocketMinipoolManagerAddress) + if err == nil { + addresses[mi] = address + } + return err + }) + } + if err := wg.Wait(); err != nil { + return []common.Address{}, err + } + + } + + // Return + return addresses, nil + +} + +// Get the addresses of all minipools in prelaunch status +func GetPrelaunchMinipoolAddresses(rp *rocketpool.RocketPool, opts *bind.CallOpts, legacyRocketMinipoolManagerAddress *common.Address) ([]common.Address, error) { + + rocketMinipoolManager, err := getRocketMinipoolManager(rp, legacyRocketMinipoolManagerAddress, opts) + if err != nil { + return []common.Address{}, err + } + + // Get the total number of minipools + totalMinipoolsUint, err := GetMinipoolCount(rp, nil, legacyRocketMinipoolManagerAddress) + if err != nil { + return []common.Address{}, err + } + + totalMinipools := int64(totalMinipoolsUint) + addresses := []common.Address{} + limit := big.NewInt(MinipoolPrelaunchBatchSize) + for i := int64(0); i < totalMinipools; i += MinipoolPrelaunchBatchSize { + // Get a batch of addresses + offset := big.NewInt(i) + newAddresses := new([]common.Address) + if err := rocketMinipoolManager.Call(opts, newAddresses, "getPrelaunchMinipools", offset, limit); err != nil { + return []common.Address{}, fmt.Errorf("error getting prelaunch minipool addresses: %w", err) + } + addresses = append(addresses, *newAddresses...) + } + + return addresses, nil +} + +// Get a node's minipool addresses +func GetNodeMinipoolAddresses(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts, legacyRocketMinipoolManagerAddress *common.Address) ([]common.Address, error) { + + // Get minipool count + minipoolCount, err := GetNodeMinipoolCount(rp, nodeAddress, opts, legacyRocketMinipoolManagerAddress) + if err != nil { + return []common.Address{}, err + } + + // Load minipool addresses in batches + addresses := make([]common.Address, minipoolCount) + for bsi := uint64(0); bsi < minipoolCount; bsi += MinipoolAddressBatchSize { + + // Get batch start & end index + msi := bsi + mei := bsi + MinipoolAddressBatchSize + if mei > minipoolCount { + mei = minipoolCount + } + + // Load addresses + var wg errgroup.Group + for mi := msi; mi < mei; mi++ { + mi := mi + wg.Go(func() error { + address, err := GetNodeMinipoolAt(rp, nodeAddress, mi, opts, legacyRocketMinipoolManagerAddress) + if err == nil { + addresses[mi] = address + } + return err + }) + } + if err := wg.Wait(); err != nil { + return []common.Address{}, err + } + + } + + // Return + return addresses, nil + +} + +// Get a node's validating minipool pubkeys +func GetNodeValidatingMinipoolPubkeys(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts, legacyRocketMinipoolManagerAddress *common.Address) ([]rptypes.ValidatorPubkey, error) { + + // Get minipool count + minipoolCount, err := GetNodeValidatingMinipoolCount(rp, nodeAddress, opts, legacyRocketMinipoolManagerAddress) + if err != nil { + return []rptypes.ValidatorPubkey{}, err + } + + // Load pubkeys in batches + var lock = sync.RWMutex{} + pubkeys := make([]rptypes.ValidatorPubkey, minipoolCount) + for bsi := uint64(0); bsi < minipoolCount; bsi += MinipoolAddressBatchSize { + + // Get batch start & end index + msi := bsi + mei := bsi + MinipoolAddressBatchSize + if mei > minipoolCount { + mei = minipoolCount + } + + // Load pubkeys + var wg errgroup.Group + for mi := msi; mi < mei; mi++ { + mi := mi + wg.Go(func() error { + minipoolAddress, err := GetNodeValidatingMinipoolAt(rp, nodeAddress, mi, opts, legacyRocketMinipoolManagerAddress) + if err != nil { + return err + } + pubkey, err := GetMinipoolPubkey(rp, minipoolAddress, opts, legacyRocketMinipoolManagerAddress) + if err != nil { + return err + } + lock.Lock() + pubkeys[mi] = pubkey + lock.Unlock() + return nil + }) + } + if err := wg.Wait(); err != nil { + return []rptypes.ValidatorPubkey{}, err + } + + } + + // Return + return pubkeys, nil + +} + +// Get a minipool's details +func GetMinipoolDetails(rp *rocketpool.RocketPool, minipoolAddress common.Address, opts *bind.CallOpts, legacyRocketMinipoolManagerAddress *common.Address) (MinipoolDetails, error) { + + // Data + var wg errgroup.Group + var exists bool + var pubkey rptypes.ValidatorPubkey + + // Load data + wg.Go(func() error { + var err error + exists, err = GetMinipoolExists(rp, minipoolAddress, opts, legacyRocketMinipoolManagerAddress) + return err + }) + wg.Go(func() error { + var err error + pubkey, err = GetMinipoolPubkey(rp, minipoolAddress, opts, legacyRocketMinipoolManagerAddress) + return err + }) + + // Wait for data + if err := wg.Wait(); err != nil { + return MinipoolDetails{}, err + } + + // Return + return MinipoolDetails{ + Address: minipoolAddress, + Exists: exists, + Pubkey: pubkey, + }, nil + +} + +// Get the minipool count +func GetMinipoolCount(rp *rocketpool.RocketPool, opts *bind.CallOpts, legacyRocketMinipoolManagerAddress *common.Address) (uint64, error) { + rocketMinipoolManager, err := getRocketMinipoolManager(rp, nil, opts) + if err != nil { + return 0, err + } + minipoolCount := new(*big.Int) + if err := rocketMinipoolManager.Call(opts, minipoolCount, "getMinipoolCount"); err != nil { + return 0, fmt.Errorf("error getting minipool count: %w", err) + } + return (*minipoolCount).Uint64(), nil +} + +// Get the number of finalised minipools in the network +func GetFinalisedMinipoolCount(rp *rocketpool.RocketPool, opts *bind.CallOpts, legacyRocketMinipoolManagerAddress *common.Address) (uint64, error) { + rocketMinipoolManager, err := getRocketMinipoolManager(rp, nil, opts) + if err != nil { + return 0, err + } + minipoolCount := new(*big.Int) + if err := rocketMinipoolManager.Call(opts, minipoolCount, "getFinalisedMinipoolCount"); err != nil { + return 0, fmt.Errorf("error getting finalised minipool count: %w", err) + } + return (*minipoolCount).Uint64(), nil +} + +// Get the number of active minipools in the network +func GetActiveMinipoolCount(rp *rocketpool.RocketPool, opts *bind.CallOpts, legacyRocketMinipoolManagerAddress *common.Address) (uint64, error) { + rocketMinipoolManager, err := getRocketMinipoolManager(rp, nil, opts) + if err != nil { + return 0, err + } + minipoolCount := new(*big.Int) + if err := rocketMinipoolManager.Call(opts, minipoolCount, "getActiveMinipoolCount"); err != nil { + return 0, fmt.Errorf("error getting finalised minipool count: %w", err) + } + return (*minipoolCount).Uint64(), nil +} + +// Get the minipool count by status +func GetMinipoolCountPerStatus(rp *rocketpool.RocketPool, opts *bind.CallOpts, legacyRocketMinipoolManagerAddress *common.Address) (MinipoolCountsPerStatus, error) { + rocketMinipoolManager, err := getRocketMinipoolManager(rp, legacyRocketMinipoolManagerAddress, opts) + if err != nil { + return MinipoolCountsPerStatus{}, err + } + + // Get the total number of minipools + totalMinipoolsUint, err := GetMinipoolCount(rp, nil, legacyRocketMinipoolManagerAddress) + if err != nil { + return MinipoolCountsPerStatus{}, err + } + + totalMinipools := int64(totalMinipoolsUint) + minipoolCounts := MinipoolCountsPerStatus{ + Initialized: big.NewInt(0), + Prelaunch: big.NewInt(0), + Staking: big.NewInt(0), + Dissolved: big.NewInt(0), + Withdrawable: big.NewInt(0), + } + limit := big.NewInt(MinipoolPrelaunchBatchSize) + for i := int64(0); i < totalMinipools; i += MinipoolPrelaunchBatchSize { + // Get a batch of counts + offset := big.NewInt(i) + newMinipoolCounts := new(MinipoolCountsPerStatus) + if err := rocketMinipoolManager.Call(opts, newMinipoolCounts, "getMinipoolCountPerStatus", offset, limit); err != nil { + return MinipoolCountsPerStatus{}, fmt.Errorf("error getting minipool counts: %w", err) + } + if newMinipoolCounts != nil { + if newMinipoolCounts.Initialized != nil { + minipoolCounts.Initialized.Add(minipoolCounts.Initialized, newMinipoolCounts.Initialized) + } + if newMinipoolCounts.Prelaunch != nil { + minipoolCounts.Prelaunch.Add(minipoolCounts.Prelaunch, newMinipoolCounts.Prelaunch) + } + if newMinipoolCounts.Staking != nil { + minipoolCounts.Staking.Add(minipoolCounts.Staking, newMinipoolCounts.Staking) + } + if newMinipoolCounts.Dissolved != nil { + minipoolCounts.Dissolved.Add(minipoolCounts.Dissolved, newMinipoolCounts.Dissolved) + } + if newMinipoolCounts.Withdrawable != nil { + minipoolCounts.Withdrawable.Add(minipoolCounts.Withdrawable, newMinipoolCounts.Withdrawable) + } + } + } + return minipoolCounts, nil +} + +// Get a minipool address by index +func GetMinipoolAt(rp *rocketpool.RocketPool, index uint64, opts *bind.CallOpts, legacyRocketMinipoolManagerAddress *common.Address) (common.Address, error) { + rocketMinipoolManager, err := getRocketMinipoolManager(rp, legacyRocketMinipoolManagerAddress, opts) + if err != nil { + return common.Address{}, err + } + minipoolAddress := new(common.Address) + if err := rocketMinipoolManager.Call(opts, minipoolAddress, "getMinipoolAt", big.NewInt(int64(index))); err != nil { + return common.Address{}, fmt.Errorf("error getting minipool %d address: %w", index, err) + } + return *minipoolAddress, nil +} + +// Get a node's minipool count +func GetNodeMinipoolCount(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts, legacyRocketMinipoolManagerAddress *common.Address) (uint64, error) { + rocketMinipoolManager, err := getRocketMinipoolManager(rp, legacyRocketMinipoolManagerAddress, opts) + if err != nil { + return 0, err + } + minipoolCount := new(*big.Int) + if err := rocketMinipoolManager.Call(opts, minipoolCount, "getNodeMinipoolCount", nodeAddress); err != nil { + return 0, fmt.Errorf("error getting node %s minipool count: %w", nodeAddress.Hex(), err) + } + return (*minipoolCount).Uint64(), nil +} + +// Get the number of minipools owned by a node that are not finalised +func GetNodeActiveMinipoolCount(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts, legacyRocketMinipoolManagerAddress *common.Address) (uint64, error) { + rocketMinipoolManager, err := getRocketMinipoolManager(rp, legacyRocketMinipoolManagerAddress, opts) + if err != nil { + return 0, err + } + minipoolCount := new(*big.Int) + if err := rocketMinipoolManager.Call(opts, minipoolCount, "getNodeActiveMinipoolCount", nodeAddress); err != nil { + return 0, fmt.Errorf("error getting node %s minipool count: %w", nodeAddress.Hex(), err) + } + return (*minipoolCount).Uint64(), nil +} + +// Get the number of minipools owned by a node that are finalised +func GetNodeFinalisedMinipoolCount(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts, legacyRocketMinipoolManagerAddress *common.Address) (uint64, error) { + rocketMinipoolManager, err := getRocketMinipoolManager(rp, legacyRocketMinipoolManagerAddress, opts) + if err != nil { + return 0, err + } + minipoolCount := new(*big.Int) + if err := rocketMinipoolManager.Call(opts, minipoolCount, "getNodeFinalisedMinipoolCount", nodeAddress); err != nil { + return 0, fmt.Errorf("error getting node %s minipool count: %w", nodeAddress.Hex(), err) + } + return (*minipoolCount).Uint64(), nil +} + +// Get a node's minipool address by index +func GetNodeMinipoolAt(rp *rocketpool.RocketPool, nodeAddress common.Address, index uint64, opts *bind.CallOpts, legacyRocketMinipoolManagerAddress *common.Address) (common.Address, error) { + rocketMinipoolManager, err := getRocketMinipoolManager(rp, legacyRocketMinipoolManagerAddress, opts) + if err != nil { + return common.Address{}, err + } + minipoolAddress := new(common.Address) + if err := rocketMinipoolManager.Call(opts, minipoolAddress, "getNodeMinipoolAt", nodeAddress, big.NewInt(int64(index))); err != nil { + return common.Address{}, fmt.Errorf("error getting node %s minipool %d address: %w", nodeAddress.Hex(), index, err) + } + return *minipoolAddress, nil +} + +// Get a node's validating minipool count +func GetNodeValidatingMinipoolCount(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts, legacyRocketMinipoolManagerAddress *common.Address) (uint64, error) { + rocketMinipoolManager, err := getRocketMinipoolManager(rp, legacyRocketMinipoolManagerAddress, opts) + if err != nil { + return 0, err + } + minipoolCount := new(*big.Int) + if err := rocketMinipoolManager.Call(opts, minipoolCount, "getNodeValidatingMinipoolCount", nodeAddress); err != nil { + return 0, fmt.Errorf("error getting node %s validating minipool count: %w", nodeAddress.Hex(), err) + } + return (*minipoolCount).Uint64(), nil +} + +// Get a node's validating minipool address by index +func GetNodeValidatingMinipoolAt(rp *rocketpool.RocketPool, nodeAddress common.Address, index uint64, opts *bind.CallOpts, legacyRocketMinipoolManagerAddress *common.Address) (common.Address, error) { + rocketMinipoolManager, err := getRocketMinipoolManager(rp, legacyRocketMinipoolManagerAddress, opts) + if err != nil { + return common.Address{}, err + } + minipoolAddress := new(common.Address) + if err := rocketMinipoolManager.Call(opts, minipoolAddress, "getNodeValidatingMinipoolAt", nodeAddress, big.NewInt(int64(index))); err != nil { + return common.Address{}, fmt.Errorf("error getting node %s validating minipool %d address: %w", nodeAddress.Hex(), index, err) + } + return *minipoolAddress, nil +} + +// Get a minipool address by validator pubkey +func GetMinipoolByPubkey(rp *rocketpool.RocketPool, pubkey rptypes.ValidatorPubkey, opts *bind.CallOpts, legacyRocketMinipoolManagerAddress *common.Address) (common.Address, error) { + rocketMinipoolManager, err := getRocketMinipoolManager(rp, legacyRocketMinipoolManagerAddress, opts) + if err != nil { + return common.Address{}, err + } + minipoolAddress := new(common.Address) + if err := rocketMinipoolManager.Call(opts, minipoolAddress, "getMinipoolByPubkey", pubkey[:]); err != nil { + return common.Address{}, fmt.Errorf("error getting validator %s minipool address: %w", pubkey.Hex(), err) + } + return *minipoolAddress, nil +} + +// Check whether a minipool exists +func GetMinipoolExists(rp *rocketpool.RocketPool, minipoolAddress common.Address, opts *bind.CallOpts, legacyRocketMinipoolManagerAddress *common.Address) (bool, error) { + rocketMinipoolManager, err := getRocketMinipoolManager(rp, legacyRocketMinipoolManagerAddress, opts) + if err != nil { + return false, err + } + exists := new(bool) + if err := rocketMinipoolManager.Call(opts, exists, "getMinipoolExists", minipoolAddress); err != nil { + return false, fmt.Errorf("error getting minipool %s exists status: %w", minipoolAddress.Hex(), err) + } + return *exists, nil +} + +// Get a minipool's validator pubkey +func GetMinipoolPubkey(rp *rocketpool.RocketPool, minipoolAddress common.Address, opts *bind.CallOpts, legacyRocketMinipoolManagerAddress *common.Address) (rptypes.ValidatorPubkey, error) { + rocketMinipoolManager, err := getRocketMinipoolManager(rp, legacyRocketMinipoolManagerAddress, opts) + if err != nil { + return rptypes.ValidatorPubkey{}, err + } + pubkey := new(rptypes.ValidatorPubkey) + if err := rocketMinipoolManager.Call(opts, pubkey, "getMinipoolPubkey", minipoolAddress); err != nil { + return rptypes.ValidatorPubkey{}, fmt.Errorf("error getting minipool %s pubkey: %w", minipoolAddress.Hex(), err) + } + return *pubkey, nil +} + +// Get the CreationCode binary for the RocketMinipool contract that will be created by node deposits +func GetMinipoolBytecode(rp *rocketpool.RocketPool, opts *bind.CallOpts, legacyRocketMinipoolManagerAddress *common.Address) ([]byte, error) { + rocketMinipoolManager, err := getRocketMinipoolManager(rp, legacyRocketMinipoolManagerAddress, opts) + if err != nil { + return []byte{}, err + } + bytecode := new([]byte) + if err := rocketMinipoolManager.Call(opts, bytecode, "getMinipoolBytecode"); err != nil { + return []byte{}, fmt.Errorf("error getting minipool contract bytecode: %w", err) + } + return *bytecode, nil +} + +// Get the 0x01-based Beacon Chain withdrawal credentials for a given minipool +func GetMinipoolWithdrawalCredentials(rp *rocketpool.RocketPool, minipoolAddress common.Address, opts *bind.CallOpts, legacyRocketMinipoolManagerAddress *common.Address) (common.Hash, error) { + rocketMinipoolManager, err := getRocketMinipoolManager(rp, legacyRocketMinipoolManagerAddress, opts) + if err != nil { + return common.Hash{}, err + } + withdrawalCredentials := new(common.Hash) + if err := rocketMinipoolManager.Call(opts, withdrawalCredentials, "getMinipoolWithdrawalCredentials", minipoolAddress); err != nil { + return common.Hash{}, fmt.Errorf("error getting minipool withdrawal credentials: %w", err) + } + return *withdrawalCredentials, nil +} + +// Get contracts +var rocketMinipoolManagerLock sync.Mutex + +func getRocketMinipoolManager(rp *rocketpool.RocketPool, address *common.Address, opts *bind.CallOpts) (*rocketpool.Contract, error) { + rocketMinipoolManagerLock.Lock() + defer rocketMinipoolManagerLock.Unlock() + if address == nil { + return rp.VersionManager.V1_0_0.GetContract("rocketMinipoolManager", opts) + } else { + return rp.VersionManager.V1_0_0.GetContractWithAddress("rocketMinipoolManager", *address) + } +} diff --git a/bindings/legacy/v1.0.0/rewards/node.go b/bindings/legacy/v1.0.0/rewards/node.go new file mode 100644 index 000000000..666a42e4a --- /dev/null +++ b/bindings/legacy/v1.0.0/rewards/node.go @@ -0,0 +1,132 @@ +package rewards + +import ( + "math/big" + "sync" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/utils/eth" +) + +// Get whether node reward claims are enabled +func GetNodeClaimsEnabled(rp *rocketpool.RocketPool, opts *bind.CallOpts, legacyRocketClaimNodeAddress *common.Address) (bool, error) { + rocketClaimNode, err := getRocketClaimNode(rp, legacyRocketClaimNodeAddress, opts) + if err != nil { + return false, err + } + return getEnabled(rocketClaimNode, "node", opts) +} + +// Get whether a node rewards claimer can claim +func GetNodeClaimPossible(rp *rocketpool.RocketPool, claimerAddress common.Address, opts *bind.CallOpts, legacyRocketClaimNodeAddress *common.Address) (bool, error) { + rocketClaimNode, err := getRocketClaimNode(rp, legacyRocketClaimNodeAddress, opts) + if err != nil { + return false, err + } + return getClaimPossible(rocketClaimNode, "node", claimerAddress, opts) +} + +// Get the percentage of rewards available for a node rewards claimer +func GetNodeClaimRewardsPerc(rp *rocketpool.RocketPool, claimerAddress common.Address, opts *bind.CallOpts, legacyRocketClaimNodeAddress *common.Address) (float64, error) { + rocketClaimNode, err := getRocketClaimNode(rp, legacyRocketClaimNodeAddress, opts) + if err != nil { + return 0, err + } + return getClaimRewardsPerc(rocketClaimNode, "node", claimerAddress, opts) +} + +// Get the total amount of rewards available for a node rewards claimer +func GetNodeClaimRewardsAmount(rp *rocketpool.RocketPool, claimerAddress common.Address, opts *bind.CallOpts, legacyRocketClaimNodeAddress *common.Address) (*big.Int, error) { + rocketClaimNode, err := getRocketClaimNode(rp, legacyRocketClaimNodeAddress, opts) + if err != nil { + return nil, err + } + return getClaimRewardsAmount(rocketClaimNode, "node", claimerAddress, opts) +} + +// Estimate the gas of ClaimNodeRewards +func EstimateClaimNodeRewardsGas(rp *rocketpool.RocketPool, opts *bind.TransactOpts, legacyRocketClaimNodeAddress *common.Address) (rocketpool.GasInfo, error) { + rocketClaimNode, err := getRocketClaimNode(rp, legacyRocketClaimNodeAddress, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return estimateClaimGas(rocketClaimNode, opts) +} + +// Claim node rewards +func ClaimNodeRewards(rp *rocketpool.RocketPool, opts *bind.TransactOpts, legacyRocketClaimNodeAddress *common.Address) (common.Hash, error) { + rocketClaimNode, err := getRocketClaimNode(rp, legacyRocketClaimNodeAddress, nil) + if err != nil { + return common.Hash{}, err + } + return claim(rocketClaimNode, "node", opts) +} + +// Filters through token claim events and sums the total amount claimed by claimerAddress +func CalculateLifetimeNodeRewards(rp *rocketpool.RocketPool, claimerAddress common.Address, intervalSize *big.Int, startBlock *big.Int, legacyRocketRewardsPoolAddress *common.Address, legacyRocketClaimNodeAddress *common.Address, opts *bind.CallOpts) (*big.Int, error) { + // Get contracts + rocketRewardsPool, err := getRocketRewardsPool(rp, legacyRocketRewardsPoolAddress, opts) + if err != nil { + return nil, err + } + rocketClaimNode, err := getRocketClaimNode(rp, legacyRocketClaimNodeAddress, opts) + if err != nil { + return nil, err + } + // Construct a filter query for relevant logs + addressFilter := []common.Address{*rocketRewardsPool.Address} + // RPLTokensClaimed(address clamingContract, address claimingAddress, uint256 amount, uint256 time) + topicFilter := [][]common.Hash{ + {rocketRewardsPool.ABI.Events["RPLTokensClaimed"].ID}, + {common.BytesToHash(rocketClaimNode.Address.Bytes())}, + {common.BytesToHash(claimerAddress.Bytes())}, + } + + // Get the event logs + logs, err := eth.GetLogs(rp, addressFilter, topicFilter, intervalSize, startBlock, nil, nil) + if err != nil { + return nil, err + } + + // Iterate over the logs and sum the amount + sum := big.NewInt(0) + for _, log := range logs { + values := make(map[string]interface{}) + // Decode the event + if rocketRewardsPool.ABI.Events["RPLTokensClaimed"].Inputs.UnpackIntoMap(values, log.Data) != nil { + return nil, err + } + // Add the amount argument to our sum + amount := values["amount"].(*big.Int) + sum.Add(sum, amount) + } + // Return the result + return sum, nil +} + +// Get the time that the user registered as a claimer +func GetNodeRegistrationTime(rp *rocketpool.RocketPool, claimerAddress common.Address, opts *bind.CallOpts, legacyRocketRewardsPoolAddress *common.Address) (time.Time, error) { + return getClaimingContractUserRegisteredTime(rp, "rocketClaimNode", claimerAddress, opts, legacyRocketRewardsPoolAddress) +} + +// Get the total rewards claimed for this claiming contract this interval +func GetNodeTotalClaimed(rp *rocketpool.RocketPool, opts *bind.CallOpts, legacyRocketRewardsPoolAddress *common.Address) (*big.Int, error) { + return getClaimingContractTotalClaimed(rp, "rocketClaimNode", opts, legacyRocketRewardsPoolAddress) +} + +// Get contracts +var rocketClaimNodeLock sync.Mutex + +func getRocketClaimNode(rp *rocketpool.RocketPool, address *common.Address, opts *bind.CallOpts) (*rocketpool.Contract, error) { + rocketClaimNodeLock.Lock() + defer rocketClaimNodeLock.Unlock() + if address == nil { + return rp.VersionManager.V1_0_0.GetContract("rocketClaimNode", opts) + } else { + return rp.VersionManager.V1_0_0.GetContractWithAddress("rocketClaimNode", *address) + } +} diff --git a/bindings/legacy/v1.0.0/rewards/rewards.go b/bindings/legacy/v1.0.0/rewards/rewards.go new file mode 100644 index 000000000..78df8fad5 --- /dev/null +++ b/bindings/legacy/v1.0.0/rewards/rewards.go @@ -0,0 +1,157 @@ +package rewards + +import ( + "fmt" + "math/big" + "sync" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/utils/eth" +) + +// Get whether a claims contract is enabled +func getEnabled(claimsContract *rocketpool.Contract, claimsName string, opts *bind.CallOpts) (bool, error) { + enabled := new(bool) + if err := claimsContract.Call(opts, enabled, "getEnabled"); err != nil { + return false, fmt.Errorf("error getting %s claims contract enabled status: %w", claimsName, err) + } + return *enabled, nil +} + +// Get whether a claimer can make a claim +// Use to check whether a claimer is able to make claims at all +func getClaimPossible(claimsContract *rocketpool.Contract, claimsName string, claimerAddress common.Address, opts *bind.CallOpts) (bool, error) { + claimPossible := new(bool) + if err := claimsContract.Call(opts, claimPossible, "getClaimPossible", claimerAddress); err != nil { + return false, fmt.Errorf("error getting %s claim possible status for %s: %w", claimsName, claimerAddress.Hex(), err) + } + return *claimPossible, nil +} + +// Get the percentage of rewards available to a claimer +func getClaimRewardsPerc(claimsContract *rocketpool.Contract, claimsName string, claimerAddress common.Address, opts *bind.CallOpts) (float64, error) { + claimRewardsPerc := new(*big.Int) + if err := claimsContract.Call(opts, claimRewardsPerc, "getClaimRewardsPerc", claimerAddress); err != nil { + return 0, fmt.Errorf("error getting %s claim rewards percent for %s: %w", claimsName, claimerAddress.Hex(), err) + } + return eth.WeiToEth(*claimRewardsPerc), nil +} + +// Get the total amount of rewards available to a claimer +// Use to check whether a claimer is able to make a claim for the current interval (returns zero if unable) +func getClaimRewardsAmount(claimsContract *rocketpool.Contract, claimsName string, claimerAddress common.Address, opts *bind.CallOpts) (*big.Int, error) { + claimRewardsAmount := new(*big.Int) + if err := claimsContract.Call(opts, claimRewardsAmount, "getClaimRewardsAmount", claimerAddress); err != nil { + return nil, fmt.Errorf("error getting %s claim rewards amount for %s: %w", claimsName, claimerAddress.Hex(), err) + } + return *claimRewardsAmount, nil +} + +// Get the time that the user registered as a claimer +func getClaimingContractUserRegisteredTime(rp *rocketpool.RocketPool, claimsContract string, claimerAddress common.Address, opts *bind.CallOpts, legacyRocketRewardsPoolAddress *common.Address) (time.Time, error) { + rocketRewardsPool, err := getRocketRewardsPool(rp, legacyRocketRewardsPoolAddress, opts) + if err != nil { + return time.Time{}, err + } + claimTime := new(*big.Int) + if err := rocketRewardsPool.Call(opts, claimTime, "getClaimingContractUserRegisteredTime", claimsContract, claimerAddress); err != nil { + return time.Time{}, fmt.Errorf("error getting claims registration time on contract %s for %s: %w", claimsContract, claimerAddress.Hex(), err) + } + return time.Unix((*claimTime).Int64(), 0), nil +} + +// Get the total amount claimed in the current interval by the given claiming contract +func getClaimingContractTotalClaimed(rp *rocketpool.RocketPool, claimsContract string, opts *bind.CallOpts, legacyRocketRewardsPoolAddress *common.Address) (*big.Int, error) { + rocketRewardsPool, err := getRocketRewardsPool(rp, legacyRocketRewardsPoolAddress, opts) + if err != nil { + return nil, err + } + totalClaimed := new(*big.Int) + if err := rocketRewardsPool.Call(opts, totalClaimed, "getClaimingContractTotalClaimed", claimsContract); err != nil { + return nil, fmt.Errorf("error getting total claimed for %s: %w", claimsContract, err) + } + return *totalClaimed, nil +} + +// Estimate the gas of claim +func estimateClaimGas(claimsContract *rocketpool.Contract, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return claimsContract.GetTransactionGasInfo(opts, "claim") +} + +// Claim rewards +func claim(claimsContract *rocketpool.Contract, claimsName string, opts *bind.TransactOpts) (common.Hash, error) { + tx, err := claimsContract.Transact(opts, "claim") + if err != nil { + return common.Hash{}, fmt.Errorf("error claiming %s rewards: %w", claimsName, err) + } + return tx.Hash(), nil +} + +// Get the timestamp that the current rewards interval started +func GetClaimIntervalTimeStart(rp *rocketpool.RocketPool, opts *bind.CallOpts, legacyRocketRewardsPoolAddress *common.Address) (time.Time, error) { + rocketRewardsPool, err := getRocketRewardsPool(rp, legacyRocketRewardsPoolAddress, opts) + if err != nil { + return time.Time{}, err + } + unixTime := new(*big.Int) + if err := rocketRewardsPool.Call(opts, unixTime, "getClaimIntervalTimeStart"); err != nil { + return time.Time{}, fmt.Errorf("error getting claim interval time start: %w", err) + } + return time.Unix((*unixTime).Int64(), 0), nil +} + +// Get the number of seconds in a claim interval +func GetClaimIntervalTime(rp *rocketpool.RocketPool, opts *bind.CallOpts, legacyRocketRewardsPoolAddress *common.Address) (time.Duration, error) { + rocketRewardsPool, err := getRocketRewardsPool(rp, legacyRocketRewardsPoolAddress, opts) + if err != nil { + return 0, err + } + unixTime := new(*big.Int) + if err := rocketRewardsPool.Call(opts, unixTime, "getClaimIntervalTime"); err != nil { + return 0, fmt.Errorf("error getting claim interval time: %w", err) + } + return time.Duration((*unixTime).Int64()) * time.Second, nil +} + +// Get the percent of checkpoint rewards that goes to node operators +func GetNodeOperatorRewardsPercent(rp *rocketpool.RocketPool, opts *bind.CallOpts, legacyRocketRewardsPoolAddress *common.Address) (float64, error) { + rocketRewardsPool, err := getRocketRewardsPool(rp, legacyRocketRewardsPoolAddress, opts) + if err != nil { + return 0, err + } + perc := new(*big.Int) + if err := rocketRewardsPool.Call(opts, perc, "getClaimingContractPerc", "rocketClaimNode"); err != nil { + return 0, fmt.Errorf("error getting node operator rewards percent: %w", err) + } + return eth.WeiToEth(*perc), nil +} + +// Get the percent of checkpoint rewards that goes to ODAO members +func GetTrustedNodeOperatorRewardsPercent(rp *rocketpool.RocketPool, opts *bind.CallOpts, legacyRocketRewardsPoolAddress *common.Address) (float64, error) { + rocketRewardsPool, err := getRocketRewardsPool(rp, legacyRocketRewardsPoolAddress, opts) + if err != nil { + return 0, err + } + perc := new(*big.Int) + if err := rocketRewardsPool.Call(opts, perc, "getClaimingContractPerc", "rocketClaimTrustedNode"); err != nil { + return 0, fmt.Errorf("error getting trusted node operator rewards percent: %w", err) + } + return eth.WeiToEth(*perc), nil +} + +// Get contracts +var rocketRewardsPoolLock sync.Mutex + +func getRocketRewardsPool(rp *rocketpool.RocketPool, address *common.Address, opts *bind.CallOpts) (*rocketpool.Contract, error) { + rocketRewardsPoolLock.Lock() + defer rocketRewardsPoolLock.Unlock() + if address == nil { + return rp.VersionManager.V1_0_0.GetContract("rocketRewardsPool", opts) + } else { + return rp.VersionManager.V1_0_0.GetContractWithAddress("rocketRewardsPool", *address) + } +} diff --git a/bindings/legacy/v1.0.0/rewards/trusted-node.go b/bindings/legacy/v1.0.0/rewards/trusted-node.go new file mode 100644 index 000000000..312da4cbd --- /dev/null +++ b/bindings/legacy/v1.0.0/rewards/trusted-node.go @@ -0,0 +1,132 @@ +package rewards + +import ( + "math/big" + "sync" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/utils/eth" +) + +// Get whether trusted node reward claims are enabled +func GetTrustedNodeClaimsEnabled(rp *rocketpool.RocketPool, opts *bind.CallOpts, legacyRocketClaimTrustedNodeAddress *common.Address) (bool, error) { + rocketClaimTrustedNode, err := getRocketClaimTrustedNode(rp, legacyRocketClaimTrustedNodeAddress, opts) + if err != nil { + return false, err + } + return getEnabled(rocketClaimTrustedNode, "trusted node", opts) +} + +// Get whether a trusted node rewards claimer can claim +func GetTrustedNodeClaimPossible(rp *rocketpool.RocketPool, claimerAddress common.Address, opts *bind.CallOpts, legacyRocketClaimTrustedNodeAddress *common.Address) (bool, error) { + rocketClaimTrustedNode, err := getRocketClaimTrustedNode(rp, legacyRocketClaimTrustedNodeAddress, opts) + if err != nil { + return false, err + } + return getClaimPossible(rocketClaimTrustedNode, "trusted node", claimerAddress, opts) +} + +// Get the percentage of rewards available for a trusted node rewards claimer +func GetTrustedNodeClaimRewardsPerc(rp *rocketpool.RocketPool, claimerAddress common.Address, opts *bind.CallOpts, legacyRocketClaimTrustedNodeAddress *common.Address) (float64, error) { + rocketClaimTrustedNode, err := getRocketClaimTrustedNode(rp, legacyRocketClaimTrustedNodeAddress, opts) + if err != nil { + return 0, err + } + return getClaimRewardsPerc(rocketClaimTrustedNode, "trusted node", claimerAddress, opts) +} + +// Get the total amount of rewards available for a trusted node rewards claimer +func GetTrustedNodeClaimRewardsAmount(rp *rocketpool.RocketPool, claimerAddress common.Address, opts *bind.CallOpts, legacyRocketClaimTrustedNodeAddress *common.Address) (*big.Int, error) { + rocketClaimTrustedNode, err := getRocketClaimTrustedNode(rp, legacyRocketClaimTrustedNodeAddress, opts) + if err != nil { + return nil, err + } + return getClaimRewardsAmount(rocketClaimTrustedNode, "trusted node", claimerAddress, opts) +} + +// Estimate the gas of ClaimTrustedNodeRewards +func EstimateClaimTrustedNodeRewardsGas(rp *rocketpool.RocketPool, opts *bind.TransactOpts, legacyRocketClaimTrustedNodeAddress *common.Address) (rocketpool.GasInfo, error) { + rocketClaimTrustedNode, err := getRocketClaimTrustedNode(rp, legacyRocketClaimTrustedNodeAddress, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return estimateClaimGas(rocketClaimTrustedNode, opts) +} + +// Claim trusted node rewards +func ClaimTrustedNodeRewards(rp *rocketpool.RocketPool, opts *bind.TransactOpts, legacyRocketClaimTrustedNodeAddress *common.Address) (common.Hash, error) { + rocketClaimTrustedNode, err := getRocketClaimTrustedNode(rp, legacyRocketClaimTrustedNodeAddress, nil) + if err != nil { + return common.Hash{}, err + } + return claim(rocketClaimTrustedNode, "trusted node", opts) +} + +// Filters through token claim events and sums the total amount claimed by claimerAddress +func CalculateLifetimeTrustedNodeRewards(rp *rocketpool.RocketPool, claimerAddress common.Address, intervalSize *big.Int, startBlock *big.Int, legacyRocketRewardsPoolAddress *common.Address, legacyRocketClaimTrustedNodeAddress *common.Address, opts *bind.CallOpts) (*big.Int, error) { + // Get contracts + rocketRewardsPool, err := getRocketRewardsPool(rp, legacyRocketRewardsPoolAddress, opts) + if err != nil { + return nil, err + } + rocketClaimTrustedNode, err := getRocketClaimTrustedNode(rp, legacyRocketClaimTrustedNodeAddress, opts) + if err != nil { + return nil, err + } + // Construct a filter query for relevant logs + addressFilter := []common.Address{*rocketRewardsPool.Address} + // RPLTokensClaimed(address clamingContract, address clainingAddress, uint256 amount, uint256 time) + topicFilter := [][]common.Hash{ + {rocketRewardsPool.ABI.Events["RPLTokensClaimed"].ID}, + {common.BytesToHash(rocketClaimTrustedNode.Address.Bytes())}, + {common.BytesToHash(claimerAddress.Bytes())}, + } + + // Get the event logs + logs, err := eth.GetLogs(rp, addressFilter, topicFilter, intervalSize, startBlock, nil, nil) + if err != nil { + return nil, err + } + + // Iterate over the logs and sum the amount + sum := big.NewInt(0) + for _, log := range logs { + values := make(map[string]interface{}) + // Decode the event + if rocketRewardsPool.ABI.Events["RPLTokensClaimed"].Inputs.UnpackIntoMap(values, log.Data) != nil { + return nil, err + } + // Add the amount argument to our sum + amount := values["amount"].(*big.Int) + sum.Add(sum, amount) + } + // Return the result + return sum, nil +} + +// Get the time that the user registered as a claimer +func GetTrustedNodeRegistrationTime(rp *rocketpool.RocketPool, claimerAddress common.Address, opts *bind.CallOpts, legacyRocketRewardsPoolAddress *common.Address) (time.Time, error) { + return getClaimingContractUserRegisteredTime(rp, "rocketClaimTrustedNode", claimerAddress, opts, legacyRocketRewardsPoolAddress) +} + +// Get the total rewards claimed for this claiming contract this interval +func GetTrustedNodeTotalClaimed(rp *rocketpool.RocketPool, opts *bind.CallOpts, legacyRocketRewardsPoolAddress *common.Address) (*big.Int, error) { + return getClaimingContractTotalClaimed(rp, "rocketClaimTrustedNode", opts, legacyRocketRewardsPoolAddress) +} + +// Get contracts +var rocketClaimTrustedNodeLock sync.Mutex + +func getRocketClaimTrustedNode(rp *rocketpool.RocketPool, address *common.Address, opts *bind.CallOpts) (*rocketpool.Contract, error) { + rocketClaimTrustedNodeLock.Lock() + defer rocketClaimTrustedNodeLock.Unlock() + if address == nil { + return rp.VersionManager.V1_0_0.GetContract("rocketClaimTrustedNode", opts) + } else { + return rp.VersionManager.V1_0_0.GetContractWithAddress("rocketClaimTrustedNode", *address) + } +} diff --git a/bindings/legacy/v1.0.0/utils/address_generation.go b/bindings/legacy/v1.0.0/utils/address_generation.go new file mode 100644 index 000000000..c41b38e39 --- /dev/null +++ b/bindings/legacy/v1.0.0/utils/address_generation.go @@ -0,0 +1,75 @@ +package utils + +import ( + "fmt" + "math/big" + "sync" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/rocket-pool/smartnode/bindings/legacy/v1.0.0/minipool" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + rptypes "github.com/rocket-pool/smartnode/bindings/types" +) + +// Combine a node's address and a salt to retreive a new salt compatible with depositing +func GetNodeSalt(nodeAddress common.Address, salt *big.Int) common.Hash { + // Create a new salt by hashing the original and the node address + saltBytes := [32]byte{} + salt.FillBytes(saltBytes[:]) + saltHash := crypto.Keccak256Hash(nodeAddress.Bytes(), saltBytes[:]) + return saltHash +} + +// Precompute the address of a minipool based on the node wallet, deposit type, and unique salt +// If you set minipoolBytecode to nil, this will retrieve it from the contracts using minipool.GetMinipoolBytecode(). +func GenerateAddress(rp *rocketpool.RocketPool, nodeAddress common.Address, depositType rptypes.MinipoolDeposit, salt *big.Int, minipoolBytecode []byte, legacyRocketMinipoolManagerAddress *common.Address, opts *bind.CallOpts) (common.Address, error) { + + // Get dependencies + rocketMinipoolManager, err := getRocketMinipoolManager(rp, legacyRocketMinipoolManagerAddress, opts) + if err != nil { + return common.Address{}, err + } + minipoolAbi, err := rp.GetABI("rocketMinipool", nil) + if err != nil { + return common.Address{}, err + } + + if len(minipoolBytecode) == 0 { + minipoolBytecode, err = minipool.GetMinipoolBytecode(rp, nil, legacyRocketMinipoolManagerAddress) + if err != nil { + return common.Address{}, fmt.Errorf("Error getting minipool bytecode: %w", err) + } + } + + // Create the hash of the minipool constructor call + depositTypeBytes := [32]byte{} + depositTypeBytes[0] = byte(depositType) + packedConstructorArgs, err := minipoolAbi.Pack("", rp.RocketStorageContract.Address, nodeAddress, depositType) + if err != nil { + return common.Address{}, fmt.Errorf("Error creating minipool constructor args: %w", err) + } + + // Get the node salt and initialization data + nodeSalt := GetNodeSalt(nodeAddress, salt) + initData := append(minipoolBytecode, packedConstructorArgs...) + initHash := crypto.Keccak256(initData) + + address := crypto.CreateAddress2(*rocketMinipoolManager.Address, nodeSalt, initHash) + return address, nil + +} + +// Get contracts +var rocketMinipoolManagerLock sync.Mutex + +func getRocketMinipoolManager(rp *rocketpool.RocketPool, address *common.Address, opts *bind.CallOpts) (*rocketpool.Contract, error) { + rocketMinipoolManagerLock.Lock() + defer rocketMinipoolManagerLock.Unlock() + if address == nil { + return rp.VersionManager.V1_0_0.GetContract("rocketMinipoolManager", opts) + } else { + return rp.VersionManager.V1_0_0.GetContractWithAddress("rocketMinipoolManager", *address) + } +} diff --git a/bindings/legacy/v1.1.0-rc1/rewards/rewards.go b/bindings/legacy/v1.1.0-rc1/rewards/rewards.go new file mode 100644 index 000000000..0766e63dd --- /dev/null +++ b/bindings/legacy/v1.1.0-rc1/rewards/rewards.go @@ -0,0 +1,306 @@ +package rewards + +import ( + "fmt" + "math/big" + "reflect" + "sync" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/utils/eth" +) + +// Info for a rewards snapshot event +type RewardsEvent struct { + Index *big.Int + ExecutionBlock *big.Int + ConsensusBlock *big.Int + MerkleRoot common.Hash + MerkleTreeCID string + IntervalsPassed *big.Int + TreasuryRPL *big.Int + TrustedNodeRPL []*big.Int + NodeRPL []*big.Int + NodeETH []*big.Int + IntervalStartTime time.Time + IntervalEndTime time.Time + SubmissionTime time.Time +} + +// Struct for submitting the rewards for a checkpoint +type RewardSubmission struct { + RewardIndex *big.Int `json:"rewardIndex"` + ExecutionBlock *big.Int `json:"executionBlock"` + ConsensusBlock *big.Int `json:"consensusBlock"` + MerkleRoot [32]byte `json:"merkleRoot"` + MerkleTreeCID string `json:"merkleTreeCID"` + IntervalsPassed *big.Int `json:"intervalsPassed"` + TreasuryRPL *big.Int `json:"treasuryRPL"` + TrustedNodeRPL []*big.Int `json:"trustedNodeRPL"` + NodeRPL []*big.Int `json:"nodeRPL"` + NodeETH []*big.Int `json:"nodeETH"` +} + +// Get the index of the active rewards period +func GetRewardIndex(rp *rocketpool.RocketPool, opts *bind.CallOpts, legacyRocketRewardsPoolAddress *common.Address) (*big.Int, error) { + rocketRewardsPool, err := getRocketRewardsPool(rp, legacyRocketRewardsPoolAddress, opts) + if err != nil { + return nil, err + } + index := new(*big.Int) + if err := rocketRewardsPool.Call(opts, index, "getRewardIndex"); err != nil { + return nil, fmt.Errorf("error getting current reward index: %w", err) + } + return *index, nil +} + +// Get the timestamp that the current rewards interval started +func GetClaimIntervalTimeStart(rp *rocketpool.RocketPool, opts *bind.CallOpts, legacyRocketRewardsPoolAddress *common.Address) (time.Time, error) { + rocketRewardsPool, err := getRocketRewardsPool(rp, legacyRocketRewardsPoolAddress, opts) + if err != nil { + return time.Time{}, err + } + unixTime := new(*big.Int) + if err := rocketRewardsPool.Call(opts, unixTime, "getClaimIntervalTimeStart"); err != nil { + return time.Time{}, fmt.Errorf("error getting claim interval time start: %w", err) + } + return time.Unix((*unixTime).Int64(), 0), nil +} + +// Get the number of seconds in a claim interval +func GetClaimIntervalTime(rp *rocketpool.RocketPool, opts *bind.CallOpts, legacyRocketRewardsPoolAddress *common.Address) (time.Duration, error) { + rocketRewardsPool, err := getRocketRewardsPool(rp, legacyRocketRewardsPoolAddress, opts) + if err != nil { + return 0, err + } + unixTime := new(*big.Int) + if err := rocketRewardsPool.Call(opts, unixTime, "getClaimIntervalTime"); err != nil { + return 0, fmt.Errorf("error getting claim interval time: %w", err) + } + return time.Duration((*unixTime).Int64()) * time.Second, nil +} + +// Get the percent of checkpoint rewards that goes to node operators +func GetNodeOperatorRewardsPercent(rp *rocketpool.RocketPool, opts *bind.CallOpts, legacyRocketRewardsPoolAddress *common.Address) (*big.Int, error) { + rocketRewardsPool, err := getRocketRewardsPool(rp, legacyRocketRewardsPoolAddress, opts) + if err != nil { + return nil, err + } + perc := new(*big.Int) + if err := rocketRewardsPool.Call(opts, perc, "getClaimingContractPerc", "rocketClaimNode"); err != nil { + return nil, fmt.Errorf("error getting node operator rewards percent: %w", err) + } + return *perc, nil +} + +// Get the percent of checkpoint rewards that goes to ODAO members +func GetTrustedNodeOperatorRewardsPercent(rp *rocketpool.RocketPool, opts *bind.CallOpts, legacyRocketRewardsPoolAddress *common.Address) (*big.Int, error) { + rocketRewardsPool, err := getRocketRewardsPool(rp, legacyRocketRewardsPoolAddress, opts) + if err != nil { + return nil, err + } + perc := new(*big.Int) + if err := rocketRewardsPool.Call(opts, perc, "getClaimingContractPerc", "rocketClaimTrustedNode"); err != nil { + return nil, fmt.Errorf("error getting trusted node operator rewards percent: %w", err) + } + return *perc, nil +} + +// Get the percent of checkpoint rewards that goes to the PDAO +func GetProtocolDaoRewardsPercent(rp *rocketpool.RocketPool, opts *bind.CallOpts, legacyRocketRewardsPoolAddress *common.Address) (*big.Int, error) { + rocketRewardsPool, err := getRocketRewardsPool(rp, legacyRocketRewardsPoolAddress, opts) + if err != nil { + return nil, err + } + perc := new(*big.Int) + if err := rocketRewardsPool.Call(opts, perc, "getClaimingContractPerc", "rocketClaimDAO"); err != nil { + return nil, fmt.Errorf("error getting protocol DAO rewards percent: %w", err) + } + return *perc, nil +} + +// Get the amount of RPL rewards that will be provided to node operators +func GetPendingRPLRewards(rp *rocketpool.RocketPool, opts *bind.CallOpts, legacyRocketRewardsPoolAddress *common.Address) (*big.Int, error) { + rocketRewardsPool, err := getRocketRewardsPool(rp, legacyRocketRewardsPoolAddress, opts) + if err != nil { + return nil, err + } + rewards := new(*big.Int) + if err := rocketRewardsPool.Call(opts, rewards, "getPendingRPLRewards"); err != nil { + return nil, fmt.Errorf("error getting pending RPL rewards: %w", err) + } + return *rewards, nil +} + +// Get the amount of ETH rewards that will be provided to node operators +func GetPendingETHRewards(rp *rocketpool.RocketPool, opts *bind.CallOpts, legacyRocketRewardsPoolAddress *common.Address) (*big.Int, error) { + rocketRewardsPool, err := getRocketRewardsPool(rp, legacyRocketRewardsPoolAddress, opts) + if err != nil { + return nil, err + } + rewards := new(*big.Int) + if err := rocketRewardsPool.Call(opts, rewards, "getPendingETHRewards"); err != nil { + return nil, fmt.Errorf("error getting pending ETH rewards: %w", err) + } + return *rewards, nil +} + +// Estimate the gas for submiting a Merkle Tree-based snapshot for a rewards interval +func EstimateSubmitRewardSnapshotGas(rp *rocketpool.RocketPool, submission RewardSubmission, opts *bind.TransactOpts, legacyRocketRewardsPoolAddress *common.Address) (rocketpool.GasInfo, error) { + rocketRewardsPool, err := getRocketRewardsPool(rp, legacyRocketRewardsPoolAddress, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketRewardsPool.GetTransactionGasInfo(opts, "submitRewardSnapshot", submission) +} + +// Submit a Merkle Tree-based snapshot for a rewards interval +func SubmitRewardSnapshot(rp *rocketpool.RocketPool, submission RewardSubmission, opts *bind.TransactOpts, legacyRocketRewardsPoolAddress *common.Address) (common.Hash, error) { + rocketRewardsPool, err := getRocketRewardsPool(rp, legacyRocketRewardsPoolAddress, nil) + if err != nil { + return common.Hash{}, err + } + tx, err := rocketRewardsPool.Transact(opts, "submitRewardSnapshot", submission) + if err != nil { + return common.Hash{}, fmt.Errorf("error submitting rewards snapshot: %w", err) + } + return tx.Hash(), nil +} + +// Get the event info for a rewards snapshot +func GetRewardSnapshotEvent(rp *rocketpool.RocketPool, index uint64, intervalSize *big.Int, startBlock *big.Int, endBlock *big.Int, legacyRocketRewardsPoolAddress *common.Address, opts *bind.CallOpts) (RewardsEvent, error) { + // Get contracts + rocketRewardsPool, err := getRocketRewardsPool(rp, legacyRocketRewardsPoolAddress, opts) + if err != nil { + return RewardsEvent{}, err + } + + // Construct a filter query for relevant logs + indexBig := big.NewInt(0).SetUint64(index) + indexBytes := [32]byte{} + indexBig.FillBytes(indexBytes[:]) + addressFilter := []common.Address{*rocketRewardsPool.Address} + topicFilter := [][]common.Hash{{rocketRewardsPool.ABI.Events["RewardSnapshot"].ID}, {indexBytes}} + + // Get the event logs + logs, err := eth.GetLogs(rp, addressFilter, topicFilter, intervalSize, startBlock, endBlock, nil) + if err != nil { + return RewardsEvent{}, err + } + + // Get the log info + values := make(map[string]interface{}) + if len(logs) == 0 { + return RewardsEvent{}, fmt.Errorf("reward snapshot for interval %d not found", index) + } + err = rocketRewardsPool.ABI.Events["RewardSnapshot"].Inputs.UnpackIntoMap(values, logs[0].Data) + if err != nil { + return RewardsEvent{}, err + } + + // Get the decoded data + submissionPrototype := RewardSubmission{} + submissionType := reflect.TypeOf(submissionPrototype) + submission := reflect.ValueOf(values["submission"]).Convert(submissionType).Interface().(RewardSubmission) + eventIntervalStartTime := values["intervalStartTime"].(*big.Int) + eventIntervalEndTime := values["intervalEndTime"].(*big.Int) + submissionTime := values["time"].(*big.Int) + eventData := RewardsEvent{ + Index: indexBig, + ExecutionBlock: submission.ExecutionBlock, + ConsensusBlock: submission.ConsensusBlock, + IntervalsPassed: submission.IntervalsPassed, + TreasuryRPL: submission.TreasuryRPL, + TrustedNodeRPL: submission.TrustedNodeRPL, + NodeRPL: submission.NodeRPL, + NodeETH: submission.NodeETH, + MerkleRoot: common.BytesToHash(submission.MerkleRoot[:]), + MerkleTreeCID: submission.MerkleTreeCID, + IntervalStartTime: time.Unix(eventIntervalStartTime.Int64(), 0), + IntervalEndTime: time.Unix(eventIntervalEndTime.Int64(), 0), + SubmissionTime: time.Unix(submissionTime.Int64(), 0), + } + + return eventData, nil + +} + +// Get the event info for a rewards snapshot +func GetRewardSnapshotEventWithUpgrades(rp *rocketpool.RocketPool, index uint64, intervalSize *big.Int, startBlock *big.Int, endBlock *big.Int, rocketRewardsPoolAddresses []common.Address, opts *bind.CallOpts) (bool, RewardsEvent, error) { + + if len(rocketRewardsPoolAddresses) == 0 { + return false, RewardsEvent{}, fmt.Errorf("rocketRewardsPoolAddresses must have at least one element.") + } + + // Get contracts + rocketRewardsPool, err := getRocketRewardsPool(rp, &rocketRewardsPoolAddresses[0], opts) + if err != nil { + return false, RewardsEvent{}, err + } + + // Construct a filter query for relevant logs + indexBig := big.NewInt(0).SetUint64(index) + indexBytes := [32]byte{} + indexBig.FillBytes(indexBytes[:]) + addressFilter := rocketRewardsPoolAddresses + topicFilter := [][]common.Hash{{rocketRewardsPool.ABI.Events["RewardSnapshot"].ID}, {indexBytes}} + + // Get the event logs + logs, err := eth.GetLogs(rp, addressFilter, topicFilter, intervalSize, startBlock, endBlock, nil) + if err != nil { + return false, RewardsEvent{}, err + } + + // Get the log info + values := make(map[string]interface{}) + if len(logs) == 0 { + return false, RewardsEvent{}, nil + } + err = rocketRewardsPool.ABI.Events["RewardSnapshot"].Inputs.UnpackIntoMap(values, logs[0].Data) + if err != nil { + return false, RewardsEvent{}, err + } + + // Get the decoded data + submissionPrototype := RewardSubmission{} + submissionType := reflect.TypeOf(submissionPrototype) + submission := reflect.ValueOf(values["submission"]).Convert(submissionType).Interface().(RewardSubmission) + eventIntervalStartTime := values["intervalStartTime"].(*big.Int) + eventIntervalEndTime := values["intervalEndTime"].(*big.Int) + submissionTime := values["time"].(*big.Int) + eventData := RewardsEvent{ + Index: indexBig, + ExecutionBlock: submission.ExecutionBlock, + ConsensusBlock: submission.ConsensusBlock, + IntervalsPassed: submission.IntervalsPassed, + TreasuryRPL: submission.TreasuryRPL, + TrustedNodeRPL: submission.TrustedNodeRPL, + NodeRPL: submission.NodeRPL, + NodeETH: submission.NodeETH, + MerkleRoot: common.BytesToHash(submission.MerkleRoot[:]), + MerkleTreeCID: submission.MerkleTreeCID, + IntervalStartTime: time.Unix(eventIntervalStartTime.Int64(), 0), + IntervalEndTime: time.Unix(eventIntervalEndTime.Int64(), 0), + SubmissionTime: time.Unix(submissionTime.Int64(), 0), + } + + return true, eventData, nil + +} + +// Get contracts +var rocketRewardsPoolLock sync.Mutex + +func getRocketRewardsPool(rp *rocketpool.RocketPool, address *common.Address, opts *bind.CallOpts) (*rocketpool.Contract, error) { + rocketRewardsPoolLock.Lock() + defer rocketRewardsPoolLock.Unlock() + if address == nil { + return rp.VersionManager.V1_1_0_RC1.GetContract("rocketRewardsPool", opts) + } else { + return rp.VersionManager.V1_1_0_RC1.GetContractWithAddress("rocketRewardsPool", *address) + } +} diff --git a/bindings/legacy/v1.1.0/minipool/factory.go b/bindings/legacy/v1.1.0/minipool/factory.go new file mode 100644 index 000000000..7bd47e3a9 --- /dev/null +++ b/bindings/legacy/v1.1.0/minipool/factory.go @@ -0,0 +1,37 @@ +package minipool + +import ( + "fmt" + "sync" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + + "github.com/rocket-pool/smartnode/bindings/rocketpool" +) + +// Get the CreationCode binary for the RocketMinipool contract that will be created by node deposits +func GetMinipoolBytecode(rp *rocketpool.RocketPool, opts *bind.CallOpts, legacyRocketMinipoolFactoryAddress *common.Address) ([]byte, error) { + rocketMinipoolFactory, err := getRocketMinipoolFactory(rp, legacyRocketMinipoolFactoryAddress, opts) + if err != nil { + return []byte{}, err + } + bytecode := new([]byte) + if err := rocketMinipoolFactory.Call(opts, bytecode, "getMinipoolBytecode"); err != nil { + return []byte{}, fmt.Errorf("error getting minipool contract bytecode: %w", err) + } + return *bytecode, nil +} + +// Get contracts +var rocketMinipoolFactoryLock sync.Mutex + +func getRocketMinipoolFactory(rp *rocketpool.RocketPool, address *common.Address, opts *bind.CallOpts) (*rocketpool.Contract, error) { + rocketMinipoolFactoryLock.Lock() + defer rocketMinipoolFactoryLock.Unlock() + if address == nil { + return rp.VersionManager.V1_1_0.GetContract("rocketMinipoolFactory", opts) + } else { + return rp.VersionManager.V1_1_0.GetContractWithAddress("rocketMinipoolFactory", *address) + } +} diff --git a/bindings/legacy/v1.1.0/minipool/queue.go b/bindings/legacy/v1.1.0/minipool/queue.go new file mode 100644 index 000000000..fe1ed496a --- /dev/null +++ b/bindings/legacy/v1.1.0/minipool/queue.go @@ -0,0 +1,302 @@ +package minipool + +import ( + "fmt" + "math/big" + "sync" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "golang.org/x/sync/errgroup" + + "github.com/rocket-pool/smartnode/bindings/minipool" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/storage" + rptypes "github.com/rocket-pool/smartnode/bindings/types" +) + +// Minipool queue lengths +type QueueLengths struct { + Total uint64 + FullDeposit uint64 + HalfDeposit uint64 + EmptyDeposit uint64 +} + +// Minipool queue capacity +type QueueCapacity struct { + Total *big.Int + Effective *big.Int + NextMinipool *big.Int +} + +// Minipools queue status details +type QueueDetails struct { + Position uint64 +} + +// Get minipool queue lengths +func GetQueueLengths(rp *rocketpool.RocketPool, opts *bind.CallOpts, legacyRocketMinipoolQueueAddress *common.Address) (QueueLengths, error) { + + // Data + var wg errgroup.Group + var total uint64 + var fullDeposit uint64 + var halfDeposit uint64 + var emptyDeposit uint64 + + // Load data + wg.Go(func() error { + var err error + total, err = GetQueueTotalLength(rp, opts, legacyRocketMinipoolQueueAddress) + return err + }) + wg.Go(func() error { + var err error + fullDeposit, err = GetQueueLength(rp, rptypes.Full, opts, legacyRocketMinipoolQueueAddress) + return err + }) + wg.Go(func() error { + var err error + halfDeposit, err = GetQueueLength(rp, rptypes.Half, opts, legacyRocketMinipoolQueueAddress) + return err + }) + wg.Go(func() error { + var err error + emptyDeposit, err = GetQueueLength(rp, rptypes.Empty, opts, legacyRocketMinipoolQueueAddress) + return err + }) + + // Wait for data + if err := wg.Wait(); err != nil { + return QueueLengths{}, err + } + + // Return + return QueueLengths{ + Total: total, + FullDeposit: fullDeposit, + HalfDeposit: halfDeposit, + EmptyDeposit: emptyDeposit, + }, nil + +} + +// Get minipool queue capacity +func GetQueueCapacity(rp *rocketpool.RocketPool, opts *bind.CallOpts, legacyRocketMinipoolQueueAddress *common.Address) (QueueCapacity, error) { + + // Data + var wg errgroup.Group + var total *big.Int + var effective *big.Int + var nextMinipool *big.Int + + // Load data + wg.Go(func() error { + var err error + total, err = GetQueueTotalCapacity(rp, opts, legacyRocketMinipoolQueueAddress) + return err + }) + wg.Go(func() error { + var err error + effective, err = GetQueueEffectiveCapacity(rp, opts, legacyRocketMinipoolQueueAddress) + return err + }) + wg.Go(func() error { + var err error + nextMinipool, err = GetQueueNextCapacity(rp, opts, legacyRocketMinipoolQueueAddress) + return err + }) + + // Wait for data + if err := wg.Wait(); err != nil { + return QueueCapacity{}, err + } + + // Return + return QueueCapacity{ + Total: total, + Effective: effective, + NextMinipool: nextMinipool, + }, nil + +} + +// Get the total length of the minipool queue +func GetQueueTotalLength(rp *rocketpool.RocketPool, opts *bind.CallOpts, legacyRocketMinipoolQueueAddress *common.Address) (uint64, error) { + rocketMinipoolQueue, err := getRocketMinipoolQueue(rp, legacyRocketMinipoolQueueAddress, opts) + if err != nil { + return 0, err + } + length := new(*big.Int) + if err := rocketMinipoolQueue.Call(opts, length, "getTotalLength"); err != nil { + return 0, fmt.Errorf("error getting minipool queue total length: %w", err) + } + return (*length).Uint64(), nil +} + +// Get the length of a single minipool queue +func GetQueueLength(rp *rocketpool.RocketPool, depositType rptypes.MinipoolDeposit, opts *bind.CallOpts, legacyRocketMinipoolQueueAddress *common.Address) (uint64, error) { + rocketMinipoolQueue, err := getRocketMinipoolQueue(rp, legacyRocketMinipoolQueueAddress, opts) + if err != nil { + return 0, err + } + length := new(*big.Int) + if err := rocketMinipoolQueue.Call(opts, length, "getLength", depositType); err != nil { + return 0, fmt.Errorf("error getting minipool queue length for deposit type %d: %w", depositType, err) + } + return (*length).Uint64(), nil +} + +// Get the total capacity of the minipool queue +func GetQueueTotalCapacity(rp *rocketpool.RocketPool, opts *bind.CallOpts, legacyRocketMinipoolQueueAddress *common.Address) (*big.Int, error) { + rocketMinipoolQueue, err := getRocketMinipoolQueue(rp, legacyRocketMinipoolQueueAddress, opts) + if err != nil { + return nil, err + } + capacity := new(*big.Int) + if err := rocketMinipoolQueue.Call(opts, capacity, "getTotalCapacity"); err != nil { + return nil, fmt.Errorf("error getting minipool queue total capacity: %w", err) + } + return *capacity, nil +} + +// Get the total effective capacity of the minipool queue (used in node demand calculation) +func GetQueueEffectiveCapacity(rp *rocketpool.RocketPool, opts *bind.CallOpts, legacyRocketMinipoolQueueAddress *common.Address) (*big.Int, error) { + rocketMinipoolQueue, err := getRocketMinipoolQueue(rp, legacyRocketMinipoolQueueAddress, opts) + if err != nil { + return nil, err + } + capacity := new(*big.Int) + if err := rocketMinipoolQueue.Call(opts, capacity, "getEffectiveCapacity"); err != nil { + return nil, fmt.Errorf("error getting minipool queue effective capacity: %w", err) + } + return *capacity, nil +} + +// Get the capacity of the next minipool in the queue +func GetQueueNextCapacity(rp *rocketpool.RocketPool, opts *bind.CallOpts, legacyRocketMinipoolQueueAddress *common.Address) (*big.Int, error) { + rocketMinipoolQueue, err := getRocketMinipoolQueue(rp, legacyRocketMinipoolQueueAddress, opts) + if err != nil { + return nil, err + } + capacity := new(*big.Int) + if err := rocketMinipoolQueue.Call(opts, capacity, "getNextCapacity"); err != nil { + return nil, fmt.Errorf("error getting minipool queue next item capacity: %w", err) + } + return *capacity, nil +} + +// Get Queue position details of a minipool +func GetQueueDetails(rp *rocketpool.RocketPool, mp minipool.Minipool, opts *bind.CallOpts, legacyRocketMinipoolQueueAddress *common.Address) (QueueDetails, error) { + position, err := GetQueuePositionOfMinipool(rp, mp, opts, legacyRocketMinipoolQueueAddress) + if err != nil { + return QueueDetails{}, err + } + + // Return + return QueueDetails{ + Position: position, + }, nil +} + +// Get a minipools position in queue (1-indexed). 0 means it is currently not queued. +func GetQueuePositionOfMinipool(rp *rocketpool.RocketPool, mp minipool.Minipool, opts *bind.CallOpts, legacyRocketMinipoolQueueAddress *common.Address) (uint64, error) { + depositType, err := mp.GetDepositType(opts) + if err != nil { + return 0, fmt.Errorf("error getting deposit type: %w", err) + } + if depositType == rptypes.None { + return 0, fmt.Errorf("Minipool address %s has no deposit type", mp.GetAddress()) + } + + queryIndex := func(key string) (uint64, error) { + index, err := storage.GetAddressQueueIndexOf(rp, opts, crypto.Keccak256Hash([]byte(key)), mp.GetAddress()) + if err != nil { + return 0, fmt.Errorf("error getting queue index for address %s: %w", mp.GetAddress(), err) + } + return uint64(index + 1), nil + } + + position := uint64(0) + + // half cleared first + if depositType != rptypes.Half { + position, err = GetQueueLength(rp, rptypes.Half, opts, legacyRocketMinipoolQueueAddress) + if err != nil { + return 0, fmt.Errorf("error getting queue length of type %s: %w", rptypes.MinipoolDepositTypes[rptypes.Empty], err) + } + } else { + return queryIndex("minipools.available.half") + } + + // full deposits next + if depositType != rptypes.Full { + length, err := GetQueueLength(rp, rptypes.Full, opts, legacyRocketMinipoolQueueAddress) + if err != nil { + return 0, fmt.Errorf("error getting queue length of type %s: %w", rptypes.MinipoolDepositTypes[rptypes.Empty], err) + } + position += length + } else { + index, err := queryIndex("minipools.available.full") + if err != nil || index == 0 { + return 0, err + } + return position + index, nil + } + + // must be empty type now + index, err := queryIndex("minipools.available.empty") + if err != nil || index == 0 { + return 0, err + } + return position + index, nil +} + +// Get the minipool at the specified position in queue (0-indexed). +func GetQueueMinipoolAtPosition(rp *rocketpool.RocketPool, position uint64, opts *bind.CallOpts, legacyRocketMinipoolQueueAddress *common.Address) (minipool.Minipool, error) { + totalLength, err := GetQueueTotalLength(rp, opts, legacyRocketMinipoolQueueAddress) + if err != nil { + return nil, fmt.Errorf("error getting total queue length: %w", err) + } + if position >= totalLength { + return nil, fmt.Errorf("error getting index %d beyond queue length %d", position, totalLength) + } + lengths, err := GetQueueLengths(rp, opts, legacyRocketMinipoolQueueAddress) + if err != nil { + return nil, fmt.Errorf("error getting queue lengths: %w", err) + } + + getMinipool := func(key string) (minipool.Minipool, error) { + pos := big.NewInt(int64(position)) + address, err := storage.GetAddressQueueItem(rp, opts, crypto.Keccak256Hash([]byte(key)), pos) + if err != nil { + return nil, fmt.Errorf("error getting address in queue at position %d: %w", position, err) + } + return minipool.NewMinipool(rp, address, opts) + } + + if position < lengths.HalfDeposit { + return getMinipool("minipools.available.half") + } + position -= lengths.HalfDeposit + if position < lengths.FullDeposit { + return getMinipool("minipools.available.full") + } + position -= lengths.FullDeposit + return getMinipool("minipools.available.empty") +} + +// Get contracts +var rocketMinipoolQueueLock sync.Mutex + +func getRocketMinipoolQueue(rp *rocketpool.RocketPool, address *common.Address, opts *bind.CallOpts) (*rocketpool.Contract, error) { + rocketMinipoolQueueLock.Lock() + defer rocketMinipoolQueueLock.Unlock() + if address == nil { + return rp.VersionManager.V1_1_0.GetContract("rocketMinipoolQueue", opts) + } else { + return rp.VersionManager.V1_1_0.GetContractWithAddress("rocketMinipoolQueue", *address) + } +} diff --git a/bindings/legacy/v1.1.0/network/prices.go b/bindings/legacy/v1.1.0/network/prices.go new file mode 100644 index 000000000..6cb4b509d --- /dev/null +++ b/bindings/legacy/v1.1.0/network/prices.go @@ -0,0 +1,99 @@ +package network + +import ( + "fmt" + "math/big" + "sync" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + + "github.com/rocket-pool/smartnode/bindings/rocketpool" +) + +// Get the block number which network prices are current for +func GetPricesBlock(rp *rocketpool.RocketPool, opts *bind.CallOpts, legacyRocketNetworkPricesAddress *common.Address) (uint64, error) { + rocketNetworkPrices, err := getRocketNetworkPrices(rp, legacyRocketNetworkPricesAddress, opts) + if err != nil { + return 0, err + } + pricesBlock := new(*big.Int) + if err := rocketNetworkPrices.Call(opts, pricesBlock, "getPricesBlock"); err != nil { + return 0, fmt.Errorf("error getting network prices block: %w", err) + } + return (*pricesBlock).Uint64(), nil +} + +// Get the current network RPL price in ETH +func GetRPLPrice(rp *rocketpool.RocketPool, opts *bind.CallOpts, legacyRocketNetworkPricesAddress *common.Address) (*big.Int, error) { + rocketNetworkPrices, err := getRocketNetworkPrices(rp, legacyRocketNetworkPricesAddress, opts) + if err != nil { + return nil, err + } + rplPrice := new(*big.Int) + if err := rocketNetworkPrices.Call(opts, rplPrice, "getRPLPrice"); err != nil { + return nil, fmt.Errorf("error getting network RPL price: %w", err) + } + return *rplPrice, nil +} + +// Estimate the gas of SubmitPrices +func EstimateSubmitPricesGas(rp *rocketpool.RocketPool, block uint64, rplPrice *big.Int, effectiveRplStake *big.Int, opts *bind.TransactOpts, legacyRocketNetworkPricesAddress *common.Address) (rocketpool.GasInfo, error) { + rocketNetworkPrices, err := getRocketNetworkPrices(rp, legacyRocketNetworkPricesAddress, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketNetworkPrices.GetTransactionGasInfo(opts, "submitPrices", big.NewInt(int64(block)), rplPrice, effectiveRplStake) +} + +// Submit network prices and total effective RPL stake for an epoch +func SubmitPrices(rp *rocketpool.RocketPool, block uint64, rplPrice, effectiveRplStake *big.Int, opts *bind.TransactOpts, legacyRocketNetworkPricesAddress *common.Address) (common.Hash, error) { + rocketNetworkPrices, err := getRocketNetworkPrices(rp, legacyRocketNetworkPricesAddress, nil) + if err != nil { + return common.Hash{}, err + } + tx, err := rocketNetworkPrices.Transact(opts, "submitPrices", big.NewInt(int64(block)), rplPrice, effectiveRplStake) + if err != nil { + return common.Hash{}, fmt.Errorf("error submitting network prices: %w", err) + } + return tx.Hash(), nil +} + +// Check if the network is currently in consensus about the RPL price, or if it is still reaching consensus +func InConsensus(rp *rocketpool.RocketPool, opts *bind.CallOpts, legacyRocketNetworkPricesAddress *common.Address) (bool, error) { + rocketNetworkPrices, err := getRocketNetworkPrices(rp, legacyRocketNetworkPricesAddress, opts) + if err != nil { + return false, err + } + isInConsensus := new(bool) + if err := rocketNetworkPrices.Call(opts, isInConsensus, "inConsensus"); err != nil { + return false, fmt.Errorf("error getting consensus status: %w", err) + } + return *isInConsensus, nil +} + +// Returns the latest block number that oracles should be reporting prices for +func GetLatestReportablePricesBlock(rp *rocketpool.RocketPool, opts *bind.CallOpts, legacyRocketNetworkPricesAddress *common.Address) (*big.Int, error) { + rocketNetworkPrices, err := getRocketNetworkPrices(rp, legacyRocketNetworkPricesAddress, opts) + if err != nil { + return nil, err + } + latestReportableBlock := new(*big.Int) + if err := rocketNetworkPrices.Call(opts, latestReportableBlock, "getLatestReportableBlock"); err != nil { + return nil, fmt.Errorf("error getting latest reportable block: %w", err) + } + return *latestReportableBlock, nil +} + +// Get contracts +var rocketNetworkPricesLock sync.Mutex + +func getRocketNetworkPrices(rp *rocketpool.RocketPool, address *common.Address, opts *bind.CallOpts) (*rocketpool.Contract, error) { + rocketNetworkPricesLock.Lock() + defer rocketNetworkPricesLock.Unlock() + if address == nil { + return rp.VersionManager.V1_1_0.GetContract("rocketNetworkPrices", opts) + } else { + return rp.VersionManager.V1_1_0.GetContractWithAddress("rocketNetworkPrices", *address) + } +} diff --git a/bindings/legacy/v1.1.0/node/deposit.go b/bindings/legacy/v1.1.0/node/deposit.go new file mode 100644 index 000000000..d0ecad67a --- /dev/null +++ b/bindings/legacy/v1.1.0/node/deposit.go @@ -0,0 +1,64 @@ +package node + +import ( + "fmt" + "math/big" + "sync" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + + "github.com/rocket-pool/smartnode/bindings/rocketpool" + rptypes "github.com/rocket-pool/smartnode/bindings/types" + "github.com/rocket-pool/smartnode/bindings/utils/eth" +) + +// Estimate the gas of Deposit +func EstimateDepositGas(rp *rocketpool.RocketPool, minimumNodeFee float64, validatorPubkey rptypes.ValidatorPubkey, validatorSignature rptypes.ValidatorSignature, depositDataRoot common.Hash, salt *big.Int, expectedMinipoolAddress common.Address, opts *bind.TransactOpts, legacyRocketNodeDepositAddress *common.Address) (rocketpool.GasInfo, error) { + rocketNodeDeposit, err := getRocketNodeDeposit(rp, legacyRocketNodeDepositAddress, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketNodeDeposit.GetTransactionGasInfo(opts, "deposit", eth.EthToWei(minimumNodeFee), validatorPubkey[:], validatorSignature[:], depositDataRoot, salt, expectedMinipoolAddress) +} + +// Make a node deposit +func Deposit(rp *rocketpool.RocketPool, minimumNodeFee float64, validatorPubkey rptypes.ValidatorPubkey, validatorSignature rptypes.ValidatorSignature, depositDataRoot common.Hash, salt *big.Int, expectedMinipoolAddress common.Address, opts *bind.TransactOpts, legacyRocketNodeDepositAddress *common.Address) (*types.Transaction, error) { + rocketNodeDeposit, err := getRocketNodeDeposit(rp, legacyRocketNodeDepositAddress, nil) + if err != nil { + return nil, err + } + tx, err := rocketNodeDeposit.Transact(opts, "deposit", eth.EthToWei(minimumNodeFee), validatorPubkey[:], validatorSignature[:], depositDataRoot, salt, expectedMinipoolAddress) + if err != nil { + return nil, fmt.Errorf("error making node deposit: %w", err) + } + return tx, nil +} + +// Get the type of a deposit based on the amount +func GetDepositType(rp *rocketpool.RocketPool, amount *big.Int, opts *bind.CallOpts, legacyRocketNodeDepositAddress *common.Address) (rptypes.MinipoolDeposit, error) { + rocketNodeDeposit, err := getRocketNodeDeposit(rp, legacyRocketNodeDepositAddress, opts) + if err != nil { + return rptypes.Empty, err + } + + depositType := new(uint8) + if err := rocketNodeDeposit.Call(opts, depositType, "getDepositType", amount); err != nil { + return rptypes.Empty, fmt.Errorf("error getting deposit type: %w", err) + } + return rptypes.MinipoolDeposit(*depositType), nil +} + +// Get contracts +var rocketNodeDepositLock sync.Mutex + +func getRocketNodeDeposit(rp *rocketpool.RocketPool, address *common.Address, opts *bind.CallOpts) (*rocketpool.Contract, error) { + rocketNodeDepositLock.Lock() + defer rocketNodeDepositLock.Unlock() + if address == nil { + return rp.VersionManager.V1_1_0.GetContract("rocketNodeDeposit", opts) + } else { + return rp.VersionManager.V1_1_0.GetContractWithAddress("rocketNodeDeposit", *address) + } +} diff --git a/bindings/legacy/v1.1.0/node/staking.go b/bindings/legacy/v1.1.0/node/staking.go new file mode 100644 index 000000000..338bb7030 --- /dev/null +++ b/bindings/legacy/v1.1.0/node/staking.go @@ -0,0 +1,211 @@ +package node + +import ( + "fmt" + "math/big" + "sync" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + + "github.com/rocket-pool/smartnode/bindings/rocketpool" +) + +// Get the version of the Node Staking contract +func GetNodeStakingVersion(rp *rocketpool.RocketPool, opts *bind.CallOpts, legacyRocketNodeStakingAddress *common.Address) (uint8, error) { + rocketNodeStaking, err := getRocketNodeStaking(rp, legacyRocketNodeStakingAddress, opts) + if err != nil { + return 0, err + } + version := new(uint8) + if err := rocketNodeStaking.Call(opts, version, "version"); err != nil { + return 0, fmt.Errorf("error getting node staking version: %w", err) + } + return *version, nil +} + +// Get the total RPL staked in the network +func GetTotalRPLStake(rp *rocketpool.RocketPool, opts *bind.CallOpts, legacyRocketNodeStakingAddress *common.Address) (*big.Int, error) { + rocketNodeStaking, err := getRocketNodeStaking(rp, legacyRocketNodeStakingAddress, opts) + if err != nil { + return nil, err + } + totalRplStake := new(*big.Int) + if err := rocketNodeStaking.Call(opts, totalRplStake, "getTotalRPLStake"); err != nil { + return nil, fmt.Errorf("error getting total network RPL stake: %w", err) + } + return *totalRplStake, nil +} + +// Get the effective RPL staked in the network +func GetTotalEffectiveRPLStake(rp *rocketpool.RocketPool, opts *bind.CallOpts, legacyRocketNodeStakingAddress *common.Address) (*big.Int, error) { + rocketNodeStaking, err := getRocketNodeStaking(rp, legacyRocketNodeStakingAddress, opts) + if err != nil { + return nil, err + } + totalEffectiveRplStake := new(*big.Int) + if err := rocketNodeStaking.Call(opts, totalEffectiveRplStake, "getTotalEffectiveRPLStake"); err != nil { + return nil, fmt.Errorf("error getting effective network RPL stake: %w", err) + } + return *totalEffectiveRplStake, nil +} + +// Get a node's RPL stake +func GetNodeRPLStake(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts, legacyRocketNodeStakingAddress *common.Address) (*big.Int, error) { + rocketNodeStaking, err := getRocketNodeStaking(rp, legacyRocketNodeStakingAddress, opts) + if err != nil { + return nil, err + } + nodeRplStake := new(*big.Int) + if err := rocketNodeStaking.Call(opts, nodeRplStake, "getNodeRPLStake", nodeAddress); err != nil { + return nil, fmt.Errorf("error getting total node RPL stake: %w", err) + } + return *nodeRplStake, nil +} + +// Get a node's effective RPL stake +func GetNodeEffectiveRPLStake(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts, legacyRocketNodeStakingAddress *common.Address) (*big.Int, error) { + rocketNodeStaking, err := getRocketNodeStaking(rp, legacyRocketNodeStakingAddress, opts) + if err != nil { + return nil, err + } + nodeEffectiveRplStakeWrapper := new(*big.Int) + if err := rocketNodeStaking.Call(opts, nodeEffectiveRplStakeWrapper, "getNodeEffectiveRPLStake", nodeAddress); err != nil { + return nil, fmt.Errorf("error getting effective node RPL stake: %w", err) + } + + minimumStake, err := GetNodeMinimumRPLStake(rp, nodeAddress, opts, legacyRocketNodeStakingAddress) + if err != nil { + return nil, fmt.Errorf("error getting minimum node RPL stake to verify effective stake: %w", err) + } + + nodeEffectiveRplStake := *nodeEffectiveRplStakeWrapper + if nodeEffectiveRplStake.Cmp(minimumStake) == -1 { + // Effective stake should be zero if it's less than the minimum RPL stake + return big.NewInt(0), nil + } + + return nodeEffectiveRplStake, nil +} + +// Get a node's minimum RPL stake to collateralize their minipools +func GetNodeMinimumRPLStake(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts, legacyRocketNodeStakingAddress *common.Address) (*big.Int, error) { + rocketNodeStaking, err := getRocketNodeStaking(rp, legacyRocketNodeStakingAddress, opts) + if err != nil { + return nil, err + } + nodeMinimumRplStake := new(*big.Int) + if err := rocketNodeStaking.Call(opts, nodeMinimumRplStake, "getNodeMinimumRPLStake", nodeAddress); err != nil { + return nil, fmt.Errorf("error getting minimum node RPL stake: %w", err) + } + return *nodeMinimumRplStake, nil +} + +// Get a node's maximum RPL stake to collateralize their minipools +func GetNodeMaximumRPLStake(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts, legacyRocketNodeStakingAddress *common.Address) (*big.Int, error) { + rocketNodeStaking, err := getRocketNodeStaking(rp, legacyRocketNodeStakingAddress, opts) + if err != nil { + return nil, err + } + nodeMaximumRplStake := new(*big.Int) + if err := rocketNodeStaking.Call(opts, nodeMaximumRplStake, "getNodeMaximumRPLStake", nodeAddress); err != nil { + return nil, fmt.Errorf("error getting maximum node RPL stake: %w", err) + } + return *nodeMaximumRplStake, nil +} + +// Get the time a node last staked RPL +func GetNodeRPLStakedTime(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts, legacyRocketNodeStakingAddress *common.Address) (uint64, error) { + rocketNodeStaking, err := getRocketNodeStaking(rp, legacyRocketNodeStakingAddress, opts) + if err != nil { + return 0, err + } + nodeRplStakedTime := new(*big.Int) + if err := rocketNodeStaking.Call(opts, nodeRplStakedTime, "getNodeRPLStakedTime", nodeAddress); err != nil { + return 0, fmt.Errorf("error getting node RPL staked time: %w", err) + } + return (*nodeRplStakedTime).Uint64(), nil +} + +// Get a node's minipool limit based on RPL stake +func GetNodeMinipoolLimit(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts, legacyRocketNodeStakingAddress *common.Address) (uint64, error) { + rocketNodeStaking, err := getRocketNodeStaking(rp, legacyRocketNodeStakingAddress, opts) + if err != nil { + return 0, err + } + minipoolLimit := new(*big.Int) + if err := rocketNodeStaking.Call(opts, minipoolLimit, "getNodeMinipoolLimit", nodeAddress); err != nil { + return 0, fmt.Errorf("error getting node minipool limit: %w", err) + } + return (*minipoolLimit).Uint64(), nil +} + +// Estimate the gas of Stake +func EstimateStakeGas(rp *rocketpool.RocketPool, rplAmount *big.Int, opts *bind.TransactOpts, legacyRocketNodeStakingAddress *common.Address) (rocketpool.GasInfo, error) { + rocketNodeStaking, err := getRocketNodeStaking(rp, legacyRocketNodeStakingAddress, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketNodeStaking.GetTransactionGasInfo(opts, "stakeRPL", rplAmount) +} + +// Stake RPL +func StakeRPL(rp *rocketpool.RocketPool, rplAmount *big.Int, opts *bind.TransactOpts, legacyRocketNodeStakingAddress *common.Address) (common.Hash, error) { + rocketNodeStaking, err := getRocketNodeStaking(rp, legacyRocketNodeStakingAddress, nil) + if err != nil { + return common.Hash{}, err + } + tx, err := rocketNodeStaking.Transact(opts, "stakeRPL", rplAmount) + if err != nil { + return common.Hash{}, fmt.Errorf("error staking RPL: %w", err) + } + return tx.Hash(), nil +} + +// Estimate the gas of WithdrawRPL +func EstimateWithdrawRPLGas(rp *rocketpool.RocketPool, rplAmount *big.Int, opts *bind.TransactOpts, legacyRocketNodeStakingAddress *common.Address) (rocketpool.GasInfo, error) { + rocketNodeStaking, err := getRocketNodeStaking(rp, legacyRocketNodeStakingAddress, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketNodeStaking.GetTransactionGasInfo(opts, "withdrawRPL", rplAmount) +} + +// Withdraw staked RPL +func WithdrawRPL(rp *rocketpool.RocketPool, rplAmount *big.Int, opts *bind.TransactOpts, legacyRocketNodeStakingAddress *common.Address) (common.Hash, error) { + rocketNodeStaking, err := getRocketNodeStaking(rp, legacyRocketNodeStakingAddress, nil) + if err != nil { + return common.Hash{}, err + } + tx, err := rocketNodeStaking.Transact(opts, "withdrawRPL", rplAmount) + if err != nil { + return common.Hash{}, fmt.Errorf("error withdrawing staked RPL: %w", err) + } + return tx.Hash(), nil +} + +// Calculate total effective RPL stake +func CalculateTotalEffectiveRPLStake(rp *rocketpool.RocketPool, offset, limit, rplPrice *big.Int, opts *bind.CallOpts, legacyRocketNodeStakingAddress *common.Address) (*big.Int, error) { + rocketNodeStaking, err := getRocketNodeStaking(rp, legacyRocketNodeStakingAddress, opts) + if err != nil { + return nil, err + } + totalEffectiveRplStake := new(*big.Int) + if err := rocketNodeStaking.Call(opts, totalEffectiveRplStake, "calculateTotalEffectiveRPLStake", offset, limit, rplPrice); err != nil { + return nil, fmt.Errorf("error getting total effective RPL stake: %w", err) + } + return *totalEffectiveRplStake, nil +} + +// Get contracts +var rocketNodeStakingLock sync.Mutex + +func getRocketNodeStaking(rp *rocketpool.RocketPool, address *common.Address, opts *bind.CallOpts) (*rocketpool.Contract, error) { + rocketNodeStakingLock.Lock() + defer rocketNodeStakingLock.Unlock() + if address == nil { + return rp.VersionManager.V1_1_0.GetContract("rocketNodeStaking", opts) + } else { + return rp.VersionManager.V1_1_0.GetContractWithAddress("rocketNodeStaking", *address) + } +} diff --git a/bindings/legacy/v1.1.0/utils/address_generation.go b/bindings/legacy/v1.1.0/utils/address_generation.go new file mode 100644 index 000000000..31b79aa26 --- /dev/null +++ b/bindings/legacy/v1.1.0/utils/address_generation.go @@ -0,0 +1,71 @@ +package utils + +import ( + "fmt" + "math/big" + "sync" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + v110_minipool "github.com/rocket-pool/smartnode/bindings/legacy/v1.1.0/minipool" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + rptypes "github.com/rocket-pool/smartnode/bindings/types" +) + +// Combine a node's address and a salt to retreive a new salt compatible with depositing +func GetNodeSalt(nodeAddress common.Address, salt *big.Int) common.Hash { + // Create a new salt by hashing the original and the node address + saltBytes := [32]byte{} + salt.FillBytes(saltBytes[:]) + saltHash := crypto.Keccak256Hash(nodeAddress.Bytes(), saltBytes[:]) + return saltHash +} + +// Precompute the address of a minipool based on the node wallet, deposit type, and unique salt +// If you set minipoolBytecode to nil, this will retrieve it from the contracts using minipool.GetMinipoolBytecode(). +func GenerateAddress(rp *rocketpool.RocketPool, nodeAddress common.Address, depositType rptypes.MinipoolDeposit, salt *big.Int, minipoolBytecode []byte, opts *bind.CallOpts, legacyRocketMinipoolFactoryAddress *common.Address) (common.Address, error) { + + // Get dependencies + rocketMinipoolFactory, err := getRocketMinipoolFactory(rp, opts) + if err != nil { + return common.Address{}, err + } + minipoolAbi, err := rp.GetABI("rocketMinipool", opts) + if err != nil { + return common.Address{}, err + } + + if len(minipoolBytecode) == 0 { + minipoolBytecode, err = v110_minipool.GetMinipoolBytecode(rp, nil, legacyRocketMinipoolFactoryAddress) + if err != nil { + return common.Address{}, fmt.Errorf("Error getting minipool bytecode: %w", err) + } + } + + // Create the hash of the minipool constructor call + depositTypeBytes := [32]byte{} + depositTypeBytes[0] = byte(depositType) + packedConstructorArgs, err := minipoolAbi.Pack("", rp.RocketStorageContract.Address, nodeAddress, depositType) + if err != nil { + return common.Address{}, fmt.Errorf("Error creating minipool constructor args: %w", err) + } + + // Get the node salt and initialization data + nodeSalt := GetNodeSalt(nodeAddress, salt) + initData := append(minipoolBytecode, packedConstructorArgs...) + initHash := crypto.Keccak256(initData) + + address := crypto.CreateAddress2(*rocketMinipoolFactory.Address, nodeSalt, initHash) + return address, nil + +} + +// Get contracts +var rocketMinipoolFactoryLock sync.Mutex + +func getRocketMinipoolFactory(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*rocketpool.Contract, error) { + rocketMinipoolFactoryLock.Lock() + defer rocketMinipoolFactoryLock.Unlock() + return rp.GetContract("rocketMinipoolFactory", opts) +} diff --git a/bindings/legacy/v1.2.0/network/balances.go b/bindings/legacy/v1.2.0/network/balances.go new file mode 100644 index 000000000..717a514a2 --- /dev/null +++ b/bindings/legacy/v1.2.0/network/balances.go @@ -0,0 +1,138 @@ +package network + +import ( + "fmt" + "math/big" + "sync" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/utils/eth" +) + +// Get the block number which network balances are current for +func GetBalancesBlock(rp *rocketpool.RocketPool, opts *bind.CallOpts, legacyRocketNetworkBalancesAddress *common.Address) (uint64, error) { + rocketNetworkBalances, err := getRocketNetworkBalances(rp, legacyRocketNetworkBalancesAddress, opts) + if err != nil { + return 0, err + } + balancesBlock := new(*big.Int) + if err := rocketNetworkBalances.Call(opts, balancesBlock, "getBalancesBlock"); err != nil { + return 0, fmt.Errorf("Could not get network balances block: %w", err) + } + return (*balancesBlock).Uint64(), nil +} + +// Get the block number which network balances are current for +func GetBalancesBlockRaw(rp *rocketpool.RocketPool, opts *bind.CallOpts, legacyRocketNetworkBalancesAddress *common.Address) (*big.Int, error) { + rocketNetworkBalances, err := getRocketNetworkBalances(rp, legacyRocketNetworkBalancesAddress, opts) + if err != nil { + return nil, err + } + balancesBlock := new(*big.Int) + if err := rocketNetworkBalances.Call(opts, balancesBlock, "getBalancesBlock"); err != nil { + return nil, fmt.Errorf("Could not get network balances block: %w", err) + } + return *balancesBlock, nil +} + +// Get the current network total ETH balance +func GetTotalETHBalance(rp *rocketpool.RocketPool, opts *bind.CallOpts, legacyRocketNetworkBalancesAddress *common.Address) (*big.Int, error) { + rocketNetworkBalances, err := getRocketNetworkBalances(rp, legacyRocketNetworkBalancesAddress, opts) + if err != nil { + return nil, err + } + totalEthBalance := new(*big.Int) + if err := rocketNetworkBalances.Call(opts, totalEthBalance, "getTotalETHBalance"); err != nil { + return nil, fmt.Errorf("Could not get network total ETH balance: %w", err) + } + return *totalEthBalance, nil +} + +// Get the current network staking ETH balance +func GetStakingETHBalance(rp *rocketpool.RocketPool, opts *bind.CallOpts, legacyRocketNetworkBalancesAddress *common.Address) (*big.Int, error) { + rocketNetworkBalances, err := getRocketNetworkBalances(rp, legacyRocketNetworkBalancesAddress, opts) + if err != nil { + return nil, err + } + stakingEthBalance := new(*big.Int) + if err := rocketNetworkBalances.Call(opts, stakingEthBalance, "getStakingETHBalance"); err != nil { + return nil, fmt.Errorf("Could not get network staking ETH balance: %w", err) + } + return *stakingEthBalance, nil +} + +// Get the current network total rETH supply +func GetTotalRETHSupply(rp *rocketpool.RocketPool, opts *bind.CallOpts, legacyRocketNetworkBalancesAddress *common.Address) (*big.Int, error) { + rocketNetworkBalances, err := getRocketNetworkBalances(rp, legacyRocketNetworkBalancesAddress, opts) + if err != nil { + return nil, err + } + totalRethSupply := new(*big.Int) + if err := rocketNetworkBalances.Call(opts, totalRethSupply, "getTotalRETHSupply"); err != nil { + return nil, fmt.Errorf("Could not get network total rETH supply: %w", err) + } + return *totalRethSupply, nil +} + +// Get the current network ETH utilization rate +func GetETHUtilizationRate(rp *rocketpool.RocketPool, opts *bind.CallOpts, legacyRocketNetworkBalancesAddress *common.Address) (float64, error) { + rocketNetworkBalances, err := getRocketNetworkBalances(rp, legacyRocketNetworkBalancesAddress, opts) + if err != nil { + return 0, err + } + ethUtilizationRate := new(*big.Int) + if err := rocketNetworkBalances.Call(opts, ethUtilizationRate, "getETHUtilizationRate"); err != nil { + return 0, fmt.Errorf("Could not get network ETH utilization rate: %w", err) + } + return eth.WeiToEth(*ethUtilizationRate), nil +} + +// Estimate the gas of SubmitBalances +func EstimateSubmitBalancesGas(rp *rocketpool.RocketPool, block uint64, totalEth, stakingEth, rethSupply *big.Int, opts *bind.TransactOpts, legacyRocketNetworkBalancesAddress *common.Address) (rocketpool.GasInfo, error) { + rocketNetworkBalances, err := getRocketNetworkBalances(rp, legacyRocketNetworkBalancesAddress, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketNetworkBalances.GetTransactionGasInfo(opts, "submitBalances", big.NewInt(int64(block)), totalEth, stakingEth, rethSupply) +} + +// Submit network balances for an epoch +func SubmitBalances(rp *rocketpool.RocketPool, block uint64, totalEth, stakingEth, rethSupply *big.Int, opts *bind.TransactOpts, legacyRocketNetworkBalancesAddress *common.Address) (common.Hash, error) { + rocketNetworkBalances, err := getRocketNetworkBalances(rp, legacyRocketNetworkBalancesAddress, nil) + if err != nil { + return common.Hash{}, err + } + tx, err := rocketNetworkBalances.Transact(opts, "submitBalances", big.NewInt(int64(block)), totalEth, stakingEth, rethSupply) + if err != nil { + return common.Hash{}, fmt.Errorf("Could not submit network balances: %w", err) + } + return tx.Hash(), nil +} + +// Returns the latest block number that oracles should be reporting balances for +func GetLatestReportableBalancesBlock(rp *rocketpool.RocketPool, opts *bind.CallOpts, legacyRocketNetworkBalancesAddress *common.Address) (*big.Int, error) { + rocketNetworkBalances, err := getRocketNetworkBalances(rp, legacyRocketNetworkBalancesAddress, opts) + if err != nil { + return nil, err + } + latestReportableBlock := new(*big.Int) + if err := rocketNetworkBalances.Call(opts, latestReportableBlock, "getLatestReportableBlock"); err != nil { + return nil, fmt.Errorf("Could not get latest reportable block: %w", err) + } + return *latestReportableBlock, nil +} + +// Get contracts +var rocketNetworkBalancesLock sync.Mutex + +func getRocketNetworkBalances(rp *rocketpool.RocketPool, address *common.Address, opts *bind.CallOpts) (*rocketpool.Contract, error) { + rocketNetworkBalancesLock.Lock() + defer rocketNetworkBalancesLock.Unlock() + if address == nil { + return rp.VersionManager.V1_2_0.GetContract("rocketNetworkBalances", opts) + } + return rp.VersionManager.V1_2_0.GetContractWithAddress("rocketNetworkBalances", *address) +} diff --git a/bindings/legacy/v1.2.0/network/prices.go b/bindings/legacy/v1.2.0/network/prices.go new file mode 100644 index 000000000..11856b966 --- /dev/null +++ b/bindings/legacy/v1.2.0/network/prices.go @@ -0,0 +1,85 @@ +package network + +import ( + "fmt" + "math/big" + "sync" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + + "github.com/rocket-pool/smartnode/bindings/rocketpool" +) + +// Get the block number which network prices are current for +func GetPricesBlock(rp *rocketpool.RocketPool, opts *bind.CallOpts, legacyRocketNetworkPricesAddress *common.Address) (uint64, error) { + rocketNetworkPrices, err := getRocketNetworkPrices(rp, legacyRocketNetworkPricesAddress, opts) + if err != nil { + return 0, err + } + pricesBlock := new(*big.Int) + if err := rocketNetworkPrices.Call(opts, pricesBlock, "getPricesBlock"); err != nil { + return 0, fmt.Errorf("Could not get network prices block: %w", err) + } + return (*pricesBlock).Uint64(), nil +} + +// Get the current network RPL price in ETH +func GetRPLPrice(rp *rocketpool.RocketPool, opts *bind.CallOpts, legacyRocketNetworkPricesAddress *common.Address) (*big.Int, error) { + rocketNetworkPrices, err := getRocketNetworkPrices(rp, legacyRocketNetworkPricesAddress, opts) + if err != nil { + return nil, err + } + rplPrice := new(*big.Int) + if err := rocketNetworkPrices.Call(opts, rplPrice, "getRPLPrice"); err != nil { + return nil, fmt.Errorf("Could not get network RPL price: %w", err) + } + return *rplPrice, nil +} + +// Estimate the gas of SubmitPrices +func EstimateSubmitPricesGas(rp *rocketpool.RocketPool, block uint64, rplPrice *big.Int, opts *bind.TransactOpts, legacyRocketNetworkPricesAddress *common.Address) (rocketpool.GasInfo, error) { + rocketNetworkPrices, err := getRocketNetworkPrices(rp, legacyRocketNetworkPricesAddress, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketNetworkPrices.GetTransactionGasInfo(opts, "submitPrices", big.NewInt(int64(block)), rplPrice) +} + +// Submit network prices and total effective RPL stake for an epoch +func SubmitPrices(rp *rocketpool.RocketPool, block uint64, rplPrice *big.Int, opts *bind.TransactOpts, legacyRocketNetworkPricesAddress *common.Address) (common.Hash, error) { + rocketNetworkPrices, err := getRocketNetworkPrices(rp, legacyRocketNetworkPricesAddress, nil) + if err != nil { + return common.Hash{}, err + } + tx, err := rocketNetworkPrices.Transact(opts, "submitPrices", big.NewInt(int64(block)), rplPrice) + if err != nil { + return common.Hash{}, fmt.Errorf("Could not submit network prices: %w", err) + } + return tx.Hash(), nil +} + +// Returns the latest block number that oracles should be reporting prices for +func GetLatestReportablePricesBlock(rp *rocketpool.RocketPool, opts *bind.CallOpts, legacyRocketNetworkPricesAddress *common.Address) (*big.Int, error) { + rocketNetworkPrices, err := getRocketNetworkPrices(rp, legacyRocketNetworkPricesAddress, opts) + if err != nil { + return nil, err + } + latestReportableBlock := new(*big.Int) + if err := rocketNetworkPrices.Call(opts, latestReportableBlock, "getLatestReportableBlock"); err != nil { + return nil, fmt.Errorf("Could not get latest reportable block: %w", err) + } + return *latestReportableBlock, nil +} + +// Get contracts +var rocketNetworkPricesLock sync.Mutex + +func getRocketNetworkPrices(rp *rocketpool.RocketPool, address *common.Address, opts *bind.CallOpts) (*rocketpool.Contract, error) { + rocketNetworkPricesLock.Lock() + defer rocketNetworkPricesLock.Unlock() + if address == nil { + return rp.VersionManager.V1_2_0.GetContract("rocketNetworkPrices", opts) + } + return rp.VersionManager.V1_2_0.GetContractWithAddress("rocketNetworkPrices", *address) +} diff --git a/bindings/legacy/v1.3.1/node/deposit.go b/bindings/legacy/v1.3.1/node/deposit.go new file mode 100644 index 000000000..48c77234a --- /dev/null +++ b/bindings/legacy/v1.3.1/node/deposit.go @@ -0,0 +1,98 @@ +package node + +import ( + "fmt" + "math/big" + "sync" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + rptypes "github.com/rocket-pool/smartnode/bindings/types" + "github.com/rocket-pool/smartnode/bindings/utils/eth" +) + +// Estimate the gas of Deposit +func EstimateDepositGas(rp *rocketpool.RocketPool, bondAmount *big.Int, minimumNodeFee float64, validatorPubkey rptypes.ValidatorPubkey, validatorSignature rptypes.ValidatorSignature, depositDataRoot common.Hash, salt *big.Int, expectedMinipoolAddress common.Address, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketNodeDeposit, err := getRocketNodeDeposit(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketNodeDeposit.GetTransactionGasInfo(opts, "deposit", bondAmount, eth.EthToWei(minimumNodeFee), validatorPubkey[:], validatorSignature[:], depositDataRoot, salt, expectedMinipoolAddress) +} + +// Make a node deposit +func Deposit(rp *rocketpool.RocketPool, bondAmount *big.Int, minimumNodeFee float64, validatorPubkey rptypes.ValidatorPubkey, validatorSignature rptypes.ValidatorSignature, depositDataRoot common.Hash, salt *big.Int, expectedMinipoolAddress common.Address, opts *bind.TransactOpts) (*types.Transaction, error) { + rocketNodeDeposit, err := getRocketNodeDeposit(rp, nil) + if err != nil { + return nil, err + } + tx, err := rocketNodeDeposit.Transact(opts, "deposit", bondAmount, eth.EthToWei(minimumNodeFee), validatorPubkey[:], validatorSignature[:], depositDataRoot, salt, expectedMinipoolAddress) + if err != nil { + return nil, fmt.Errorf("error making node deposit: %w", err) + } + return tx, nil +} + +// Estimate the gas of DepositWithCredit +func EstimateDepositWithCreditGas(rp *rocketpool.RocketPool, bondAmount *big.Int, minimumNodeFee float64, validatorPubkey rptypes.ValidatorPubkey, validatorSignature rptypes.ValidatorSignature, depositDataRoot common.Hash, salt *big.Int, expectedMinipoolAddress common.Address, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketNodeDeposit, err := getRocketNodeDeposit(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketNodeDeposit.GetTransactionGasInfo(opts, "depositWithCredit", bondAmount, eth.EthToWei(minimumNodeFee), validatorPubkey[:], validatorSignature[:], depositDataRoot, salt, expectedMinipoolAddress) +} + +// Make a node deposit by using the credit balance +func DepositWithCredit(rp *rocketpool.RocketPool, bondAmount *big.Int, minimumNodeFee float64, validatorPubkey rptypes.ValidatorPubkey, validatorSignature rptypes.ValidatorSignature, depositDataRoot common.Hash, salt *big.Int, expectedMinipoolAddress common.Address, opts *bind.TransactOpts) (*types.Transaction, error) { + rocketNodeDeposit, err := getRocketNodeDeposit(rp, nil) + if err != nil { + return nil, err + } + tx, err := rocketNodeDeposit.Transact(opts, "depositWithCredit", bondAmount, eth.EthToWei(minimumNodeFee), validatorPubkey[:], validatorSignature[:], depositDataRoot, salt, expectedMinipoolAddress) + if err != nil { + return nil, fmt.Errorf("error making node deposit with credit: %w", err) + } + return tx, nil +} + +// Get contracts +var rocketNodeDepositLock sync.Mutex + +func getRocketNodeDeposit(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*rocketpool.Contract, error) { + rocketNodeDepositLock.Lock() + defer rocketNodeDepositLock.Unlock() + return rp.GetContract("rocketNodeDeposit", opts) +} + +// Estimate the gas of AssignDeposits +func EstimateAssignDepositsGas(rp *rocketpool.RocketPool, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketDepositPool, err := getRocketDepositPool(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketDepositPool.GetTransactionGasInfo(opts, "assignDeposits") +} + +// Assign deposits +func AssignDeposits(rp *rocketpool.RocketPool, opts *bind.TransactOpts) (common.Hash, error) { + rocketDepositPool, err := getRocketDepositPool(rp, nil) + if err != nil { + return common.Hash{}, err + } + tx, err := rocketDepositPool.Transact(opts, "assignDeposits") + if err != nil { + return common.Hash{}, fmt.Errorf("error assigning deposits: %w", err) + } + return tx.Hash(), nil +} + +// Get contracts +var rocketDepositPoolLock sync.Mutex + +func getRocketDepositPool(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*rocketpool.Contract, error) { + rocketDepositPoolLock.Lock() + defer rocketDepositPoolLock.Unlock() + return rp.GetContract("rocketDepositPool", opts) +} diff --git a/bindings/megapool/beacon-state-verifier.go b/bindings/megapool/beacon-state-verifier.go new file mode 100644 index 000000000..bb1ebc078 --- /dev/null +++ b/bindings/megapool/beacon-state-verifier.go @@ -0,0 +1,54 @@ +package megapool + +import ( + "fmt" + "math/big" + "sync" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/rocket-pool/smartnode/bindings/rocketpool" +) + +func verifyValidator(rp *rocketpool.RocketPool, proof ValidatorProof, opts *bind.CallOpts) (bool, error) { + beaconStateVerifier, err := getBeaconStateVerifier(rp, opts) + if err != nil { + return false, err + } + verifiedValidator := new(bool) + if err := beaconStateVerifier.Call(opts, verifiedValidator, "verifyValidator"); err != nil { + return false, fmt.Errorf("error verifying validatorindex %d at slot %d: %w", proof.ValidatorIndex, proof.Slot, err) + } + return *verifiedValidator, nil +} + +func verifyExit(rp *rocketpool.RocketPool, validatorIndex *big.Int, withdrawableEpoch *big.Int, slot uint64, proof [][32]byte, opts *bind.CallOpts) (bool, error) { + beaconStateVerifier, err := getBeaconStateVerifier(rp, opts) + if err != nil { + return false, err + } + verifiedExit := new(bool) + if err := beaconStateVerifier.Call(opts, verifiedExit, "verifyExit", validatorIndex, withdrawableEpoch, slot, proof); err != nil { + return false, fmt.Errorf("error verifying exit of validator index %d at slot %d: %w", validatorIndex.Int64(), slot, err) + } + return *verifiedExit, nil +} + +func verifyWithdrawal(rp *rocketpool.RocketPool, validatorIndex *big.Int, withdrawalSlot uint64, withdrawalNum *big.Int, withdrawal Withdrawal, slot uint64, proof [][32]byte, opts *bind.CallOpts) (bool, error) { + beaconStateVerifier, err := getBeaconStateVerifier(rp, opts) + if err != nil { + return false, err + } + verifiedWithdrawal := new(bool) + if err := beaconStateVerifier.Call(opts, verifiedWithdrawal, "verifyWithdrawal", validatorIndex, withdrawalSlot, withdrawalNum, withdrawal, slot, proof, opts); err != nil { + return false, fmt.Errorf("error verifying withdrawal of validator index %d at withdrawalSlot %d: %w", validatorIndex.Int64(), withdrawalSlot, err) + } + return *verifiedWithdrawal, nil +} + +var BeaconStateVerifierLock sync.Mutex + +func getBeaconStateVerifier(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*rocketpool.Contract, error) { + BeaconStateVerifierLock.Lock() + defer BeaconStateVerifierLock.Unlock() + return rp.GetContract("beaconStateVerifierLock", opts) +} diff --git a/bindings/megapool/factory.go b/bindings/megapool/factory.go new file mode 100644 index 000000000..40f7649ef --- /dev/null +++ b/bindings/megapool/factory.go @@ -0,0 +1,59 @@ +package megapool + +import ( + "fmt" + "math/big" + "sync" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/rocket-pool/smartnode/bindings/rocketpool" +) + +// Get a megapool deployment state +func GetMegapoolDeployed(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (bool, error) { + rocketMegapoolFactory, err := getRocketMegapoolFactory(rp, opts) + if err != nil { + return false, err + } + deployed := new(bool) + if err := rocketMegapoolFactory.Call(opts, deployed, "getMegapoolDeployed", nodeAddress); err != nil { + return false, fmt.Errorf("error getting megapool deployed for node %s: %w", nodeAddress, err) + } + return *deployed, nil +} + +// Get a megapool expected address +func GetMegapoolExpectedAddress(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (common.Address, error) { + rocketMegapoolFactory, err := getRocketMegapoolFactory(rp, opts) + if err != nil { + return common.Address{}, err + } + expectedAddress := common.Address{} + if err := rocketMegapoolFactory.Call(opts, &expectedAddress, "getExpectedAddress", nodeAddress); err != nil { + return common.Address{}, fmt.Errorf("error getting megapool expected address for node %s: %w", nodeAddress, err) + } + return expectedAddress, nil +} + +// Get a megapool delegate expiration block +func GetMegapoolDelegateExpiry(rp *rocketpool.RocketPool, delegateAddress common.Address, opts *bind.CallOpts) (uint64, error) { + rocketMegapoolFactory, err := getRocketMegapoolFactory(rp, opts) + if err != nil { + return 0, err + } + expiryBlock := new(*big.Int) + if err := rocketMegapoolFactory.Call(opts, expiryBlock, "getDelegateExpiry", delegateAddress); err != nil { + return 0, fmt.Errorf("error getting expiration block for delegate address %s: %w", delegateAddress, err) + } + return (*expiryBlock).Uint64(), nil +} + +// Get contracts +var rocketMegapoolFactoryLock sync.Mutex + +func getRocketMegapoolFactory(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*rocketpool.Contract, error) { + rocketMegapoolFactoryLock.Lock() + defer rocketMegapoolFactoryLock.Unlock() + return rp.GetContract("rocketMegapoolFactory", opts) +} diff --git a/bindings/megapool/megapool-contract.go b/bindings/megapool/megapool-contract.go new file mode 100644 index 000000000..b50473007 --- /dev/null +++ b/bindings/megapool/megapool-contract.go @@ -0,0 +1,585 @@ +package megapool + +import ( + "context" + "fmt" + "math/big" + "sync" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + rptypes "github.com/rocket-pool/smartnode/bindings/types" + "golang.org/x/sync/errgroup" +) + +type ValidatorProof struct { + Slot uint64 + ValidatorIndex *big.Int + Pubkey []byte + WithdrawalCredentials [32]byte + Witnesses [][32]byte +} + +type Withdrawal struct { + Index uint64 + ValidatorIndex *big.Int + WithdrawalCredentials [20]byte + AmountInGwei uint64 +} + +type RewardSplit struct { + NodeRewards *big.Int `abi:"nodeRewards"` + VoterRewards *big.Int `abi:"voterRewards"` + RethRewards *big.Int `abi:"rethRewards"` +} + +type MegapoolV1 interface { + Megapool +} + +type ValidatorInfo struct { + PubKey []byte `abi:"pubKey"` + LastAssignmentTime uint32 `abi:"lastAssignmentTime"` + LastRequestedValue uint32 `abi:"lastRequestedValue"` + LastRequestedBond uint32 `abi:"lastRequestedBond"` + DepositValue uint32 `abi:"depositValue"` + Staked bool `abi:"staked"` + Exited bool `abi:"exited"` + InQueue bool `abi:"inQueue"` + InPrestake bool `abi:"inPrestake"` + ExpressUsed bool `abi:"expressUsed"` + Dissolved bool `abi:"dissolved"` + Exiting bool `abi:"exiting"` + ValidatorIndex uint64 `abi:"validatorIndex"` + ExitBalance uint64 `abi:"exitBalance"` + WithdrawableEpoch uint64 `abi:"withdrawableEpoch"` +} + +type ValidatorInfoFromGlobalIndex struct { + ValidatorInfo ValidatorInfo `abi:"validatorInfo"` + MegapoolAddress common.Address `abi:"megapoolAddress"` + ValidatorId uint32 `abi:"validatorId"` +} + +// Megapool contract +type megapoolV1 struct { + Address common.Address + Version uint8 + Contract *rocketpool.Contract + RocketPool *rocketpool.RocketPool +} + +const ( + megapoolV1EncodedAbi string = "eJztWktv2zgQ/isLn4Nike320Fse7iJoWmTtZPcQFAYljR0iNKnlw45Q7H/foS3rLYuKpECt99Q6JIffDGe+GQ71+H1CuODRWhg1+bgkTMHZhPLQaPz5+B3/G8ALBJkhDZITdh+FMPk4Mfj7/PcPk7MJJ2v7B4KCuMbfOj/h37P2sjTFf8qSviUTvsCKhEKwS8GDGQTGR+HJfNgAArH7/vgKXoOnb7gvgaifWcXOZ6ilccC0IYwGRAt5E7yxkn8ddr5Qiq74Sah6Df8YMKehKlVKsM1J6Drlgx3rb+cOmuKkN1L0herTOFFUlPLVCWg61+T5Z8ujM9gSGagrRnDm8LpxEcBFj/ptBI73KVCCfupTnusBYA7QknqmhjNyJj++46LWf9JNSRiy6A5Qio5wojA6Fo9TlCYavhhNPMooDttD4yGJiMcymiwN9zUV3AFdlpcXR4k5A3BX6HwyPFDN+BzBpdJ9wnzDUAyaIEDuis8gt1Ozoa0vpysrfcXBeTtJsN5aL6DSWBsK21cdY2snS6z8v3lrHdHSbp8BmIoOIJTgo6RRxnewr+7HiS0ux5O8P9ABJYw/jPwV6AuckNHjKg7ZhijM2qzSTl293iKLL7KIreAFzVE5TCgiKNtLGAuWuKoe49lNX0IqiZ10yYT/PBaL3RKlkypqbOC+2ro3CCSohjRIkkklSIehHiHZDuhYTNSpFhsM1Qxw0rhoai4EB6X/pvopkGRrs8I0FP5TM74P7+vg4UhP6B4UyCsSUk3YWAw2EhLtrUDJqnTDl6KgkS/WIXoIr97UizRk6CU03meI0t32wzXFcRYsQ7rdZ/E1bnVfuuYmjbZGKTNbCyqdFgPdpMSc1loIFsxCUe0MwhOCpatVoTe0G21cBYUmodsqyv+Mi+eWy+4wf1ic7WGGNvM8qPZYg3Jv2900ubZi3bIisaVBZFs1ZZprlmC3viSMcB9es3xbQcxlqi0IwbrF+PqXGdYsoA+dxzlqQVZwSyKM73fFmC8xlDYhXhB6Is0kvbArCQGGOCWsISvvuKOaPA9Dg/YkPIz84727I/62MAqmez+/p/YQHBw2T6Upfd8Z79mJVGslzJFXiTYSXIXk8khMZddEk5kQuvoYDm0W2A5y0e2Y7ZrDbOEQZw5SFBPaZWFsusdvmbWhFGJZsi5OydpXaLqM7HXuB7UumztaqByQqYyvZl0dk8eLlSIq+lpKb58Udod5/msFrRcIUefnO4DZ905v+B9boC1yQ8rIlSYu5YHROP8nipLShDpsELRuU8vd1yOlm3BvfTgJuKTUWurykNE9+NtFnauX9BR0uctJizyatYBLsNaU9WVXR2F44VZQFhB7e3W8Jmn1Lo6UmshJy7bsXWIYZ9yAVHb4zW/mXV6RSz0xwYJrYFgk64zqSXus+ZW2JBCLoE4C2z/7HrZ7CFeSBL1/KFLScCnFuh/V3vTzhKl+sm94PtBOn0dVwynd4HX1zbhnnfAafYtbKf0QIj1Uq+WSGwhjHvGf6x8Acx42DJ/sXk2SwBlFX/uAZ/dMAQ3t7bwLFE+/p/eS5RJ2D3Fjs1TiiG7ABrJVswEWPPd6UmGM5HpAbaKnatgys9A4qCSOTDZvMvTr4DkwhNzzJoL5DzTANyg=" +) + +// The decoded ABI for megapools +var megapoolV1Abi *abi.ABI + +// Create new megapool contract +func NewMegaPoolV1(rp *rocketpool.RocketPool, address common.Address, opts *bind.CallOpts) (Megapool, error) { + + var contract *rocketpool.Contract + var err error + if megapoolV1Abi == nil { + // Get contract + contract, err = createMegapoolContractFromEncodedAbi(rp, address, megapoolV1EncodedAbi) + } else { + contract, err = createMegapoolContractFromAbi(rp, address, megapoolV1Abi) + } + if err != nil { + return nil, err + } else if megapoolV1Abi == nil { + megapoolV1Abi = contract.ABI + } + + // Create and return + return &megapoolV1{ + Address: address, + Version: 1, + Contract: contract, + RocketPool: rp, + }, nil +} + +// Get the contract +func (mp *megapoolV1) GetContract() *rocketpool.Contract { + return mp.Contract +} + +// Get the contract address +func (mp *megapoolV1) GetAddress() common.Address { + return mp.Address +} + +// Get the contract version +func (mp *megapoolV1) GetVersion() uint8 { + return mp.Version +} + +// Get the count of all validators on a megapool +func (mp *megapoolV1) GetValidatorCount(opts *bind.CallOpts) (uint32, error) { + var validatorCount uint32 + if err := mp.Contract.Call(opts, &validatorCount, "getValidatorCount"); err != nil { + return 0, fmt.Errorf("error getting megapool %s validator count: %w", mp.Address.Hex(), err) + } + return validatorCount, nil +} + +// Get the count of validators on a megapool, excluding inactive validators +func (mp *megapoolV1) GetActiveValidatorCount(opts *bind.CallOpts) (uint32, error) { + var validatorCount uint32 + if err := mp.Contract.Call(opts, &validatorCount, "getActiveValidatorCount"); err != nil { + return 0, fmt.Errorf("error getting megapool %s active validator count: %w", mp.Address.Hex(), err) + } + return validatorCount, nil +} + +func (mp *megapoolV1) GetValidatorInfo(validatorId uint32, opts *bind.CallOpts) (ValidatorInfo, error) { + validatorInfo := new(ValidatorInfo) + + callData, err := mp.Contract.ABI.Pack("getValidatorInfo", validatorId) + if err != nil { + return ValidatorInfo{}, fmt.Errorf("error creating calldata for getValidatorInfo: %w", err) + } + + response, err := mp.Contract.Client.CallContract(context.Background(), ethereum.CallMsg{To: mp.Contract.Address, Data: callData}, nil) + if err != nil { + return ValidatorInfo{}, fmt.Errorf("error calling getValidatorInfo: %w", err) + } + + err = mp.Contract.ABI.UnpackIntoInterface(&validatorInfo, "getValidatorInfo", response) + if err != nil { + return ValidatorInfo{}, fmt.Errorf("error unpacking getValidatorInfo response: %w", err) + } + + return *validatorInfo, nil +} + +// Get the number of validators currently exiting +func (mp *megapoolV1) GetExitingValidatorCount(opts *bind.CallOpts) (uint32, error) { + var exitingValidatorCount uint32 + if err := mp.Contract.Call(opts, &exitingValidatorCount, "getExitingValidatorCount"); err != nil { + return 0, fmt.Errorf("error getting megapool %s exiting validator count: %w", mp.Address.Hex(), err) + } + return exitingValidatorCount, nil +} + +// Gets the soonest epoch a validator within this megapool can be withdrawn +func (mp *megapoolV1) GetSoonestWithdrawableEpoch(opts *bind.CallOpts) (uint32, error) { + var soonestWithdrawableEpoch uint32 + if err := mp.Contract.Call(opts, &soonestWithdrawableEpoch, "getSoonestWithdrawableEpoch"); err != nil { + return 0, fmt.Errorf("error getting megapool %s soonest withdrawable epoch: %w", mp.Address.Hex(), err) + } + return soonestWithdrawableEpoch, nil +} + +func (mp *megapoolV1) GetLastDistributionBlock(opts *bind.CallOpts) (uint64, error) { + lastDistributionBlock := new(*big.Int) + if err := mp.Contract.Call(opts, lastDistributionBlock, "getLastDistributionBlock"); err != nil { + return 0, fmt.Errorf("error getting megapool %s lastDistributionBlock: %w", mp.Address.Hex(), err) + } + return (*lastDistributionBlock).Uint64(), nil +} + +func (mp *megapoolV1) GetAssignedValue(opts *bind.CallOpts) (*big.Int, error) { + assignedValue := new(*big.Int) + if err := mp.Contract.Call(opts, assignedValue, "getAssignedValue"); err != nil { + return nil, fmt.Errorf("error getting megapool %s assigned value: %w", mp.Address.Hex(), err) + } + return *assignedValue, nil +} + +func (mp *megapoolV1) GetDebt(opts *bind.CallOpts) (*big.Int, error) { + debt := new(*big.Int) + if err := mp.Contract.Call(opts, debt, "getDebt"); err != nil { + return nil, fmt.Errorf("error getting megapool %s debt: %w", mp.Address.Hex(), err) + } + return *debt, nil +} + +func (mp *megapoolV1) GetRefundValue(opts *bind.CallOpts) (*big.Int, error) { + refundValue := new(*big.Int) + if err := mp.Contract.Call(opts, refundValue, "getRefundValue"); err != nil { + return nil, fmt.Errorf("error getting megapool %s refund value: %w", mp.Address.Hex(), err) + } + return *refundValue, nil +} + +func (mp *megapoolV1) GetNodeBond(opts *bind.CallOpts) (*big.Int, error) { + nodeBond := new(*big.Int) + if err := mp.Contract.Call(opts, nodeBond, "getNodeBond"); err != nil { + return nil, fmt.Errorf("error getting megapool %s debt: %w", mp.Address.Hex(), err) + } + return *nodeBond, nil +} + +func (mp *megapoolV1) GetUserCapital(opts *bind.CallOpts) (*big.Int, error) { + userCapital := new(*big.Int) + if err := mp.Contract.Call(opts, userCapital, "getUserCapital"); err != nil { + return nil, fmt.Errorf("error getting megapool %s user capital: %w", mp.Address.Hex(), err) + } + return *userCapital, nil +} + +func (mp *megapoolV1) CalculatePendingRewards(opts *bind.CallOpts) (RewardSplit, error) { + rewardSplits := new(RewardSplit) + if err := mp.Contract.Call(opts, rewardSplits, "calculatePendingRewards"); err != nil { + return RewardSplit{}, fmt.Errorf("error calculating the pending rewards for megapool %s: %w", mp.Address.Hex(), err) + } + return *rewardSplits, nil +} + +func (mp *megapoolV1) CalculateRewards(amount *big.Int, opts *bind.CallOpts) (RewardSplit, error) { + rewardSplits := new(RewardSplit) + if err := mp.Contract.Call(opts, rewardSplits, "calculateRewards", amount); err != nil { + return RewardSplit{}, fmt.Errorf("error calculating the rewards for amount %s: %w", amount, err) + } + return *rewardSplits, nil +} + +func (mp *megapoolV1) GetPendingRewards(opts *bind.CallOpts) (*big.Int, error) { + pendingRewards := new(*big.Int) + if err := mp.Contract.Call(opts, pendingRewards, "getPendingRewards"); err != nil { + return nil, fmt.Errorf("error getting megapool %s pending rewards: %w", mp.Address.Hex(), err) + } + return *pendingRewards, nil +} + +func (mp *megapoolV1) GetNodeAddress(opts *bind.CallOpts) (common.Address, error) { + nodeAddress := new(common.Address) + if err := mp.Contract.Call(opts, nodeAddress, "getNodeAddress"); err != nil { + return common.Address{}, fmt.Errorf("error getting megapool %s node address: %w", mp.Address.Hex(), err) + } + return *nodeAddress, nil +} + +// Estimate the gas required to create a new validator as part of a megapool +func (mp *megapoolV1) EstimateNewValidatorGas(validatorId uint32, validatorSignature rptypes.ValidatorSignature, depositDataRoot common.Hash, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return mp.Contract.GetTransactionGasInfo(opts, "newValidator", validatorId, validatorSignature[:], depositDataRoot) +} + +// Create a new validator as part of a megapool +func (mp *megapoolV1) NewValidator(bondAmount *big.Int, useExpressTicket bool, validatorPubkey rptypes.ValidatorPubkey, validatorSignature rptypes.ValidatorSignature, opts *bind.TransactOpts) (common.Hash, error) { + tx, err := mp.Contract.Transact(opts, "newValidator", bondAmount, useExpressTicket, validatorPubkey[:], validatorSignature[:]) + if err != nil { + return common.Hash{}, fmt.Errorf("error creating new validator %s: %w", validatorPubkey.Hex(), err) + } + return tx.Hash(), nil +} + +// Estimate the gas required to remove a validator from the deposit queue +func (mp *megapoolV1) EstimateDequeueGas(validatorId uint32, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return mp.Contract.GetTransactionGasInfo(opts, "dequeue", validatorId) +} + +// Remove a validator from the deposit queue +func (mp *megapoolV1) Dequeue(validatorId uint32, opts *bind.TransactOpts) (common.Hash, error) { + tx, err := mp.Contract.Transact(opts, "dequeue", validatorId) + if err != nil { + return common.Hash{}, fmt.Errorf("error dequeuing validator ID %d: %w", validatorId, err) + } + return tx.Hash(), nil +} + +// Estimate the gas required to accept requested funds from the deposit pool +func (mp *megapoolV1) EstimateAssignFundsGas(validatorId uint32, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return mp.Contract.GetTransactionGasInfo(opts, "assignFunds", validatorId) +} + +// Accept requested funds from the deposit pool +func (mp *megapoolV1) AssignFunds(validatorId uint32, opts *bind.TransactOpts) (common.Hash, error) { + tx, err := mp.Contract.Transact(opts, "assignFunds", validatorId) + if err != nil { + return common.Hash{}, fmt.Errorf("error assigning funds to validator ID %d: %w", validatorId, err) + } + return tx.Hash(), nil +} + +// Estimate the gas required to dissolve a validator that has not staked within the required period +func (mp *megapoolV1) EstimateDissolveValidatorGas(validatorId uint32, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return mp.Contract.GetTransactionGasInfo(opts, "dissolveValidator", validatorId) +} + +// Dissolve a validator that has not staked within the required period +func (mp *megapoolV1) DissolveValidator(validatorId uint32, opts *bind.TransactOpts) (common.Hash, error) { + tx, err := mp.Contract.Transact(opts, "dissolveValidator", validatorId) + if err != nil { + return common.Hash{}, fmt.Errorf("error dissolving validator ID %d: %w", validatorId, err) + } + return tx.Hash(), nil +} + +// Estimate the gas required to repay megapool debt +func (mp *megapoolV1) EstimateRepayDebtGas(opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return mp.Contract.GetTransactionGasInfo(opts, "repayDebt") +} + +// Receive ETH, which is sent to the rETH contract, to repay a megapool debt +func (mp *megapoolV1) RepayDebt(opts *bind.TransactOpts) (common.Hash, error) { + tx, err := mp.Contract.Transact(opts, "repayDebt") + if err != nil { + return common.Hash{}, fmt.Errorf("error repaying debt for megapool %s: %w", mp.Address.Hex(), err) + } + return tx.Hash(), nil +} + +// Estimate the gas required to reduce a megapool bond +func (mp *megapoolV1) EstimateReduceBondGas(amount *big.Int, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return mp.Contract.GetTransactionGasInfo(opts, "reduceBond", amount) +} + +// If the megapool is overbonded, reduce the bond by the specified amount +func (mp *megapoolV1) ReduceBond(amount *big.Int, opts *bind.TransactOpts) (common.Hash, error) { + tx, err := mp.Contract.Transact(opts, "reduceBond", amount) + if err != nil { + return common.Hash{}, fmt.Errorf("error reducing the megapool bond %s: %w", mp.Address.Hex(), err) + } + return tx.Hash(), nil +} + +// Estimate the gas required to claim a megapool refund +func (mp *megapoolV1) EstimateClaimRefundGas(opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return mp.Contract.GetTransactionGasInfo(opts, "claim") +} + +// Claim megapool rewards that were distributed but not yet claimed +func (mp *megapoolV1) ClaimRefund(opts *bind.TransactOpts) (common.Hash, error) { + tx, err := mp.Contract.Transact(opts, "claim") + if err != nil { + return common.Hash{}, fmt.Errorf("error claiming megapool refund %s: %w", mp.Address.Hex(), err) + } + return tx.Hash(), nil +} + +// Get the expected withdrawal credentials for any validator within this megapool +func (mp *megapoolV1) GetWithdrawalCredentials(opts *bind.CallOpts) (common.Hash, error) { + withdrawalCredentials := new(common.Hash) + if err := mp.Contract.Call(opts, withdrawalCredentials, "getWithdrawalCredentials"); err != nil { + return common.Hash{}, fmt.Errorf("error getting megapool %s withdrawal credentials: %w", mp.Address.Hex(), err) + } + return *withdrawalCredentials, nil +} + +// Estimate the gas required to Request RPL previously staked on this megapool to be unstaked +func (mp *megapoolV1) EstimateRequestUnstakeRPL(opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return mp.Contract.GetTransactionGasInfo(opts, "requestUnstakeRPL") +} + +// RequestUnstakeRPL is not yet implemented in RocketMegapoolDelegate.sol +// Request RPL previously staked on this megapool to be unstaked +func (mp *megapoolV1) RequestUnstakeRPL(opts *bind.TransactOpts) (common.Hash, error) { + tx, err := mp.Contract.Transact(opts, "requestUnstakeRPL") + if err != nil { + return common.Hash{}, fmt.Errorf("error requesting unstake rpl for megapool %s: %w", mp.Address.Hex(), err) + } + return tx.Hash(), nil +} + +// Estimate the gas of Stake +func (mp *megapoolV1) EstimateStakeGas(validatorId uint32, validatorProof ValidatorProof, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return mp.Contract.GetTransactionGasInfo(opts, "stake", validatorId, validatorProof) +} + +// Progress the prelaunch megapool to staking +func (mp *megapoolV1) Stake(validatorId uint32, validatorProof ValidatorProof, opts *bind.TransactOpts) (common.Hash, error) { + // callData, err := mp.Contract.ABI.Pack("stake", validatorId, validatorSignature[:], depositDataRoot, validatorProof) + // if err != nil { + // return common.Hash{}, fmt.Errorf("error creating calldata for getValidatorInfo: %w", err) + // } + + // fmt.Println("call data:\n") + // fmt.Printf("%s", hex.EncodeToString(callData)) + + // tx, err := mp.Contract.Contract.RawTransact(opts, callData) + // if err != nil { + // return common.Hash{}, fmt.Errorf("error calling getValidatorInfo: %w", err) + // } + + tx, err := mp.Contract.Transact(opts, "stake", validatorId, validatorProof) + if err != nil { + return common.Hash{}, fmt.Errorf("error staking megapool %s: %w", mp.Address.Hex(), err) + } + return tx.Hash(), nil +} + +// Estimate the gas to call NotifyExit +func (mp *megapoolV1) EstimateNotifyExitGas(validatorId uint32, withdrawalEpoch *big.Int, slot uint64, exitProof [][32]byte, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return mp.Contract.GetTransactionGasInfo(opts, "notifyExit", validatorId, withdrawalEpoch, slot, exitProof) +} + +// Notify the megapool that one of its validators is exiting +func (mp *megapoolV1) NotifyExit(validatorId uint32, withdrawalEpoch *big.Int, slot uint64, exitProof [][32]byte, opts *bind.TransactOpts) (common.Hash, error) { + tx, err := mp.Contract.Transact(opts, "notifyExit", validatorId, withdrawalEpoch, slot, exitProof) + if err != nil { + return common.Hash{}, fmt.Errorf("error calling notify exit: %w", err) + } + return tx.Hash(), nil +} + +// Estimate the gas to call NotifyFinalBalance +func (mp *megapoolV1) EstimateNotifyFinalBalance(validatorId uint32, withdrawalSlot *big.Int, withdrawalNum *big.Int, withdrawal Withdrawal, slot *big.Int, exitProof [][32]byte, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return mp.Contract.GetTransactionGasInfo(opts, "notifyFinalBalance", validatorId, withdrawalSlot, withdrawalNum, withdrawal, slot, exitProof) +} + +// Notify the megapool of the final balance of an exited validator +func (mp *megapoolV1) NotifyFinalBalance(validatorId uint32, withdrawalSlot *big.Int, withdrawalNum *big.Int, withdrawal Withdrawal, slot *big.Int, exitProof [][32]byte, opts *bind.TransactOpts) (common.Hash, error) { + tx, err := mp.Contract.Transact(opts, "notifyFinalBalance", validatorId, withdrawalSlot, withdrawalNum, withdrawal, slot, exitProof) + if err != nil { + return common.Hash{}, fmt.Errorf("error calling notify final balance: %w", err) + } + return tx.Hash(), nil +} + +// Estimate the gas required to distribute megapool rewards +func (mp *megapoolV1) EstimateDistributeGas(opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return mp.Contract.GetTransactionGasInfo(opts, "distribute") +} + +// Distribute megapool rewards +func (mp *megapoolV1) Distribute(opts *bind.TransactOpts) (common.Hash, error) { + tx, err := mp.Contract.Transact(opts, "distribute") + if err != nil { + return common.Hash{}, fmt.Errorf("error distributing megapool rewards: %w", err) + } + return tx.Hash(), nil +} + +// Estimate the gas of SetUseLatestDelegate +func (mp *megapoolV1) EstimateSetUseLatestDelegateGas(setting bool, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return mp.Contract.GetTransactionGasInfo(opts, "setUseLatestDelegate", setting) +} + +// If set to true, will automatically use the latest delegate contract +func (mp *megapoolV1) SetUseLatestDelegate(setting bool, opts *bind.TransactOpts) (common.Hash, error) { + tx, err := mp.Contract.Transact(opts, "setUseLatestDelegate", setting) + if err != nil { + return common.Hash{}, fmt.Errorf("error setting use latest delegate for megapool %s: %w", mp.Address.Hex(), err) + } + return tx.Hash(), nil +} + +// Getter for useLatestDelegate setting +func (mp *megapoolV1) GetUseLatestDelegate(opts *bind.CallOpts) (bool, error) { + setting := new(bool) + if err := mp.Contract.Call(opts, setting, "getUseLatestDelegate"); err != nil { + return false, fmt.Errorf("error getting use latest delegate for megapool %s: %w", mp.Address.Hex(), err) + } + return *setting, nil +} + +// Returns the address of the megapool's stored delegate +func (mp *megapoolV1) GetDelegate(opts *bind.CallOpts) (common.Address, error) { + address := new(common.Address) + if err := mp.Contract.Call(opts, address, "getDelegate"); err != nil { + return common.Address{}, fmt.Errorf("error getting delegate for megapool %s: %w", mp.Address.Hex(), err) + } + return *address, nil +} + +// Returns the delegate which will be used when calling this minipool taking into account useLatestDelegate setting +func (mp *megapoolV1) GetEffectiveDelegate(opts *bind.CallOpts) (common.Address, error) { + address := new(common.Address) + if err := mp.Contract.Call(opts, address, "getEffectiveDelegate"); err != nil { + return common.Address{}, fmt.Errorf("error getting effective delegate for megapool %s: %w", mp.Address.Hex(), err) + } + return *address, nil +} + +// Returns true if the megapools current delegate has expired +func (mp *megapoolV1) GetDelegateExpired(rp *rocketpool.RocketPool, opts *bind.CallOpts) (bool, error) { + delegateExpired := new(bool) + if err := mp.Contract.Call(opts, delegateExpired, "getDelegateExpired"); err != nil { + return false, fmt.Errorf("error checking if the megapool's delegate has expired:, %w", err) + } + return *delegateExpired, nil +} + +// Estimate the gas of DelegateUpgrade +func (mp *megapoolV1) EstimateDelegateUpgradeGas(opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return mp.Contract.GetTransactionGasInfo(opts, "delegateUpgrade") +} + +// Upgrade this megapool to the latest network delegate contract +func (mp *megapoolV1) DelegateUpgrade(opts *bind.TransactOpts) (common.Hash, error) { + tx, err := mp.Contract.Transact(opts, "delegateUpgrade") + if err != nil { + return common.Hash{}, fmt.Errorf("error upgrading delegate for megapool %s: %w", mp.Address.Hex(), err) + } + return tx.Hash(), nil +} + +var ValidatorBatchSize = uint32(50) + +func (mp *megapoolV1) GetMegapoolPubkeys(opts *bind.CallOpts) ([]rptypes.ValidatorPubkey, error) { + validatorCount, err := mp.GetValidatorCount(opts) + if err != nil { + return []rptypes.ValidatorPubkey{}, err + } + + // Load pubkeys in batches + var lock = sync.RWMutex{} + pubkeys := make([]rptypes.ValidatorPubkey, validatorCount) + for bsi := uint32(0); bsi < validatorCount; bsi += ValidatorBatchSize { + + // Get batch start & end index + msi := bsi + mei := bsi + ValidatorBatchSize + if mei > validatorCount { + mei = validatorCount + } + + // Load pubkeys + var wg errgroup.Group + for mi := msi; mi < mei; mi++ { + mi := mi + wg.Go(func() error { + validator, err := mp.GetValidatorInfo(mi, opts) + if err != nil { + return err + } + lock.Lock() + pubkeys[mi] = rptypes.BytesToValidatorPubkey(validator.PubKey) + lock.Unlock() + return nil + }) + } + if err := wg.Wait(); err != nil { + return []rptypes.ValidatorPubkey{}, err + } + + } + // Return + return pubkeys, nil +} + +// Create a megapool contract directly from its ABI +func createMegapoolContractFromAbi(rp *rocketpool.RocketPool, address common.Address, abi *abi.ABI) (*rocketpool.Contract, error) { + // Create and return + return &rocketpool.Contract{ + Contract: bind.NewBoundContract(address, *abi, rp.Client, rp.Client, rp.Client), + Address: &address, + ABI: abi, + Client: rp.Client, + }, nil +} + +// Create a megapool contract directly from its ABI, encoded in string form +func createMegapoolContractFromEncodedAbi(rp *rocketpool.RocketPool, address common.Address, encodedAbi string) (*rocketpool.Contract, error) { + // Decode ABI + abi, err := rocketpool.DecodeAbi(encodedAbi) + if err != nil { + return nil, fmt.Errorf("error decoding megapool %s ABI: %w", address, err) + } + + // Create and return + return &rocketpool.Contract{ + Contract: bind.NewBoundContract(address, *abi, rp.Client, rp.Client, rp.Client), + Address: &address, + ABI: abi, + Client: rp.Client, + }, nil +} diff --git a/bindings/megapool/megapool-interface.go b/bindings/megapool/megapool-interface.go new file mode 100644 index 000000000..10d70242e --- /dev/null +++ b/bindings/megapool/megapool-interface.go @@ -0,0 +1,65 @@ +package megapool + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + rptypes "github.com/rocket-pool/smartnode/bindings/types" +) + +type Megapool interface { + GetContract() *rocketpool.Contract + GetAddress() common.Address + GetVersion() uint8 + GetValidatorCount(opts *bind.CallOpts) (uint32, error) + GetActiveValidatorCount(opts *bind.CallOpts) (uint32, error) + GetExitingValidatorCount(opts *bind.CallOpts) (uint32, error) + GetSoonestWithdrawableEpoch(opts *bind.CallOpts) (uint32, error) + GetValidatorInfo(validatorId uint32, opts *bind.CallOpts) (ValidatorInfo, error) + GetLastDistributionBlock(opts *bind.CallOpts) (uint64, error) + GetAssignedValue(opts *bind.CallOpts) (*big.Int, error) + GetDebt(opts *bind.CallOpts) (*big.Int, error) + GetRefundValue(opts *bind.CallOpts) (*big.Int, error) + GetNodeBond(opts *bind.CallOpts) (*big.Int, error) + GetUserCapital(opts *bind.CallOpts) (*big.Int, error) + CalculatePendingRewards(opts *bind.CallOpts) (RewardSplit, error) + CalculateRewards(amount *big.Int, opts *bind.CallOpts) (RewardSplit, error) + GetPendingRewards(opts *bind.CallOpts) (*big.Int, error) + GetNodeAddress(opts *bind.CallOpts) (common.Address, error) + EstimateNewValidatorGas(validatorId uint32, validatorSignature rptypes.ValidatorSignature, depositDataRoot common.Hash, opts *bind.TransactOpts) (rocketpool.GasInfo, error) + NewValidator(bondAmount *big.Int, useExpressTicket bool, validatorPubkey rptypes.ValidatorPubkey, validatorSignature rptypes.ValidatorSignature, opts *bind.TransactOpts) (common.Hash, error) + EstimateDequeueGas(validatorId uint32, opts *bind.TransactOpts) (rocketpool.GasInfo, error) + Dequeue(validatorId uint32, opts *bind.TransactOpts) (common.Hash, error) + EstimateDistributeGas(opts *bind.TransactOpts) (rocketpool.GasInfo, error) + Distribute(opts *bind.TransactOpts) (common.Hash, error) + EstimateAssignFundsGas(validatorId uint32, opts *bind.TransactOpts) (rocketpool.GasInfo, error) + AssignFunds(validatorId uint32, opts *bind.TransactOpts) (common.Hash, error) + EstimateDissolveValidatorGas(validatorId uint32, opts *bind.TransactOpts) (rocketpool.GasInfo, error) + DissolveValidator(validatorId uint32, opts *bind.TransactOpts) (common.Hash, error) + EstimateClaimRefundGas(opts *bind.TransactOpts) (rocketpool.GasInfo, error) + ClaimRefund(opts *bind.TransactOpts) (common.Hash, error) + EstimateRepayDebtGas(opts *bind.TransactOpts) (rocketpool.GasInfo, error) + RepayDebt(opts *bind.TransactOpts) (common.Hash, error) + EstimateReduceBondGas(amount *big.Int, opts *bind.TransactOpts) (rocketpool.GasInfo, error) + ReduceBond(amount *big.Int, opts *bind.TransactOpts) (common.Hash, error) + GetWithdrawalCredentials(opts *bind.CallOpts) (common.Hash, error) + EstimateRequestUnstakeRPL(opts *bind.TransactOpts) (rocketpool.GasInfo, error) + RequestUnstakeRPL(opts *bind.TransactOpts) (common.Hash, error) + EstimateStakeGas(validatorId uint32, validatorProof ValidatorProof, opts *bind.TransactOpts) (rocketpool.GasInfo, error) + Stake(validatorId uint32, validatorProof ValidatorProof, opts *bind.TransactOpts) (common.Hash, error) + EstimateNotifyExitGas(validatorId uint32, withdrawalEpoch *big.Int, slot uint64, exitProof [][32]byte, opts *bind.TransactOpts) (rocketpool.GasInfo, error) + NotifyExit(validatorId uint32, withdrawalEpoch *big.Int, slot uint64, exitProof [][32]byte, opts *bind.TransactOpts) (common.Hash, error) + EstimateNotifyFinalBalance(validatorId uint32, withdrawalSlot *big.Int, withdrawalNum *big.Int, withdrawal Withdrawal, slot *big.Int, exitProof [][32]byte, opts *bind.TransactOpts) (rocketpool.GasInfo, error) + NotifyFinalBalance(validatorId uint32, withdrawalSlot *big.Int, withdrawalNum *big.Int, withdrawal Withdrawal, slot *big.Int, exitProof [][32]byte, opts *bind.TransactOpts) (common.Hash, error) + EstimateSetUseLatestDelegateGas(setting bool, opts *bind.TransactOpts) (rocketpool.GasInfo, error) + SetUseLatestDelegate(setting bool, opts *bind.TransactOpts) (common.Hash, error) + GetUseLatestDelegate(opts *bind.CallOpts) (bool, error) + GetDelegate(opts *bind.CallOpts) (common.Address, error) + GetDelegateExpired(rp *rocketpool.RocketPool, opts *bind.CallOpts) (bool, error) + GetEffectiveDelegate(opts *bind.CallOpts) (common.Address, error) + EstimateDelegateUpgradeGas(opts *bind.TransactOpts) (rocketpool.GasInfo, error) + DelegateUpgrade(opts *bind.TransactOpts) (common.Hash, error) + GetMegapoolPubkeys(opts *bind.CallOpts) ([]rptypes.ValidatorPubkey, error) +} diff --git a/bindings/megapool/megapool-manager.go b/bindings/megapool/megapool-manager.go new file mode 100644 index 000000000..e7404c652 --- /dev/null +++ b/bindings/megapool/megapool-manager.go @@ -0,0 +1,100 @@ +package megapool + +import ( + "context" + "fmt" + "math/big" + "sync" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/rocket-pool/smartnode/bindings/rocketpool" +) + +func GetValidatorCount(rp *rocketpool.RocketPool, opts *bind.CallOpts) (uint32, error) { + megapoolManager, err := getRocketMegapoolManager(rp, opts) + if err != nil { + return 0, err + } + var validatorCount *big.Int + if err := megapoolManager.Call(opts, &validatorCount, "getValidatorCount"); err != nil { + return 0, fmt.Errorf("error getting megapool manager validator count: %w", err) + } + return uint32((*validatorCount).Uint64()), nil +} + +func GetValidatorInfo(rp *rocketpool.RocketPool, index uint32, opts *bind.CallOpts) (ValidatorInfoFromGlobalIndex, error) { + megapoolManager, err := getRocketMegapoolManager(rp, opts) + if err != nil { + return ValidatorInfoFromGlobalIndex{}, err + } + + validatorInfo := new(ValidatorInfoFromGlobalIndex) + + indexBig := new(big.Int).SetUint64(uint64(index)) + + callData, err := megapoolManager.ABI.Pack("getValidatorInfo", indexBig) + if err != nil { + return ValidatorInfoFromGlobalIndex{}, fmt.Errorf("error creating calldata for getValidatorInfo: %w", err) + } + + response, err := megapoolManager.Client.CallContract(context.Background(), ethereum.CallMsg{To: megapoolManager.Address, Data: callData}, nil) + if err != nil { + return ValidatorInfoFromGlobalIndex{}, fmt.Errorf("error calling getValidatorInfo: %w", err) + } + + // Both Call and UnpackIntoStruct were not working with this response (which contains a struct inside a struct) + // For the moment this was the only way for it to work. We should investigate further. + iface, err := megapoolManager.ABI.Unpack("getValidatorInfo", response) + if err != nil { + return ValidatorInfoFromGlobalIndex{}, fmt.Errorf("error unpacking getValidatorInfo response: %w", err) + } + + src := iface[0].(struct { + PubKey []byte `json:"pubKey"` + LastAssignmentTime uint32 `json:"lastAssignmentTime"` + LastRequestedValue uint32 `json:"lastRequestedValue"` + LastRequestedBond uint32 `json:"lastRequestedBond"` + DepositValue uint32 `json:"depositValue"` + Staked bool `json:"staked"` + Exited bool `json:"exited"` + InQueue bool `json:"inQueue"` + InPrestake bool `json:"inPrestake"` + ExpressUsed bool `json:"expressUsed"` + Dissolved bool `json:"dissolved"` + Exiting bool `json:"exiting"` + ValidatorIndex uint64 `json:"validatorIndex"` + ExitBalance uint64 `json:"exitBalance"` + WithdrawableEpoch uint64 `json:"withdrawableEpoch"` + }) + validatorInfo.ValidatorInfo.PubKey = make([]byte, len(src.PubKey)) + copy(validatorInfo.ValidatorInfo.PubKey[:], src.PubKey) + validatorInfo.ValidatorInfo.LastAssignmentTime = src.LastAssignmentTime + validatorInfo.ValidatorInfo.LastRequestedValue = src.LastRequestedValue + validatorInfo.ValidatorInfo.LastRequestedBond = src.LastRequestedBond + validatorInfo.ValidatorInfo.Staked = src.Staked + validatorInfo.ValidatorInfo.DepositValue = src.DepositValue + validatorInfo.ValidatorInfo.ExitBalance = src.ExitBalance + validatorInfo.ValidatorInfo.WithdrawableEpoch = src.WithdrawableEpoch + validatorInfo.ValidatorInfo.Exiting = src.Exiting + validatorInfo.ValidatorInfo.ValidatorIndex = src.ValidatorIndex + validatorInfo.ValidatorInfo.Exited = src.Exited + validatorInfo.ValidatorInfo.InQueue = src.InQueue + validatorInfo.ValidatorInfo.InPrestake = src.InPrestake + validatorInfo.ValidatorInfo.ExpressUsed = src.ExpressUsed + validatorInfo.ValidatorInfo.Dissolved = src.Dissolved + validatorInfo.MegapoolAddress = iface[1].(common.Address) + validatorInfo.ValidatorId = iface[2].(uint32) + + return *validatorInfo, nil +} + +// Get contracts +var rocketMegapoolManagerLock sync.Mutex + +func getRocketMegapoolManager(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*rocketpool.Contract, error) { + rocketMegapoolManagerLock.Lock() + defer rocketMegapoolManagerLock.Unlock() + return rp.GetContract("rocketMegapoolManager", opts) +} diff --git a/bindings/megapool/megapool-penalties.go b/bindings/megapool/megapool-penalties.go new file mode 100644 index 000000000..54e6cace7 --- /dev/null +++ b/bindings/megapool/megapool-penalties.go @@ -0,0 +1,40 @@ +package megapool + +import ( + "fmt" + "math/big" + "sync" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/rocket-pool/smartnode/bindings/rocketpool" +) + +func EstimatePenaliseGas(rp *rocketpool.RocketPool, megapoolAddress common.Address, block *big.Int, amount *big.Int, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + megapoolPenalties, err := getRocketMegapoolPenalties(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return megapoolPenalties.GetTransactionGasInfo(opts, "penalise", megapoolAddress, block, amount) +} + +func Penalise(rp *rocketpool.RocketPool, megapoolAddress common.Address, block *big.Int, amount *big.Int, opts *bind.TransactOpts) (common.Hash, error) { + megapoolPenalties, err := getRocketMegapoolPenalties(rp, nil) + if err != nil { + return common.Hash{}, err + } + tx, err := megapoolPenalties.Transact(opts, "penalise", megapoolAddress, block, amount) + if err != nil { + return common.Hash{}, fmt.Errorf("error voting to penalise megapool: %w", err) + } + return tx.Hash(), nil +} + +// Get contracts +var rocketMegapoolPenaltiesLock sync.Mutex + +func getRocketMegapoolPenalties(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*rocketpool.Contract, error) { + rocketMegapoolPenaltiesLock.Lock() + defer rocketMegapoolPenaltiesLock.Unlock() + return rp.GetContract("rocketMegapoolPenalties", opts) +} diff --git a/bindings/megapool/megapool-proxy.go b/bindings/megapool/megapool-proxy.go new file mode 100644 index 000000000..aec878cdc --- /dev/null +++ b/bindings/megapool/megapool-proxy.go @@ -0,0 +1,71 @@ +package megapool + +import ( + "fmt" + "sync" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/rocket-pool/smartnode/bindings/rocketpool" +) + +// Returns true if this megapool always uses the latest delegate contract +func GetUseLatestDelegate(rp *rocketpool.RocketPool, opts *bind.CallOpts) (bool, error) { + megapoolProxy, err := getRocketMegapoolProxy(rp, opts) + if err != nil { + return false, err + } + isUsingLatestDelegate := new(bool) + if err := megapoolProxy.Call(opts, isUsingLatestDelegate, "getUseLatestDelegate"); err != nil { + return false, fmt.Errorf("error checking if this megapool is using the latest delegate:, %w", err) + } + return *isUsingLatestDelegate, nil +} + +// Returns the address of the megapool's stored delegate +func GetDelegate(rp *rocketpool.RocketPool, opts *bind.CallOpts) (common.Address, error) { + megapoolProxy, err := getRocketMegapoolProxy(rp, opts) + if err != nil { + return common.Address{}, err + } + delegateAddress := new(common.Address) + if err := megapoolProxy.Call(opts, delegateAddress, "getDelegate"); err != nil { + return common.Address{}, fmt.Errorf("error getting the delegate address: %w", err) + } + return *delegateAddress, nil +} + +// Returns the delegate which will be used when calling this megapool taking into account useLatestDelegate setting +func GetEffectiveDelegate(rp *rocketpool.RocketPool, opts *bind.CallOpts) (common.Address, error) { + megapoolProxy, err := getRocketMegapoolProxy(rp, opts) + if err != nil { + return common.Address{}, err + } + effectiveDelegateAddress := new(common.Address) + if err := megapoolProxy.Call(opts, effectiveDelegateAddress, "getEffectiveDelegate"); err != nil { + return common.Address{}, fmt.Errorf("error getting the effective delegate address: %w", err) + } + return *effectiveDelegateAddress, nil +} + +// Returns true if the megapools current delegate has expired +func GetDelegateExpired(rp *rocketpool.RocketPool, opts *bind.CallOpts) (bool, error) { + megapoolProxy, err := getRocketMegapoolProxy(rp, opts) + if err != nil { + return false, err + } + delegateExpired := new(bool) + if err := megapoolProxy.Call(opts, delegateExpired, "getDelegateExpired"); err != nil { + return false, fmt.Errorf("error checking if the megapool's delegate has expired:, %w", err) + } + return *delegateExpired, nil +} + +// Get contracts +var rocketMegapoolProxyLock sync.Mutex + +func getRocketMegapoolProxy(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*rocketpool.Contract, error) { + rocketMegapoolProxyLock.Lock() + defer rocketMegapoolProxyLock.Unlock() + return rp.GetContract("rocketMegapoolProxy", opts) +} diff --git a/bindings/minipool/bond-reducer.go b/bindings/minipool/bond-reducer.go new file mode 100644 index 000000000..3fa760844 --- /dev/null +++ b/bindings/minipool/bond-reducer.go @@ -0,0 +1,143 @@ +package minipool + +import ( + "fmt" + "math/big" + "sync" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/rocket-pool/smartnode/bindings/rocketpool" +) + +// Estimate the gas required to vote to cancel a minipool's bond reduction +func EstimateVoteCancelReductionGas(rp *rocketpool.RocketPool, minipoolAddress common.Address, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketMinipoolBondReducer, err := getRocketMinipoolBondReducer(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketMinipoolBondReducer.GetTransactionGasInfo(opts, "voteCancelReduction", minipoolAddress) +} + +// Vote to cancel a minipool's bond reduction +func VoteCancelReduction(rp *rocketpool.RocketPool, minipoolAddress common.Address, opts *bind.TransactOpts) (common.Hash, error) { + rocketMinipoolBondReducer, err := getRocketMinipoolBondReducer(rp, nil) + if err != nil { + return common.Hash{}, err + } + tx, err := rocketMinipoolBondReducer.Transact(opts, "voteCancelReduction", minipoolAddress) + if err != nil { + return common.Hash{}, fmt.Errorf("error voting to cancel bond reduction for minipool %s: %w", minipoolAddress.Hex(), err) + } + return tx.Hash(), nil +} + +// Gets whether or not the bond reduction process for this minipool has already been cancelled +func GetReduceBondCancelled(rp *rocketpool.RocketPool, minipoolAddress common.Address, opts *bind.CallOpts) (bool, error) { + rocketMinipoolBondReducer, err := getRocketMinipoolBondReducer(rp, nil) + if err != nil { + return false, err + } + isCancelled := new(bool) + if err := rocketMinipoolBondReducer.Call(opts, isCancelled, "getReduceBondCancelled", minipoolAddress); err != nil { + return false, fmt.Errorf("error getting reduce bond cancelled status for minipool %s: %w", minipoolAddress.Hex(), err) + } + return *isCancelled, nil +} + +// Gets the time at which the MP owner started the bond reduction process +func GetReduceBondTime(rp *rocketpool.RocketPool, minipoolAddress common.Address, opts *bind.CallOpts) (time.Time, error) { + rocketMinipoolBondReducer, err := getRocketMinipoolBondReducer(rp, nil) + if err != nil { + return time.Time{}, err + } + reduceBondTime := new(*big.Int) + if err := rocketMinipoolBondReducer.Call(opts, reduceBondTime, "getReduceBondTime", minipoolAddress); err != nil { + return time.Time{}, fmt.Errorf("error getting reduce bond time for minipool %s: %w", minipoolAddress.Hex(), err) + } + return time.Unix((*reduceBondTime).Int64(), 0), nil +} + +// Gets the amount of ETH a minipool is reducing its bond to +func GetReduceBondValue(rp *rocketpool.RocketPool, minipoolAddress common.Address, opts *bind.CallOpts) (*big.Int, error) { + rocketMinipoolBondReducer, err := getRocketMinipoolBondReducer(rp, nil) + if err != nil { + return nil, err + } + reduceBondValue := new(*big.Int) + if err := rocketMinipoolBondReducer.Call(opts, reduceBondValue, "getReduceBondValue", minipoolAddress); err != nil { + return nil, fmt.Errorf("error getting reduce bond value for minipool %s: %w", minipoolAddress.Hex(), err) + } + return *reduceBondValue, nil +} + +// Gets the timestamp at which the bond was last reduced +func GetLastBondReductionTime(rp *rocketpool.RocketPool, minipoolAddress common.Address, opts *bind.CallOpts) (time.Time, error) { + rocketMinipoolBondReducer, err := getRocketMinipoolBondReducer(rp, nil) + if err != nil { + return time.Time{}, err + } + lastBondReductionTime := new(*big.Int) + if err := rocketMinipoolBondReducer.Call(opts, lastBondReductionTime, "getLastBondReductionTime", minipoolAddress); err != nil { + return time.Time{}, fmt.Errorf("error getting last bond reduction time for minipool %s: %w", minipoolAddress.Hex(), err) + } + return time.Unix((*lastBondReductionTime).Int64(), 0), nil +} + +// Gets the previous bond amount of the minipool prior to its last reduction +func GetLastBondReductionPrevValue(rp *rocketpool.RocketPool, minipoolAddress common.Address, opts *bind.CallOpts) (*big.Int, error) { + rocketMinipoolBondReducer, err := getRocketMinipoolBondReducer(rp, nil) + if err != nil { + return nil, err + } + lastBondReductionPrevValue := new(*big.Int) + if err := rocketMinipoolBondReducer.Call(opts, lastBondReductionPrevValue, "getLastBondReductionPrevValue", minipoolAddress); err != nil { + return nil, fmt.Errorf("error getting last bond reduction previous value for minipool %s: %w", minipoolAddress.Hex(), err) + } + return *lastBondReductionPrevValue, nil +} + +// Gets the previous node fee (commission) of the minipool prior to its last reduction +func GetLastBondReductionPrevNodeFee(rp *rocketpool.RocketPool, minipoolAddress common.Address, opts *bind.CallOpts) (*big.Int, error) { + rocketMinipoolBondReducer, err := getRocketMinipoolBondReducer(rp, nil) + if err != nil { + return nil, err + } + lastBondReductionPrevNodeFee := new(*big.Int) + if err := rocketMinipoolBondReducer.Call(opts, lastBondReductionPrevNodeFee, "getLastBondReductionPrevNodeFee", minipoolAddress); err != nil { + return nil, fmt.Errorf("error getting last bond reduction previous node fee for minipool %s: %w", minipoolAddress.Hex(), err) + } + return *lastBondReductionPrevNodeFee, nil +} + +// Estimate the gas required to begin a minipool bond reduction +func EstimateBeginReduceBondAmountGas(rp *rocketpool.RocketPool, minipoolAddress common.Address, newBondAmount *big.Int, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketMinipoolBondReducer, err := getRocketMinipoolBondReducer(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketMinipoolBondReducer.GetTransactionGasInfo(opts, "beginReduceBondAmount", minipoolAddress, newBondAmount) +} + +// Begin a minipool bond reduction +func BeginReduceBondAmount(rp *rocketpool.RocketPool, minipoolAddress common.Address, newBondAmount *big.Int, opts *bind.TransactOpts) (common.Hash, error) { + rocketMinipoolBondReducer, err := getRocketMinipoolBondReducer(rp, nil) + if err != nil { + return common.Hash{}, err + } + tx, err := rocketMinipoolBondReducer.Transact(opts, "beginReduceBondAmount", minipoolAddress, newBondAmount) + if err != nil { + return common.Hash{}, fmt.Errorf("error beginning bond reduction for minipool %s: %w", minipoolAddress.Hex(), err) + } + return tx.Hash(), nil +} + +// Get contracts +var rocketMinipoolBondReducerLock sync.Mutex + +func getRocketMinipoolBondReducer(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*rocketpool.Contract, error) { + rocketMinipoolBondReducerLock.Lock() + defer rocketMinipoolBondReducerLock.Unlock() + return rp.GetContract("rocketMinipoolBondReducer", opts) +} diff --git a/bindings/minipool/factory.go b/bindings/minipool/factory.go new file mode 100644 index 000000000..fbf73d58c --- /dev/null +++ b/bindings/minipool/factory.go @@ -0,0 +1,34 @@ +package minipool + +import ( + "fmt" + "math/big" + "sync" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + + "github.com/rocket-pool/smartnode/bindings/rocketpool" +) + +// Get the address of a minipool based on the node address and a salt +func GetExpectedAddress(rp *rocketpool.RocketPool, nodeAddress common.Address, salt *big.Int, opts *bind.CallOpts) (common.Address, error) { + rocketMinipoolFactory, err := getRocketMinipoolFactory(rp, opts) + if err != nil { + return common.Address{}, err + } + address := new(common.Address) + if err := rocketMinipoolFactory.Call(opts, address, "getExpectedAddress", nodeAddress, salt); err != nil { + return common.Address{}, fmt.Errorf("error getting minipool expected address: %w", err) + } + return *address, nil +} + +// Get contracts +var rocketMinipoolFactoryLock sync.Mutex + +func getRocketMinipoolFactory(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*rocketpool.Contract, error) { + rocketMinipoolFactoryLock.Lock() + defer rocketMinipoolFactoryLock.Unlock() + return rp.GetContract("rocketMinipoolFactory", opts) +} diff --git a/bindings/minipool/minipool-constructor.go b/bindings/minipool/minipool-constructor.go new file mode 100644 index 000000000..61c02b9e6 --- /dev/null +++ b/bindings/minipool/minipool-constructor.go @@ -0,0 +1,88 @@ +package minipool + +import ( + "fmt" + "strings" + "sync" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/rocket-pool/smartnode/bindings/rocketpool" +) + +// Create a minipool binding +func NewMinipool(rp *rocketpool.RocketPool, address common.Address, opts *bind.CallOpts) (Minipool, error) { + + // Get the contract version + version, err := rocketpool.GetContractVersion(rp, address, opts) + if err != nil { + errMsg := err.Error() + errMsg = strings.ToLower(errMsg) + if strings.Contains(errMsg, "execution reverted") || + strings.Contains(errMsg, "vm execution error") { + // Reversions happen for minipool v1 on Prater which didn't have version() yet + version = 1 + } else { + return nil, fmt.Errorf("error getting minipool contract version: %w", err) + } + } + + switch version { + case 1, 2: + return newMinipool_v2(rp, address) + case 3: + return newMinipool_v3(rp, address, opts) + default: + return nil, fmt.Errorf("unexpected minipool contract version [%d]", version) + } +} + +// Create a minipool binding from an explicit version number +func NewMinipoolFromVersion(rp *rocketpool.RocketPool, address common.Address, version uint8, opts *bind.CallOpts) (Minipool, error) { + switch version { + case 1, 2: + return newMinipool_v2(rp, address) + case 3: + return newMinipool_v3(rp, address, opts) + default: + return nil, fmt.Errorf("unexpected minipool contract version [%d]", version) + } +} + +// Create a minipool contract directly from its ABI, encoded in string form +func createMinipoolContractFromEncodedAbi(rp *rocketpool.RocketPool, address common.Address, encodedAbi string) (*rocketpool.Contract, error) { + // Decode ABI + abi, err := rocketpool.DecodeAbi(encodedAbi) + if err != nil { + return nil, fmt.Errorf("error decoding minipool %s ABI: %w", address, err) + } + + // Create and return + return &rocketpool.Contract{ + Contract: bind.NewBoundContract(address, *abi, rp.Client, rp.Client, rp.Client), + Address: &address, + ABI: abi, + Client: rp.Client, + }, nil +} + +// Create a minipool contract directly from its ABI +func createMinipoolContractFromAbi(rp *rocketpool.RocketPool, address common.Address, abi *abi.ABI) (*rocketpool.Contract, error) { + // Create and return + return &rocketpool.Contract{ + Contract: bind.NewBoundContract(address, *abi, rp.Client, rp.Client, rp.Client), + Address: &address, + ABI: abi, + Client: rp.Client, + }, nil +} + +// Get a minipool contract +var rocketMinipoolLock sync.Mutex + +func getMinipoolContract(rp *rocketpool.RocketPool, minipoolAddress common.Address, opts *bind.CallOpts) (*rocketpool.Contract, error) { + rocketMinipoolLock.Lock() + defer rocketMinipoolLock.Unlock() + return rp.MakeContract("rocketMinipool", minipoolAddress, opts) +} diff --git a/bindings/minipool/minipool-contract-v2.go b/bindings/minipool/minipool-contract-v2.go new file mode 100644 index 000000000..c76b2a92e --- /dev/null +++ b/bindings/minipool/minipool-contract-v2.go @@ -0,0 +1,610 @@ +package minipool + +import ( + "context" + "fmt" + "math/big" + "time" + + "github.com/ethereum/go-ethereum/core/types" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "golang.org/x/sync/errgroup" + + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/storage" + rptypes "github.com/rocket-pool/smartnode/bindings/types" + "github.com/rocket-pool/smartnode/bindings/utils/eth" +) + +const ( + minipoolV2EncodedAbi string = "eJzdWd1v2jAQ/1cqnvvUaVPVt3ZdpUnrVEG7PVQVcpIDLIyN7HMYqva/7xwgHyRAKHGT7qkNXO5+97vzfZjn1x6TSi5nypre1YgJA+c9LucW6fH5lf6N4A9EvSvUNvkGQUsmHpdz6F31WBRpMKZ33pNs5j4YaTWjJyx+/fc8pyi1UdBk6fni85dMEyNEEjNdG4G36EJOf8qaXlKBbzgBfQtzZTiS3lQUYiAMzmSTJJFsaAt2TiFKqgiuGyTLGtBN6kOFTNwwwWRYFQRv4fzNcRJptmDiQauQ2PUfWFQfNfc3ZMm3U1SNJ1gi5BiKmeARQ6UfbDCFZWZtJVfDwV0KB3wsGVoNb9X56SLTGq1KwS1D1lcKt1SS5DtHdcvpRZraXzVEFCZOb73B7+OT5Z5LPleKjhQYZNNTjlTTkAahtkHg/5DPYBaAbuagH3QuceqXar4pOVuXGRAKJlpThHLpyaXE1NOcTm21V6kP2TsxaMOVK07KYs7DfS6VnCF1zk24t8gCLjgunWYOi0xyZGWIztAOHGPAwYapPUhA2tlZmpebF/ziuuNkn6+a3B5oASGqwpJ83ihFN0KF08MRKyRPdeI0BulxlZttISpZK9WW4c7iUvQmXxVaDvZ6ak4s1j8U67e8n4qfbjhOWd6DrhSK6hg0BOkO2szDEpx1NLIhvTPI+kCCUQeBrSm7Nobmzi6cwyeTbrAdoyuHrJN0bUC13B0K8B7d0pyW+QO1q+2mJQtFtnIsPqITDKNCRyl1hbUYve7WHpp4CuRU+klj8pwtWSDgaG+3Nq9hrQW2noYDG+v+DXV4eEXNuJJZwTpMVk2mMu02O0oetOukAzQZ40y3EcxM/KgercdxP9pDJgdu/W6ljnYxw02JjeaSBDC9Sly96MFIxA1qHliEdfO+ltGd1xQqWfRaRkstahjsvBHOp7kIrSAYbuIaTJju1PZ2ok9uAmnbp0I6GCViX/VKKF95HNN8lAxKXvM3VBI1C/Gsr8Kpu05Qmo3huxMasSTimxzQeYFjpqK256p6fBERVDdsSP6dfNdb8liJ6BYEjAnILoePUyhhcZLC4283N+b6SgigxTW5Amv0hvx/Zu1pPtYs8n+H/5F/pu5DCDyu5qjOvM2ECFxa1pTXK3M7+0Yxcp5mldyhCtjWrXLjG19hah7S+Idcjium52w+pFb+gxAYzB0bDzSMD1lq6QK4DpL3u1990BBzKhNdw/VtNAKSiaElYC//AGQZTdM=" +) + +type MinipoolV2 interface { + Minipool + EstimateDistributeBalanceAndFinaliseGas(opts *bind.TransactOpts) (rocketpool.GasInfo, error) + DistributeBalanceAndFinalise(opts *bind.TransactOpts) (common.Hash, error) + EstimateDistributeBalanceGas(opts *bind.TransactOpts) (rocketpool.GasInfo, error) + DistributeBalance(opts *bind.TransactOpts) (common.Hash, error) +} + +// Minipool contract +type minipool_v2 struct { + Address common.Address + Version uint8 + Contract *rocketpool.Contract + RocketPool *rocketpool.RocketPool +} + +// The decoded ABI for v2 minipools +var minipoolV2Abi *abi.ABI + +// Create new minipool contract +func newMinipool_v2(rp *rocketpool.RocketPool, address common.Address) (Minipool, error) { + + var contract *rocketpool.Contract + var err error + if minipoolV2Abi == nil { + // Get contract + contract, err = createMinipoolContractFromEncodedAbi(rp, address, minipoolV2EncodedAbi) + } else { + contract, err = createMinipoolContractFromAbi(rp, address, minipoolV2Abi) + } + if err != nil { + return nil, err + } else if minipoolV2Abi == nil { + minipoolV2Abi = contract.ABI + } + + // Create and return + return &minipool_v2{ + Address: address, + Version: 2, + Contract: contract, + RocketPool: rp, + }, nil +} + +// Get the minipool as a v2 minipool if it implements the required methods +func GetMinipoolAsV2(mp Minipool) (MinipoolV2, bool) { + castedMp, ok := mp.(MinipoolV2) + if ok { + return castedMp, true + } + return nil, false +} + +// Get the contract +func (mp *minipool_v2) GetContract() *rocketpool.Contract { + return mp.Contract +} + +// Get the contract address +func (mp *minipool_v2) GetAddress() common.Address { + return mp.Address +} + +// Get the contract version +func (mp *minipool_v2) GetVersion() uint8 { + return mp.Version +} + +// Get status details +func (mp *minipool_v2) GetStatusDetails(opts *bind.CallOpts) (StatusDetails, error) { + + // Data + var wg errgroup.Group + var status rptypes.MinipoolStatus + var statusBlock uint64 + var statusTime time.Time + + // Load data + wg.Go(func() error { + var err error + status, err = mp.GetStatus(opts) + return err + }) + wg.Go(func() error { + var err error + statusBlock, err = mp.GetStatusBlock(opts) + return err + }) + wg.Go(func() error { + var err error + statusTime, err = mp.GetStatusTime(opts) + return err + }) + + // Wait for data + if err := wg.Wait(); err != nil { + return StatusDetails{}, err + } + + // Return + return StatusDetails{ + Status: status, + StatusBlock: statusBlock, + StatusTime: statusTime, + }, nil + +} +func (mp *minipool_v2) GetStatus(opts *bind.CallOpts) (rptypes.MinipoolStatus, error) { + status := new(uint8) + if err := mp.Contract.Call(opts, status, "getStatus"); err != nil { + return 0, fmt.Errorf("error getting minipool %s status: %w", mp.Address.Hex(), err) + } + return rptypes.MinipoolStatus(*status), nil +} +func (mp *minipool_v2) GetStatusBlock(opts *bind.CallOpts) (uint64, error) { + statusBlock := new(*big.Int) + if err := mp.Contract.Call(opts, statusBlock, "getStatusBlock"); err != nil { + return 0, fmt.Errorf("error getting minipool %s status changed block: %w", mp.Address.Hex(), err) + } + return (*statusBlock).Uint64(), nil +} +func (mp *minipool_v2) GetStatusTime(opts *bind.CallOpts) (time.Time, error) { + statusTime := new(*big.Int) + if err := mp.Contract.Call(opts, statusTime, "getStatusTime"); err != nil { + return time.Unix(0, 0), fmt.Errorf("error getting minipool %s status changed time: %w", mp.Address.Hex(), err) + } + return time.Unix((*statusTime).Int64(), 0), nil +} +func (mp *minipool_v2) GetFinalised(opts *bind.CallOpts) (bool, error) { + finalised := new(bool) + if err := mp.Contract.Call(opts, finalised, "getFinalised"); err != nil { + return false, fmt.Errorf("error getting minipool %s finalised: %w", mp.Address.Hex(), err) + } + return *finalised, nil +} + +// Get deposit type +func (mp *minipool_v2) GetDepositType(opts *bind.CallOpts) (rptypes.MinipoolDeposit, error) { + depositType := new(uint8) + if err := mp.Contract.Call(opts, depositType, "getDepositType"); err != nil { + return 0, fmt.Errorf("error getting minipool %s deposit type: %w", mp.Address.Hex(), err) + } + return rptypes.MinipoolDeposit(*depositType), nil +} + +// Get node details +func (mp *minipool_v2) GetNodeDetails(opts *bind.CallOpts) (NodeDetails, error) { + + // Data + var wg errgroup.Group + var address common.Address + var fee float64 + var depositBalance *big.Int + var refundBalance *big.Int + var depositAssigned bool + + // Load data + wg.Go(func() error { + var err error + address, err = mp.GetNodeAddress(opts) + return err + }) + wg.Go(func() error { + var err error + fee, err = mp.GetNodeFee(opts) + return err + }) + wg.Go(func() error { + var err error + depositBalance, err = mp.GetNodeDepositBalance(opts) + return err + }) + wg.Go(func() error { + var err error + refundBalance, err = mp.GetNodeRefundBalance(opts) + return err + }) + wg.Go(func() error { + var err error + depositAssigned, err = mp.GetNodeDepositAssigned(opts) + return err + }) + + // Wait for data + if err := wg.Wait(); err != nil { + return NodeDetails{}, err + } + + // Return + return NodeDetails{ + Address: address, + Fee: fee, + DepositBalance: depositBalance, + RefundBalance: refundBalance, + DepositAssigned: depositAssigned, + }, nil + +} +func (mp *minipool_v2) GetNodeAddress(opts *bind.CallOpts) (common.Address, error) { + nodeAddress := new(common.Address) + if err := mp.Contract.Call(opts, nodeAddress, "getNodeAddress"); err != nil { + return common.Address{}, fmt.Errorf("error getting minipool %s node address: %w", mp.Address.Hex(), err) + } + return *nodeAddress, nil +} +func (mp *minipool_v2) GetNodeFee(opts *bind.CallOpts) (float64, error) { + nodeFee := new(*big.Int) + if err := mp.Contract.Call(opts, nodeFee, "getNodeFee"); err != nil { + return 0, fmt.Errorf("error getting minipool %s node fee: %w", mp.Address.Hex(), err) + } + return eth.WeiToEth(*nodeFee), nil +} +func (mp *minipool_v2) GetNodeFeeRaw(opts *bind.CallOpts) (*big.Int, error) { + nodeFee := new(*big.Int) + if err := mp.Contract.Call(opts, nodeFee, "getNodeFee"); err != nil { + return nil, fmt.Errorf("error getting minipool %s node fee: %w", mp.Address.Hex(), err) + } + return *nodeFee, nil +} +func (mp *minipool_v2) GetNodeDepositBalance(opts *bind.CallOpts) (*big.Int, error) { + nodeDepositBalance := new(*big.Int) + if err := mp.Contract.Call(opts, nodeDepositBalance, "getNodeDepositBalance"); err != nil { + return nil, fmt.Errorf("error getting minipool %s node deposit balance: %w", mp.Address.Hex(), err) + } + return *nodeDepositBalance, nil +} +func (mp *minipool_v2) GetNodeRefundBalance(opts *bind.CallOpts) (*big.Int, error) { + nodeRefundBalance := new(*big.Int) + if err := mp.Contract.Call(opts, nodeRefundBalance, "getNodeRefundBalance"); err != nil { + return nil, fmt.Errorf("error getting minipool %s node refund balance: %w", mp.Address.Hex(), err) + } + return *nodeRefundBalance, nil +} +func (mp *minipool_v2) GetNodeDepositAssigned(opts *bind.CallOpts) (bool, error) { + nodeDepositAssigned := new(bool) + if err := mp.Contract.Call(opts, nodeDepositAssigned, "getNodeDepositAssigned"); err != nil { + return false, fmt.Errorf("error getting minipool %s node deposit assigned status: %w", mp.Address.Hex(), err) + } + return *nodeDepositAssigned, nil +} + +// Get user deposit details +func (mp *minipool_v2) GetUserDetails(opts *bind.CallOpts) (UserDetails, error) { + + // Data + var wg errgroup.Group + var depositBalance *big.Int + var depositAssigned bool + var depositAssignedTime time.Time + + // Load data + wg.Go(func() error { + var err error + depositBalance, err = mp.GetUserDepositBalance(opts) + return err + }) + wg.Go(func() error { + var err error + depositAssigned, err = mp.GetUserDepositAssigned(opts) + return err + }) + wg.Go(func() error { + var err error + depositAssignedTime, err = mp.GetUserDepositAssignedTime(opts) + return err + }) + + // Wait for data + if err := wg.Wait(); err != nil { + return UserDetails{}, err + } + + // Return + return UserDetails{ + DepositBalance: depositBalance, + DepositAssigned: depositAssigned, + DepositAssignedTime: depositAssignedTime, + }, nil + +} +func (mp *minipool_v2) GetUserDepositBalance(opts *bind.CallOpts) (*big.Int, error) { + userDepositBalance := new(*big.Int) + if err := mp.Contract.Call(opts, userDepositBalance, "getUserDepositBalance"); err != nil { + return nil, fmt.Errorf("error getting minipool %s user deposit balance: %w", mp.Address.Hex(), err) + } + return *userDepositBalance, nil +} +func (mp *minipool_v2) GetUserDepositAssigned(opts *bind.CallOpts) (bool, error) { + userDepositAssigned := new(bool) + if err := mp.Contract.Call(opts, userDepositAssigned, "getUserDepositAssigned"); err != nil { + return false, fmt.Errorf("error getting minipool %s user deposit assigned status: %w", mp.Address.Hex(), err) + } + return *userDepositAssigned, nil +} +func (mp *minipool_v2) GetUserDepositAssignedTime(opts *bind.CallOpts) (time.Time, error) { + depositAssignedTime := new(*big.Int) + if err := mp.Contract.Call(opts, depositAssignedTime, "getUserDepositAssignedTime"); err != nil { + return time.Unix(0, 0), fmt.Errorf("error getting minipool %s user deposit assigned time: %w", mp.Address.Hex(), err) + } + return time.Unix((*depositAssignedTime).Int64(), 0), nil +} + +// Estimate the gas of Refund +func (mp *minipool_v2) EstimateRefundGas(opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return mp.Contract.GetTransactionGasInfo(opts, "refund") +} + +// Refund node ETH from the minipool +func (mp *minipool_v2) Refund(opts *bind.TransactOpts) (common.Hash, error) { + tx, err := mp.Contract.Transact(opts, "refund") + if err != nil { + return common.Hash{}, fmt.Errorf("error refunding from minipool %s: %w", mp.Address.Hex(), err) + } + return tx.Hash(), nil +} + +// Estimate the gas of DistributeBalance +func (mp *minipool_v2) EstimateDistributeBalanceGas(opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return mp.Contract.GetTransactionGasInfo(opts, "distributeBalance") +} + +// Distribute the minipool's ETH balance to the node operator and rETH staking pool. +// !!! WARNING !!! +// DO NOT CALL THIS until the minipool's validator has exited from the Beacon Chain +// and the balance has been deposited into the minipool! +func (mp *minipool_v2) DistributeBalance(opts *bind.TransactOpts) (common.Hash, error) { + tx, err := mp.Contract.Transact(opts, "distributeBalance") + if err != nil { + return common.Hash{}, fmt.Errorf("error processing withdrawal for minipool %s: %w", mp.Address.Hex(), err) + } + return tx.Hash(), nil +} + +// Estimate the gas of DistributeBalanceAndFinalise +func (mp *minipool_v2) EstimateDistributeBalanceAndFinaliseGas(opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return mp.Contract.GetTransactionGasInfo(opts, "distributeBalanceAndFinalise") +} + +// Distribute the minipool's ETH balance to the node operator and rETH staking pool, +// then finalises the minipool +// !!! WARNING !!! +// DO NOT CALL THIS until the minipool's validator has exited from the Beacon Chain +// and the balance has been deposited into the minipool! +func (mp *minipool_v2) DistributeBalanceAndFinalise(opts *bind.TransactOpts) (common.Hash, error) { + tx, err := mp.Contract.Transact(opts, "distributeBalanceAndFinalise") + if err != nil { + return common.Hash{}, fmt.Errorf("error processing withdrawal for and finalise minipool %s: %w", mp.Address.Hex(), err) + } + return tx.Hash(), nil +} + +// Estimate the gas of Stake +func (mp *minipool_v2) EstimateStakeGas(validatorSignature rptypes.ValidatorSignature, depositDataRoot common.Hash, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return mp.Contract.GetTransactionGasInfo(opts, "stake", validatorSignature[:], depositDataRoot) +} + +// Progress the prelaunch minipool to staking +func (mp *minipool_v2) Stake(validatorSignature rptypes.ValidatorSignature, depositDataRoot common.Hash, opts *bind.TransactOpts) (common.Hash, error) { + tx, err := mp.Contract.Transact(opts, "stake", validatorSignature[:], depositDataRoot) + if err != nil { + return common.Hash{}, fmt.Errorf("error staking minipool %s: %w", mp.Address.Hex(), err) + } + return tx.Hash(), nil +} + +// Estimate the gas of Dissolve +func (mp *minipool_v2) EstimateDissolveGas(opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return mp.Contract.GetTransactionGasInfo(opts, "dissolve") +} + +// Dissolve the initialized or prelaunch minipool +func (mp *minipool_v2) Dissolve(opts *bind.TransactOpts) (common.Hash, error) { + tx, err := mp.Contract.Transact(opts, "dissolve") + if err != nil { + return common.Hash{}, fmt.Errorf("error dissolving minipool %s: %w", mp.Address.Hex(), err) + } + return tx.Hash(), nil +} + +// Estimate the gas of Close +func (mp *minipool_v2) EstimateCloseGas(opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return mp.Contract.GetTransactionGasInfo(opts, "close") +} + +// Withdraw node balances from the dissolved minipool and close it +func (mp *minipool_v2) Close(opts *bind.TransactOpts) (common.Hash, error) { + tx, err := mp.Contract.Transact(opts, "close") + if err != nil { + return common.Hash{}, fmt.Errorf("error closing minipool %s: %w", mp.Address.Hex(), err) + } + return tx.Hash(), nil +} + +// Estimate the gas of Finalise +func (mp *minipool_v2) EstimateFinaliseGas(opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return mp.Contract.GetTransactionGasInfo(opts, "finalise") +} + +// Finalise a minipool to get the RPL stake back +func (mp *minipool_v2) Finalise(opts *bind.TransactOpts) (common.Hash, error) { + tx, err := mp.Contract.Transact(opts, "finalise") + if err != nil { + return common.Hash{}, fmt.Errorf("error finalizing minipool %s: %w", mp.Address.Hex(), err) + } + return tx.Hash(), nil +} + +// Estimate the gas of DelegateUpgrade +func (mp *minipool_v2) EstimateDelegateUpgradeGas(opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return mp.Contract.GetTransactionGasInfo(opts, "delegateUpgrade") +} + +// Upgrade this minipool to the latest network delegate contract +func (mp *minipool_v2) DelegateUpgrade(opts *bind.TransactOpts) (common.Hash, error) { + tx, err := mp.Contract.Transact(opts, "delegateUpgrade") + if err != nil { + return common.Hash{}, fmt.Errorf("error upgrading delegate for minipool %s: %w", mp.Address.Hex(), err) + } + return tx.Hash(), nil +} + +// Estimate the gas of DelegateRollback +func (mp *minipool_v2) EstimateDelegateRollbackGas(opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return mp.Contract.GetTransactionGasInfo(opts, "delegateRollback") +} + +// Rollback to previous delegate contract +func (mp *minipool_v2) DelegateRollback(opts *bind.TransactOpts) (common.Hash, error) { + tx, err := mp.Contract.Transact(opts, "delegateRollback") + if err != nil { + return common.Hash{}, fmt.Errorf("error rolling back delegate for minipool %s: %w", mp.Address.Hex(), err) + } + return tx.Hash(), nil +} + +// Estimate the gas of SetUseLatestDelegate +func (mp *minipool_v2) EstimateSetUseLatestDelegateGas(setting bool, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return mp.Contract.GetTransactionGasInfo(opts, "setUseLatestDelegate", setting) +} + +// If set to true, will automatically use the latest delegate contract +func (mp *minipool_v2) SetUseLatestDelegate(setting bool, opts *bind.TransactOpts) (common.Hash, error) { + tx, err := mp.Contract.Transact(opts, "setUseLatestDelegate", setting) + if err != nil { + return common.Hash{}, fmt.Errorf("error setting use latest delegate for minipool %s: %w", mp.Address.Hex(), err) + } + return tx.Hash(), nil +} + +// Getter for useLatestDelegate setting +func (mp *minipool_v2) GetUseLatestDelegate(opts *bind.CallOpts) (bool, error) { + setting := new(bool) + if err := mp.Contract.Call(opts, setting, "getUseLatestDelegate"); err != nil { + return false, fmt.Errorf("error getting use latest delegate for minipool %s: %w", mp.Address.Hex(), err) + } + return *setting, nil +} + +// Returns the address of the minipool's stored delegate +func (mp *minipool_v2) GetDelegate(opts *bind.CallOpts) (common.Address, error) { + address := new(common.Address) + if err := mp.Contract.Call(opts, address, "getDelegate"); err != nil { + return common.Address{}, fmt.Errorf("error getting delegate for minipool %s: %w", mp.Address.Hex(), err) + } + return *address, nil +} + +// Returns the address of the minipool's previous delegate (or address(0) if not set) +func (mp *minipool_v2) GetPreviousDelegate(opts *bind.CallOpts) (common.Address, error) { + address := new(common.Address) + if err := mp.Contract.Call(opts, address, "getPreviousDelegate"); err != nil { + return common.Address{}, fmt.Errorf("error getting previous delegate for minipool %s: %w", mp.Address.Hex(), err) + } + return *address, nil +} + +// Returns the delegate which will be used when calling this minipool taking into account useLatestDelegate setting +func (mp *minipool_v2) GetEffectiveDelegate(opts *bind.CallOpts) (common.Address, error) { + address := new(common.Address) + if err := mp.Contract.Call(opts, address, "getEffectiveDelegate"); err != nil { + return common.Address{}, fmt.Errorf("error getting effective delegate for minipool %s: %w", mp.Address.Hex(), err) + } + return *address, nil +} + +// Given a validator balance, calculates how much belongs to the node taking into consideration rewards and penalties +func (mp *minipool_v2) CalculateNodeShare(balance *big.Int, opts *bind.CallOpts) (*big.Int, error) { + nodeAmount := new(*big.Int) + if err := mp.Contract.Call(opts, nodeAmount, "calculateNodeShare", balance); err != nil { + return nil, fmt.Errorf("error getting minipool node portion: %w", err) + } + return *nodeAmount, nil +} + +// Given a validator balance, calculates how much belongs to rETH users taking into consideration rewards and penalties +func (mp *minipool_v2) CalculateUserShare(balance *big.Int, opts *bind.CallOpts) (*big.Int, error) { + userAmount := new(*big.Int) + if err := mp.Contract.Call(opts, userAmount, "calculateUserShare", balance); err != nil { + return nil, fmt.Errorf("error getting minipool user portion: %w", err) + } + return *userAmount, nil +} + +// Estimate the gas requiired to vote to scrub a minipool +func (mp *minipool_v2) EstimateVoteScrubGas(opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return mp.Contract.GetTransactionGasInfo(opts, "voteScrub") +} + +// Vote to scrub a minipool +func (mp *minipool_v2) VoteScrub(opts *bind.TransactOpts) (common.Hash, error) { + tx, err := mp.Contract.Transact(opts, "voteScrub") + if err != nil { + return common.Hash{}, fmt.Errorf("error voting to scrub minipool %s: %w", mp.Address.Hex(), err) + } + return tx.Hash(), nil +} + +// Get the data from this minipool's MinipoolPrestaked event +func (mp *minipool_v2) GetPrestakeEvent(intervalSize *big.Int, opts *bind.CallOpts) (PrestakeData, error) { + + addressFilter := []common.Address{mp.Address} + topicFilter := [][]common.Hash{{mp.Contract.ABI.Events["MinipoolPrestaked"].ID}} + + // Grab the latest block number + currentBlock, err := mp.RocketPool.Client.BlockNumber(context.Background()) + if err != nil { + return PrestakeData{}, fmt.Errorf("Error getting current block %s: %w", mp.Address.Hex(), err) + } + + // Grab the lowest block number worth querying from (should never have to go back this far in practice) + fromBlockBig, err := storage.GetDeployBlock(mp.RocketPool) + if err != nil { + return PrestakeData{}, fmt.Errorf("Error getting deploy block %s: %w", mp.Address.Hex(), err) + } + + fromBlock := fromBlockBig.Uint64() + var log types.Log + found := false + + // Backwards scan through blocks to find the event + for i := currentBlock; i >= fromBlock; i -= EventScanInterval { + from := i - EventScanInterval + 1 + if from < fromBlock { + from = fromBlock + } + + fromBig := big.NewInt(0).SetUint64(from) + toBig := big.NewInt(0).SetUint64(i) + + logs, err := eth.GetLogs(mp.RocketPool, addressFilter, topicFilter, intervalSize, fromBig, toBig, nil) + if err != nil { + return PrestakeData{}, fmt.Errorf("Error getting prestake logs for minipool %s: %w", mp.Address.Hex(), err) + } + + if len(logs) > 0 { + log = logs[0] + found = true + break + } + } + + if !found { + // This should never happen + return PrestakeData{}, fmt.Errorf("Error finding prestake log for minipool %s", mp.Address.Hex()) + } + + // Decode the event + prestakeEvent := new(MinipoolPrestakeEvent) + mp.Contract.Contract.UnpackLog(prestakeEvent, "MinipoolPrestaked", log) + if err != nil { + return PrestakeData{}, fmt.Errorf("Error unpacking prestake data: %w", err) + } + + // Convert the event to a more useable struct + prestakeData := PrestakeData{ + Pubkey: rptypes.BytesToValidatorPubkey(prestakeEvent.Pubkey), + WithdrawalCredentials: common.BytesToHash(prestakeEvent.WithdrawalCredentials), + Amount: prestakeEvent.Amount, + Signature: rptypes.BytesToValidatorSignature(prestakeEvent.Signature), + DepositDataRoot: prestakeEvent.DepositDataRoot, + Time: time.Unix(prestakeEvent.Time.Int64(), 0), + } + return prestakeData, nil +} diff --git a/bindings/minipool/minipool-contract-v3.go b/bindings/minipool/minipool-contract-v3.go new file mode 100644 index 000000000..9e1e22540 --- /dev/null +++ b/bindings/minipool/minipool-contract-v3.go @@ -0,0 +1,651 @@ +package minipool + +import ( + "context" + "fmt" + "math/big" + "time" + + "github.com/ethereum/go-ethereum/core/types" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "golang.org/x/sync/errgroup" + + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/storage" + rptypes "github.com/rocket-pool/smartnode/bindings/types" + "github.com/rocket-pool/smartnode/bindings/utils/eth" +) + +const ( + minipoolV3EncodedAbi string = "eJztWltv2koQ/isVz3lq1SrqW3Jon5qeCJKch6pCY+8Aqyy71l7MQdH57x0bY2Mw2MAau0d9SsDjmW8uO7flx9sApJKrhXJm8HkKwuDNgMvIWfr4443+Zfgvsq1HFrUE8bSKcPB54Ojz+4+fBjcDCYvki0hjzInXvZLsjphKS89smfi/m9P5Slz6Zmk5/dnn9DMnSASOkLmQmOZ0GCMBSOQ1s5vVbh8LMKbRmALLVKtFIWPz+Byt4JoW+mLnqIcYKcNt+0Yi2tCV5FxiKKkY+gwnZ1B7DU9lQdyDABlWOaE1d/7D7ZxpWIJ41Cok67bvWKt+19jfGEueb6JqPMHK4paFYhCcgVX60QWvuCqkrekaKHiI4ZjPJFin8VyeH94XXNk6FQzBwkgpu8OSKK/s1R2ll3lo/6WRkZs4vXWG3qcHywOXPFKKjhQaC6+XHCn/kNRCXZS9fSMah9oFwRUQBZ47itBpTUCvm7Q3VnuBkKSuKMIi0O3n7AUuAtR+8natjmlEvFwWpZVqJbJuCyB0Nq0zZSi3LamUinqOKAlXa5XrULwT4IzLZ+ozhtxYzQNqhuhN5WxBmWiAD85CwAW3q7TPkRGsIBBbeKZOhpYrWRb0VqvVJDgY2gXKEEToBMH4Th3WeA66DLJeSjX3fc1ijstr65RYv2udtmHJLH0fhxNQfqjCkn7vCcg4qWtdwxDKeD0SP3dbm3rmp3PmxigRt3uUy6afaFyCZuZvKVYVbiiAZVmmqGgtWHbKCSZvy28ztNmAmlriaHyidIt3m3o6zB1eeYRvvQQsofuaqc86PjsEJUnYd3mhPwJmrxuo6AP8Qco8cWcMzSp9sVMGqupkdFUNMmRfsVdwRkiErId2elLRc/QCwvUGFfXu34DI5n1D9cBnGhKy7t1Yn4smB0eTklqlmaLjhDLezB1Ni1P+Qru1aS3mXqjwtS/RuIb0tB6veoHoKVnU5tFU48XrwUrnxL5VzgpQfXLlFrzuM90+srwf74En03WT7QRHgxIgS53s4TpA6TRZAnuePposHZRkmypfuSU8tseeNLoOaMahZv9/fN8/qV/4F7aONA49D9ENDX18x1vDoHapW9Iw2b1mm9h25tmoYt/jjblOb7tLF+2tSEkmgnZ4GwFm3u4mpfuTZPZWbd7s54oK2MKqy5Wq2J0Qatl5LYtRm+RxbaG/rYLhr92O6VSnXaRnxzZ7PVSS3OJCiuOL79n2yrESbIgCZ2C3JJ5wYbTHUOLyIoanX9dsxI2UEMjuIR2IMnI/t5P/Z6s9RzMN7M/PqA6YKf0pyQhD5HHju0C2FZAB7MznHm89Sg5sR0i6os/jshdr5y/TKRJNjH0D9pj9vrJvuGg+/UZMTENPdjcZahW+JlskpWF2MA3+XtPlzt2eQWu5nFUYddM+1rnrPIBN+kPIklVDer3OiQT+F40AQik=" +) + +type MinipoolV3 interface { + Minipool + EstimateReduceBondAmountGas(opts *bind.TransactOpts) (rocketpool.GasInfo, error) + ReduceBondAmount(opts *bind.TransactOpts) (common.Hash, error) + EstimatePromoteGas(opts *bind.TransactOpts) (rocketpool.GasInfo, error) + Promote(opts *bind.TransactOpts) (common.Hash, error) + GetPreMigrationBalance(opts *bind.CallOpts) (*big.Int, error) + GetUserDistributed(opts *bind.CallOpts) (bool, error) + EstimateDistributeBalanceGas(rewardsOnly bool, opts *bind.TransactOpts) (rocketpool.GasInfo, error) + DistributeBalance(rewardsOnly bool, opts *bind.TransactOpts) (common.Hash, error) +} + +// Minipool contract +type minipool_v3 struct { + Address common.Address + Version uint8 + Contract *rocketpool.Contract + RocketPool *rocketpool.RocketPool +} + +// The decoded ABI for v2 minipools +var minipoolV3Abi *abi.ABI + +// Create new minipool contract +func newMinipool_v3(rp *rocketpool.RocketPool, address common.Address, opts *bind.CallOpts) (Minipool, error) { + + var contract *rocketpool.Contract + var err error + if minipoolV3Abi == nil { + // Get contract + contract, err = createMinipoolContractFromEncodedAbi(rp, address, minipoolV3EncodedAbi) + } else { + contract, err = createMinipoolContractFromAbi(rp, address, minipoolV3Abi) + } + if err != nil { + return nil, err + } else if minipoolV3Abi == nil { + minipoolV3Abi = contract.ABI + } + + // Create and return + return &minipool_v3{ + Address: address, + Version: 3, + Contract: contract, + RocketPool: rp, + }, nil +} + +// Get the minipool as a v3 minipool if it implements the required methods +func GetMinipoolAsV3(mp Minipool) (MinipoolV3, bool) { + castedMp, ok := mp.(MinipoolV3) + if ok { + return castedMp, true + } + return nil, false +} + +// Get the contract +func (mp *minipool_v3) GetContract() *rocketpool.Contract { + return mp.Contract +} + +// Get the contract address +func (mp *minipool_v3) GetAddress() common.Address { + return mp.Address +} + +// Get the contract version +func (mp *minipool_v3) GetVersion() uint8 { + return mp.Version +} + +// Get status details +func (mp *minipool_v3) GetStatusDetails(opts *bind.CallOpts) (StatusDetails, error) { + + // Data + var wg errgroup.Group + var status rptypes.MinipoolStatus + var statusBlock uint64 + var statusTime time.Time + var isVacant bool + + // Load data + wg.Go(func() error { + var err error + status, err = mp.GetStatus(opts) + return err + }) + wg.Go(func() error { + var err error + statusBlock, err = mp.GetStatusBlock(opts) + return err + }) + wg.Go(func() error { + var err error + statusTime, err = mp.GetStatusTime(opts) + return err + }) + wg.Go(func() error { + var err error + isVacant, err = mp.GetVacant(opts) + return err + }) + + // Wait for data + if err := wg.Wait(); err != nil { + return StatusDetails{}, err + } + + // Return + return StatusDetails{ + Status: status, + StatusBlock: statusBlock, + StatusTime: statusTime, + IsVacant: isVacant, + }, nil + +} +func (mp *minipool_v3) GetStatus(opts *bind.CallOpts) (rptypes.MinipoolStatus, error) { + status := new(uint8) + if err := mp.Contract.Call(opts, status, "getStatus"); err != nil { + return 0, fmt.Errorf("error getting minipool %s status: %w", mp.Address.Hex(), err) + } + return rptypes.MinipoolStatus(*status), nil +} +func (mp *minipool_v3) GetStatusBlock(opts *bind.CallOpts) (uint64, error) { + statusBlock := new(*big.Int) + if err := mp.Contract.Call(opts, statusBlock, "getStatusBlock"); err != nil { + return 0, fmt.Errorf("error getting minipool %s status changed block: %w", mp.Address.Hex(), err) + } + return (*statusBlock).Uint64(), nil +} +func (mp *minipool_v3) GetStatusTime(opts *bind.CallOpts) (time.Time, error) { + statusTime := new(*big.Int) + if err := mp.Contract.Call(opts, statusTime, "getStatusTime"); err != nil { + return time.Unix(0, 0), fmt.Errorf("error getting minipool %s status changed time: %w", mp.Address.Hex(), err) + } + return time.Unix((*statusTime).Int64(), 0), nil +} +func (mp *minipool_v3) GetFinalised(opts *bind.CallOpts) (bool, error) { + finalised := new(bool) + if err := mp.Contract.Call(opts, finalised, "getFinalised"); err != nil { + return false, fmt.Errorf("error getting minipool %s finalised: %w", mp.Address.Hex(), err) + } + return *finalised, nil +} + +// Get deposit type +func (mp *minipool_v3) GetDepositType(opts *bind.CallOpts) (rptypes.MinipoolDeposit, error) { + depositType := new(uint8) + if err := mp.Contract.Call(opts, depositType, "getDepositType"); err != nil { + return 0, fmt.Errorf("error getting minipool %s deposit type: %w", mp.Address.Hex(), err) + } + return rptypes.MinipoolDeposit(*depositType), nil +} + +// Get node details +func (mp *minipool_v3) GetNodeDetails(opts *bind.CallOpts) (NodeDetails, error) { + + // Data + var wg errgroup.Group + var address common.Address + var fee float64 + var depositBalance *big.Int + var refundBalance *big.Int + var depositAssigned bool + + // Load data + wg.Go(func() error { + var err error + address, err = mp.GetNodeAddress(opts) + return err + }) + wg.Go(func() error { + var err error + fee, err = mp.GetNodeFee(opts) + return err + }) + wg.Go(func() error { + var err error + depositBalance, err = mp.GetNodeDepositBalance(opts) + return err + }) + wg.Go(func() error { + var err error + refundBalance, err = mp.GetNodeRefundBalance(opts) + return err + }) + wg.Go(func() error { + var err error + depositAssigned, err = mp.GetNodeDepositAssigned(opts) + return err + }) + + // Wait for data + if err := wg.Wait(); err != nil { + return NodeDetails{}, err + } + + // Return + return NodeDetails{ + Address: address, + Fee: fee, + DepositBalance: depositBalance, + RefundBalance: refundBalance, + DepositAssigned: depositAssigned, + }, nil + +} +func (mp *minipool_v3) GetNodeAddress(opts *bind.CallOpts) (common.Address, error) { + nodeAddress := new(common.Address) + if err := mp.Contract.Call(opts, nodeAddress, "getNodeAddress"); err != nil { + return common.Address{}, fmt.Errorf("error getting minipool %s node address: %w", mp.Address.Hex(), err) + } + return *nodeAddress, nil +} +func (mp *minipool_v3) GetNodeFee(opts *bind.CallOpts) (float64, error) { + nodeFee := new(*big.Int) + if err := mp.Contract.Call(opts, nodeFee, "getNodeFee"); err != nil { + return 0, fmt.Errorf("error getting minipool %s node fee: %w", mp.Address.Hex(), err) + } + return eth.WeiToEth(*nodeFee), nil +} +func (mp *minipool_v3) GetNodeFeeRaw(opts *bind.CallOpts) (*big.Int, error) { + nodeFee := new(*big.Int) + if err := mp.Contract.Call(opts, nodeFee, "getNodeFee"); err != nil { + return nil, fmt.Errorf("error getting minipool %s node fee: %w", mp.Address.Hex(), err) + } + return *nodeFee, nil +} +func (mp *minipool_v3) GetNodeDepositBalance(opts *bind.CallOpts) (*big.Int, error) { + nodeDepositBalance := new(*big.Int) + if err := mp.Contract.Call(opts, nodeDepositBalance, "getNodeDepositBalance"); err != nil { + return nil, fmt.Errorf("error getting minipool %s node deposit balance: %w", mp.Address.Hex(), err) + } + return *nodeDepositBalance, nil +} +func (mp *minipool_v3) GetNodeRefundBalance(opts *bind.CallOpts) (*big.Int, error) { + nodeRefundBalance := new(*big.Int) + if err := mp.Contract.Call(opts, nodeRefundBalance, "getNodeRefundBalance"); err != nil { + return nil, fmt.Errorf("error getting minipool %s node refund balance: %w", mp.Address.Hex(), err) + } + return *nodeRefundBalance, nil +} +func (mp *minipool_v3) GetNodeDepositAssigned(opts *bind.CallOpts) (bool, error) { + nodeDepositAssigned := new(bool) + if err := mp.Contract.Call(opts, nodeDepositAssigned, "getNodeDepositAssigned"); err != nil { + return false, fmt.Errorf("error getting minipool %s node deposit assigned status: %w", mp.Address.Hex(), err) + } + return *nodeDepositAssigned, nil +} +func (mp *minipool_v3) GetVacant(opts *bind.CallOpts) (bool, error) { + isVacant := new(bool) + if err := mp.Contract.Call(opts, isVacant, "getVacant"); err != nil { + return false, fmt.Errorf("error getting minipool %s vacant status: %w", mp.Address.Hex(), err) + } + return *isVacant, nil +} +func (mp *minipool_v3) GetPreMigrationBalance(opts *bind.CallOpts) (*big.Int, error) { + preMigrationBalance := new(*big.Int) + if err := mp.Contract.Call(opts, preMigrationBalance, "getPreMigrationBalance"); err != nil { + return nil, fmt.Errorf("error getting minipool %s pre-migration balance: %w", mp.Address.Hex(), err) + } + return *preMigrationBalance, nil +} + +// Get user deposit details +func (mp *minipool_v3) GetUserDetails(opts *bind.CallOpts) (UserDetails, error) { + + // Data + var wg errgroup.Group + var depositBalance *big.Int + var depositAssigned bool + var depositAssignedTime time.Time + + // Load data + wg.Go(func() error { + var err error + depositBalance, err = mp.GetUserDepositBalance(opts) + return err + }) + wg.Go(func() error { + var err error + depositAssigned, err = mp.GetUserDepositAssigned(opts) + return err + }) + wg.Go(func() error { + var err error + depositAssignedTime, err = mp.GetUserDepositAssignedTime(opts) + return err + }) + + // Wait for data + if err := wg.Wait(); err != nil { + return UserDetails{}, err + } + + // Return + return UserDetails{ + DepositBalance: depositBalance, + DepositAssigned: depositAssigned, + DepositAssignedTime: depositAssignedTime, + }, nil + +} +func (mp *minipool_v3) GetUserDepositBalance(opts *bind.CallOpts) (*big.Int, error) { + userDepositBalance := new(*big.Int) + if err := mp.Contract.Call(opts, userDepositBalance, "getUserDepositBalance"); err != nil { + return nil, fmt.Errorf("error getting minipool %s user deposit balance: %w", mp.Address.Hex(), err) + } + return *userDepositBalance, nil +} +func (mp *minipool_v3) GetUserDepositAssigned(opts *bind.CallOpts) (bool, error) { + userDepositAssigned := new(bool) + if err := mp.Contract.Call(opts, userDepositAssigned, "getUserDepositAssigned"); err != nil { + return false, fmt.Errorf("error getting minipool %s user deposit assigned status: %w", mp.Address.Hex(), err) + } + return *userDepositAssigned, nil +} +func (mp *minipool_v3) GetUserDepositAssignedTime(opts *bind.CallOpts) (time.Time, error) { + depositAssignedTime := new(*big.Int) + if err := mp.Contract.Call(opts, depositAssignedTime, "getUserDepositAssignedTime"); err != nil { + return time.Unix(0, 0), fmt.Errorf("error getting minipool %s user deposit assigned time: %w", mp.Address.Hex(), err) + } + return time.Unix((*depositAssignedTime).Int64(), 0), nil +} + +// Estimate the gas of Refund +func (mp *minipool_v3) EstimateRefundGas(opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return mp.Contract.GetTransactionGasInfo(opts, "refund") +} + +// Refund node ETH from the minipool +func (mp *minipool_v3) Refund(opts *bind.TransactOpts) (common.Hash, error) { + tx, err := mp.Contract.Transact(opts, "refund") + if err != nil { + return common.Hash{}, fmt.Errorf("error refunding from minipool %s: %w", mp.Address.Hex(), err) + } + return tx.Hash(), nil +} + +// Check if the minipool's balance has already been distributed +func (mp *minipool_v3) GetUserDistributed(opts *bind.CallOpts) (bool, error) { + distributed := new(bool) + if err := mp.Contract.Call(opts, distributed, "getUserDistributed"); err != nil { + return false, fmt.Errorf("error getting user distributed status for minipool %s: %w", mp.Address.Hex(), err) + } + return *distributed, nil +} + +// Estimate the gas of DistributeBalance +func (mp *minipool_v3) EstimateDistributeBalanceGas(rewardsOnly bool, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return mp.Contract.GetTransactionGasInfo(opts, "distributeBalance", rewardsOnly) +} + +// Distribute the minipool's ETH balance to the node operator and rETH staking pool. +func (mp *minipool_v3) DistributeBalance(rewardsOnly bool, opts *bind.TransactOpts) (common.Hash, error) { + tx, err := mp.Contract.Transact(opts, "distributeBalance", rewardsOnly) + if err != nil { + return common.Hash{}, fmt.Errorf("error processing withdrawal for minipool %s: %w", mp.Address.Hex(), err) + } + return tx.Hash(), nil +} + +// Estimate the gas of Stake +func (mp *minipool_v3) EstimateStakeGas(validatorSignature rptypes.ValidatorSignature, depositDataRoot common.Hash, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return mp.Contract.GetTransactionGasInfo(opts, "stake", validatorSignature[:], depositDataRoot) +} + +// Progress the prelaunch minipool to staking +func (mp *minipool_v3) Stake(validatorSignature rptypes.ValidatorSignature, depositDataRoot common.Hash, opts *bind.TransactOpts) (common.Hash, error) { + tx, err := mp.Contract.Transact(opts, "stake", validatorSignature[:], depositDataRoot) + if err != nil { + return common.Hash{}, fmt.Errorf("error staking minipool %s: %w", mp.Address.Hex(), err) + } + return tx.Hash(), nil +} + +// Estimate the gas of Dissolve +func (mp *minipool_v3) EstimateDissolveGas(opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return mp.Contract.GetTransactionGasInfo(opts, "dissolve") +} + +// Dissolve the initialized or prelaunch minipool +func (mp *minipool_v3) Dissolve(opts *bind.TransactOpts) (common.Hash, error) { + tx, err := mp.Contract.Transact(opts, "dissolve") + if err != nil { + return common.Hash{}, fmt.Errorf("error dissolving minipool %s: %w", mp.Address.Hex(), err) + } + return tx.Hash(), nil +} + +// Estimate the gas of Close +func (mp *minipool_v3) EstimateCloseGas(opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return mp.Contract.GetTransactionGasInfo(opts, "close") +} + +// Withdraw node balances from the dissolved minipool and close it +func (mp *minipool_v3) Close(opts *bind.TransactOpts) (common.Hash, error) { + tx, err := mp.Contract.Transact(opts, "close") + if err != nil { + return common.Hash{}, fmt.Errorf("error closing minipool %s: %w", mp.Address.Hex(), err) + } + return tx.Hash(), nil +} + +// Estimate the gas of Finalise +func (mp *minipool_v3) EstimateFinaliseGas(opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return mp.Contract.GetTransactionGasInfo(opts, "finalise") +} + +// Finalise a minipool to get the RPL stake back +func (mp *minipool_v3) Finalise(opts *bind.TransactOpts) (common.Hash, error) { + tx, err := mp.Contract.Transact(opts, "finalise") + if err != nil { + return common.Hash{}, fmt.Errorf("error finalizing minipool %s: %w", mp.Address.Hex(), err) + } + return tx.Hash(), nil +} + +// Estimate the gas of DelegateUpgrade +func (mp *minipool_v3) EstimateDelegateUpgradeGas(opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return mp.Contract.GetTransactionGasInfo(opts, "delegateUpgrade") +} + +// Upgrade this minipool to the latest network delegate contract +func (mp *minipool_v3) DelegateUpgrade(opts *bind.TransactOpts) (common.Hash, error) { + tx, err := mp.Contract.Transact(opts, "delegateUpgrade") + if err != nil { + return common.Hash{}, fmt.Errorf("error upgrading delegate for minipool %s: %w", mp.Address.Hex(), err) + } + return tx.Hash(), nil +} + +// Estimate the gas of DelegateRollback +func (mp *minipool_v3) EstimateDelegateRollbackGas(opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return mp.Contract.GetTransactionGasInfo(opts, "delegateRollback") +} + +// Rollback to previous delegate contract +func (mp *minipool_v3) DelegateRollback(opts *bind.TransactOpts) (common.Hash, error) { + tx, err := mp.Contract.Transact(opts, "delegateRollback") + if err != nil { + return common.Hash{}, fmt.Errorf("error rolling back delegate for minipool %s: %w", mp.Address.Hex(), err) + } + return tx.Hash(), nil +} + +// Estimate the gas of SetUseLatestDelegate +func (mp *minipool_v3) EstimateSetUseLatestDelegateGas(setting bool, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return mp.Contract.GetTransactionGasInfo(opts, "setUseLatestDelegate", setting) +} + +// If set to true, will automatically use the latest delegate contract +func (mp *minipool_v3) SetUseLatestDelegate(setting bool, opts *bind.TransactOpts) (common.Hash, error) { + tx, err := mp.Contract.Transact(opts, "setUseLatestDelegate", setting) + if err != nil { + return common.Hash{}, fmt.Errorf("error setting use latest delegate for minipool %s: %w", mp.Address.Hex(), err) + } + return tx.Hash(), nil +} + +// Getter for useLatestDelegate setting +func (mp *minipool_v3) GetUseLatestDelegate(opts *bind.CallOpts) (bool, error) { + setting := new(bool) + if err := mp.Contract.Call(opts, setting, "getUseLatestDelegate"); err != nil { + return false, fmt.Errorf("error getting use latest delegate for minipool %s: %w", mp.Address.Hex(), err) + } + return *setting, nil +} + +// Returns the address of the minipool's stored delegate +func (mp *minipool_v3) GetDelegate(opts *bind.CallOpts) (common.Address, error) { + address := new(common.Address) + if err := mp.Contract.Call(opts, address, "getDelegate"); err != nil { + return common.Address{}, fmt.Errorf("error getting delegate for minipool %s: %w", mp.Address.Hex(), err) + } + return *address, nil +} + +// Returns the address of the minipool's previous delegate (or address(0) if not set) +func (mp *minipool_v3) GetPreviousDelegate(opts *bind.CallOpts) (common.Address, error) { + address := new(common.Address) + if err := mp.Contract.Call(opts, address, "getPreviousDelegate"); err != nil { + return common.Address{}, fmt.Errorf("error getting previous delegate for minipool %s: %w", mp.Address.Hex(), err) + } + return *address, nil +} + +// Returns the delegate which will be used when calling this minipool taking into account useLatestDelegate setting +func (mp *minipool_v3) GetEffectiveDelegate(opts *bind.CallOpts) (common.Address, error) { + address := new(common.Address) + if err := mp.Contract.Call(opts, address, "getEffectiveDelegate"); err != nil { + return common.Address{}, fmt.Errorf("error getting effective delegate for minipool %s: %w", mp.Address.Hex(), err) + } + return *address, nil +} + +// Estimate the gas required to reduce a minipool's bond +func (mp *minipool_v3) EstimateReduceBondAmountGas(opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return mp.Contract.GetTransactionGasInfo(opts, "reduceBondAmount") +} + +// Reduce a minipool's bond +func (mp *minipool_v3) ReduceBondAmount(opts *bind.TransactOpts) (common.Hash, error) { + tx, err := mp.Contract.Transact(opts, "reduceBondAmount") + if err != nil { + return common.Hash{}, fmt.Errorf("error reducing bond for minipool %s: %w", mp.Address.Hex(), err) + } + return tx.Hash(), nil +} + +// Given a validator balance, calculates how much belongs to the node taking into consideration rewards and penalties +func (mp *minipool_v3) CalculateNodeShare(balance *big.Int, opts *bind.CallOpts) (*big.Int, error) { + nodeAmount := new(*big.Int) + if err := mp.Contract.Call(opts, nodeAmount, "calculateNodeShare", balance); err != nil { + return nil, fmt.Errorf("error getting minipool node portion: %w", err) + } + return *nodeAmount, nil +} + +// Given a validator balance, calculates how much belongs to rETH users taking into consideration rewards and penalties +func (mp *minipool_v3) CalculateUserShare(balance *big.Int, opts *bind.CallOpts) (*big.Int, error) { + userAmount := new(*big.Int) + if err := mp.Contract.Call(opts, userAmount, "calculateUserShare", balance); err != nil { + return nil, fmt.Errorf("error getting minipool user portion: %w", err) + } + return *userAmount, nil +} + +// Estimate the gas required to vote to scrub a minipool +func (mp *minipool_v3) EstimateVoteScrubGas(opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return mp.Contract.GetTransactionGasInfo(opts, "voteScrub") +} + +// Vote to scrub a minipool +func (mp *minipool_v3) VoteScrub(opts *bind.TransactOpts) (common.Hash, error) { + tx, err := mp.Contract.Transact(opts, "voteScrub") + if err != nil { + return common.Hash{}, fmt.Errorf("error voting to scrub minipool %s: %w", mp.Address.Hex(), err) + } + return tx.Hash(), nil +} + +// Estimate the gas required to promote a vacant minipool +func (mp *minipool_v3) EstimatePromoteGas(opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return mp.Contract.GetTransactionGasInfo(opts, "promote") +} + +// Promote a vacant minipool +func (mp *minipool_v3) Promote(opts *bind.TransactOpts) (common.Hash, error) { + tx, err := mp.Contract.Transact(opts, "promote") + if err != nil { + return common.Hash{}, fmt.Errorf("error promoting minipool %s: %w", mp.Address.Hex(), err) + } + return tx.Hash(), nil +} + +// Get the data from this minipool's MinipoolPrestaked event +func (mp *minipool_v3) GetPrestakeEvent(intervalSize *big.Int, opts *bind.CallOpts) (PrestakeData, error) { + + addressFilter := []common.Address{mp.Address} + topicFilter := [][]common.Hash{{mp.Contract.ABI.Events["MinipoolPrestaked"].ID}} + + // Grab the latest block number + currentBlock, err := mp.RocketPool.Client.BlockNumber(context.Background()) + if err != nil { + return PrestakeData{}, fmt.Errorf("Error getting current block %s: %w", mp.Address.Hex(), err) + } + + // Grab the lowest block number worth querying from (should never have to go back this far in practice) + fromBlockBig, err := storage.GetDeployBlock(mp.RocketPool) + if err != nil { + return PrestakeData{}, fmt.Errorf("Error getting deploy block %s: %w", mp.Address.Hex(), err) + } + + fromBlock := fromBlockBig.Uint64() + var log types.Log + found := false + + // Backwards scan through blocks to find the event + for i := currentBlock; i >= fromBlock; i -= EventScanInterval { + from := i - EventScanInterval + 1 + if from < fromBlock { + from = fromBlock + } + + fromBig := big.NewInt(0).SetUint64(from) + toBig := big.NewInt(0).SetUint64(i) + + logs, err := eth.GetLogs(mp.RocketPool, addressFilter, topicFilter, intervalSize, fromBig, toBig, nil) + if err != nil { + return PrestakeData{}, fmt.Errorf("Error getting prestake logs for minipool %s: %w", mp.Address.Hex(), err) + } + + if len(logs) > 0 { + log = logs[0] + found = true + break + } + } + + if !found { + // This should never happen + return PrestakeData{}, fmt.Errorf("Error finding prestake log for minipool %s", mp.Address.Hex()) + } + + // Decode the event + prestakeEvent := new(MinipoolPrestakeEvent) + mp.Contract.Contract.UnpackLog(prestakeEvent, "MinipoolPrestaked", log) + if err != nil { + return PrestakeData{}, fmt.Errorf("Error unpacking prestake data: %w", err) + } + + // Convert the event to a more useable struct + prestakeData := PrestakeData{ + Pubkey: rptypes.BytesToValidatorPubkey(prestakeEvent.Pubkey), + WithdrawalCredentials: common.BytesToHash(prestakeEvent.WithdrawalCredentials), + Amount: prestakeEvent.Amount, + Signature: rptypes.BytesToValidatorSignature(prestakeEvent.Signature), + DepositDataRoot: prestakeEvent.DepositDataRoot, + Time: time.Unix(prestakeEvent.Time.Int64(), 0), + } + return prestakeData, nil +} diff --git a/bindings/minipool/minipool-interface.go b/bindings/minipool/minipool-interface.go new file mode 100644 index 000000000..a2c270414 --- /dev/null +++ b/bindings/minipool/minipool-interface.go @@ -0,0 +1,102 @@ +package minipool + +import ( + "math/big" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + rptypes "github.com/rocket-pool/smartnode/bindings/types" +) + +// The number of blocks to look for events in at once when scanning +const EventScanInterval = 10000 + +// Minipool detail types +type StatusDetails struct { + Status rptypes.MinipoolStatus `json:"status"` + StatusBlock uint64 `json:"statusBlock"` + StatusTime time.Time `json:"statusTime"` + IsVacant bool `json:"isVacant"` +} +type NodeDetails struct { + Address common.Address `json:"address"` + Fee float64 `json:"fee"` + DepositBalance *big.Int `json:"depositBalance"` + RefundBalance *big.Int `json:"refundBalance"` + DepositAssigned bool `json:"depositAssigned"` +} +type UserDetails struct { + DepositBalance *big.Int `json:"depositBalance"` + DepositAssigned bool `json:"depositAssigned"` + DepositAssignedTime time.Time `json:"depositAssignedTime"` +} + +// The data from a minipool's MinipoolPrestaked event +type MinipoolPrestakeEvent struct { + Pubkey []byte `abi:"validatorPubkey"` + Signature []byte `abi:"validatorSignature"` + DepositDataRoot [32]byte `abi:"depositDataRoot"` + Amount *big.Int `abi:"amount"` + WithdrawalCredentials []byte `abi:"withdrawalCredentials"` + Time *big.Int `abi:"time"` +} + +// Formatted MinipoolPrestaked event data +type PrestakeData struct { + Pubkey rptypes.ValidatorPubkey `json:"pubkey"` + WithdrawalCredentials common.Hash `json:"withdrawalCredentials"` + Amount *big.Int `json:"amount"` + Signature rptypes.ValidatorSignature `json:"signature"` + DepositDataRoot common.Hash `json:"depositDataRoot"` + Time time.Time `json:"time"` +} + +type Minipool interface { + GetContract() *rocketpool.Contract + GetAddress() common.Address + GetVersion() uint8 + GetStatusDetails(opts *bind.CallOpts) (StatusDetails, error) + GetStatus(opts *bind.CallOpts) (rptypes.MinipoolStatus, error) + GetStatusBlock(opts *bind.CallOpts) (uint64, error) + GetStatusTime(opts *bind.CallOpts) (time.Time, error) + GetFinalised(opts *bind.CallOpts) (bool, error) + GetDepositType(opts *bind.CallOpts) (rptypes.MinipoolDeposit, error) + GetNodeDetails(opts *bind.CallOpts) (NodeDetails, error) + GetNodeAddress(opts *bind.CallOpts) (common.Address, error) + GetNodeFee(opts *bind.CallOpts) (float64, error) + GetNodeFeeRaw(opts *bind.CallOpts) (*big.Int, error) + GetNodeDepositBalance(opts *bind.CallOpts) (*big.Int, error) + GetNodeRefundBalance(opts *bind.CallOpts) (*big.Int, error) + GetNodeDepositAssigned(opts *bind.CallOpts) (bool, error) + GetUserDetails(opts *bind.CallOpts) (UserDetails, error) + GetUserDepositBalance(opts *bind.CallOpts) (*big.Int, error) + GetUserDepositAssigned(opts *bind.CallOpts) (bool, error) + GetUserDepositAssignedTime(opts *bind.CallOpts) (time.Time, error) + EstimateRefundGas(opts *bind.TransactOpts) (rocketpool.GasInfo, error) + Refund(opts *bind.TransactOpts) (common.Hash, error) + EstimateStakeGas(validatorSignature rptypes.ValidatorSignature, depositDataRoot common.Hash, opts *bind.TransactOpts) (rocketpool.GasInfo, error) + Stake(validatorSignature rptypes.ValidatorSignature, depositDataRoot common.Hash, opts *bind.TransactOpts) (common.Hash, error) + EstimateDissolveGas(opts *bind.TransactOpts) (rocketpool.GasInfo, error) + Dissolve(opts *bind.TransactOpts) (common.Hash, error) + EstimateCloseGas(opts *bind.TransactOpts) (rocketpool.GasInfo, error) + Close(opts *bind.TransactOpts) (common.Hash, error) + EstimateFinaliseGas(opts *bind.TransactOpts) (rocketpool.GasInfo, error) + Finalise(opts *bind.TransactOpts) (common.Hash, error) + EstimateDelegateUpgradeGas(opts *bind.TransactOpts) (rocketpool.GasInfo, error) + DelegateUpgrade(opts *bind.TransactOpts) (common.Hash, error) + EstimateDelegateRollbackGas(opts *bind.TransactOpts) (rocketpool.GasInfo, error) + DelegateRollback(opts *bind.TransactOpts) (common.Hash, error) + EstimateSetUseLatestDelegateGas(setting bool, opts *bind.TransactOpts) (rocketpool.GasInfo, error) + SetUseLatestDelegate(setting bool, opts *bind.TransactOpts) (common.Hash, error) + GetUseLatestDelegate(opts *bind.CallOpts) (bool, error) + GetDelegate(opts *bind.CallOpts) (common.Address, error) + GetPreviousDelegate(opts *bind.CallOpts) (common.Address, error) + GetEffectiveDelegate(opts *bind.CallOpts) (common.Address, error) + CalculateNodeShare(balance *big.Int, opts *bind.CallOpts) (*big.Int, error) + CalculateUserShare(balance *big.Int, opts *bind.CallOpts) (*big.Int, error) + EstimateVoteScrubGas(opts *bind.TransactOpts) (rocketpool.GasInfo, error) + VoteScrub(opts *bind.TransactOpts) (common.Hash, error) + GetPrestakeEvent(intervalSize *big.Int, opts *bind.CallOpts) (PrestakeData, error) +} diff --git a/bindings/minipool/minipool.go b/bindings/minipool/minipool.go new file mode 100644 index 000000000..f6cf2b63d --- /dev/null +++ b/bindings/minipool/minipool.go @@ -0,0 +1,626 @@ +package minipool + +import ( + "fmt" + "math/big" + "sync" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "golang.org/x/sync/errgroup" + + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/types" + rptypes "github.com/rocket-pool/smartnode/bindings/types" +) + +// Settings +const ( + MinipoolPrelaunchBatchSize = 250 + MinipoolAddressBatchSize = 50 + MinipoolDetailsBatchSize = 20 + NativeMinipoolDetailsBatchSize = 1000 +) + +// Minipool details +type MinipoolDetails struct { + Address common.Address `json:"address"` + Exists bool `json:"exists"` + Pubkey rptypes.ValidatorPubkey `json:"pubkey"` +} + +// The counts of minipools per status +type MinipoolCountsPerStatus struct { + Initialized *big.Int `abi:"initialisedCount"` + Prelaunch *big.Int `abi:"prelaunchCount"` + Staking *big.Int `abi:"stakingCount"` + Withdrawable *big.Int `abi:"withdrawableCount"` + Dissolved *big.Int `abi:"dissolvedCount"` +} + +// Get all minipool details +func GetMinipools(rp *rocketpool.RocketPool, opts *bind.CallOpts) ([]MinipoolDetails, error) { + minipoolAddresses, err := GetMinipoolAddresses(rp, opts) + if err != nil { + return []MinipoolDetails{}, err + } + return loadMinipoolDetails(rp, minipoolAddresses, opts) +} + +// Get a node's minipool details +func GetNodeMinipools(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) ([]MinipoolDetails, error) { + minipoolAddresses, err := GetNodeMinipoolAddresses(rp, nodeAddress, opts) + if err != nil { + return []MinipoolDetails{}, err + } + return loadMinipoolDetails(rp, minipoolAddresses, opts) +} + +// Load minipool details +func loadMinipoolDetails(rp *rocketpool.RocketPool, minipoolAddresses []common.Address, opts *bind.CallOpts) ([]MinipoolDetails, error) { + + // Load minipool details in batches + details := make([]MinipoolDetails, len(minipoolAddresses)) + for bsi := 0; bsi < len(minipoolAddresses); bsi += MinipoolDetailsBatchSize { + + // Get batch start & end index + msi := bsi + mei := bsi + MinipoolDetailsBatchSize + if mei > len(minipoolAddresses) { + mei = len(minipoolAddresses) + } + + // Load details + var wg errgroup.Group + for mi := msi; mi < mei; mi++ { + mi := mi + wg.Go(func() error { + minipoolAddress := minipoolAddresses[mi] + minipoolDetails, err := GetMinipoolDetails(rp, minipoolAddress, opts) + if err == nil { + details[mi] = minipoolDetails + } + return err + }) + } + if err := wg.Wait(); err != nil { + return []MinipoolDetails{}, err + } + + } + + // Return + return details, nil + +} + +// Get all minipool addresses +func GetMinipoolAddresses(rp *rocketpool.RocketPool, opts *bind.CallOpts) ([]common.Address, error) { + + // Get minipool count + minipoolCount, err := GetMinipoolCount(rp, opts) + if err != nil { + return []common.Address{}, err + } + + // Load minipool addresses in batches + addresses := make([]common.Address, minipoolCount) + for bsi := uint64(0); bsi < minipoolCount; bsi += MinipoolAddressBatchSize { + + // Get batch start & end index + msi := bsi + mei := bsi + MinipoolAddressBatchSize + if mei > minipoolCount { + mei = minipoolCount + } + + // Load addresses + var wg errgroup.Group + for mi := msi; mi < mei; mi++ { + mi := mi + wg.Go(func() error { + address, err := GetMinipoolAt(rp, mi, opts) + if err == nil { + addresses[mi] = address + } + return err + }) + } + if err := wg.Wait(); err != nil { + return []common.Address{}, err + } + + } + + // Return + return addresses, nil + +} + +// Get the addresses of all minipools in prelaunch status +func GetPrelaunchMinipoolAddresses(rp *rocketpool.RocketPool, opts *bind.CallOpts) ([]common.Address, error) { + + rocketMinipoolManager, err := getRocketMinipoolManager(rp, opts) + if err != nil { + return []common.Address{}, err + } + + // Get the total number of minipools + totalMinipoolsUint, err := GetMinipoolCount(rp, nil) + if err != nil { + return []common.Address{}, err + } + + totalMinipools := int64(totalMinipoolsUint) + addresses := []common.Address{} + limit := big.NewInt(MinipoolPrelaunchBatchSize) + for i := int64(0); i < totalMinipools; i += MinipoolPrelaunchBatchSize { + // Get a batch of addresses + offset := big.NewInt(i) + newAddresses := new([]common.Address) + if err := rocketMinipoolManager.Call(opts, newAddresses, "getPrelaunchMinipools", offset, limit); err != nil { + return []common.Address{}, fmt.Errorf("error getting prelaunch minipool addresses: %w", err) + } + addresses = append(addresses, *newAddresses...) + } + + return addresses, nil +} + +// Get a node's minipool addresses +func GetNodeMinipoolAddresses(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) ([]common.Address, error) { + + // Get minipool count + minipoolCount, err := GetNodeMinipoolCount(rp, nodeAddress, opts) + if err != nil { + return []common.Address{}, err + } + + // Load minipool addresses in batches + addresses := make([]common.Address, minipoolCount) + for bsi := uint64(0); bsi < minipoolCount; bsi += MinipoolAddressBatchSize { + + // Get batch start & end index + msi := bsi + mei := bsi + MinipoolAddressBatchSize + if mei > minipoolCount { + mei = minipoolCount + } + + // Load addresses + var wg errgroup.Group + for mi := msi; mi < mei; mi++ { + mi := mi + wg.Go(func() error { + address, err := GetNodeMinipoolAt(rp, nodeAddress, mi, opts) + if err == nil { + addresses[mi] = address + } + return err + }) + } + if err := wg.Wait(); err != nil { + return []common.Address{}, err + } + + } + + // Return + return addresses, nil + +} + +// Get a node's validating minipool pubkeys +func GetNodeValidatingMinipoolPubkeys(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) ([]rptypes.ValidatorPubkey, error) { + + // Get minipool count + minipoolCount, err := GetNodeValidatingMinipoolCount(rp, nodeAddress, opts) + if err != nil { + return []rptypes.ValidatorPubkey{}, err + } + + // Load pubkeys in batches + var lock = sync.RWMutex{} + pubkeys := make([]rptypes.ValidatorPubkey, minipoolCount) + for bsi := uint64(0); bsi < minipoolCount; bsi += MinipoolAddressBatchSize { + + // Get batch start & end index + msi := bsi + mei := bsi + MinipoolAddressBatchSize + if mei > minipoolCount { + mei = minipoolCount + } + + // Load pubkeys + var wg errgroup.Group + for mi := msi; mi < mei; mi++ { + mi := mi + wg.Go(func() error { + minipoolAddress, err := GetNodeValidatingMinipoolAt(rp, nodeAddress, mi, opts) + if err != nil { + return err + } + pubkey, err := GetMinipoolPubkey(rp, minipoolAddress, opts) + if err != nil { + return err + } + lock.Lock() + pubkeys[mi] = pubkey + lock.Unlock() + return nil + }) + } + if err := wg.Wait(); err != nil { + return []rptypes.ValidatorPubkey{}, err + } + + } + + // Return + return pubkeys, nil + +} + +// Get a minipool's details +func GetMinipoolDetails(rp *rocketpool.RocketPool, minipoolAddress common.Address, opts *bind.CallOpts) (MinipoolDetails, error) { + + // Data + var wg errgroup.Group + var exists bool + var pubkey rptypes.ValidatorPubkey + + // Load data + wg.Go(func() error { + var err error + exists, err = GetMinipoolExists(rp, minipoolAddress, opts) + return err + }) + wg.Go(func() error { + var err error + pubkey, err = GetMinipoolPubkey(rp, minipoolAddress, opts) + return err + }) + + // Wait for data + if err := wg.Wait(); err != nil { + return MinipoolDetails{}, err + } + + // Return + return MinipoolDetails{ + Address: minipoolAddress, + Exists: exists, + Pubkey: pubkey, + }, nil + +} + +// Get the minipool count +func GetMinipoolCount(rp *rocketpool.RocketPool, opts *bind.CallOpts) (uint64, error) { + rocketMinipoolManager, err := getRocketMinipoolManager(rp, opts) + if err != nil { + return 0, err + } + minipoolCount := new(*big.Int) + if err := rocketMinipoolManager.Call(opts, minipoolCount, "getMinipoolCount"); err != nil { + return 0, fmt.Errorf("error getting minipool count: %w", err) + } + return (*minipoolCount).Uint64(), nil +} + +// Get the number of staking minipools in the network +func GetStakingMinipoolCount(rp *rocketpool.RocketPool, opts *bind.CallOpts) (uint64, error) { + rocketMinipoolManager, err := getRocketMinipoolManager(rp, opts) + if err != nil { + return 0, err + } + minipoolCount := new(*big.Int) + if err := rocketMinipoolManager.Call(opts, minipoolCount, "getStakingMinipoolCount"); err != nil { + return 0, fmt.Errorf("error getting staking minipool count: %w", err) + } + return (*minipoolCount).Uint64(), nil +} + +// Get the number of finalised minipools in the network +func GetFinalisedMinipoolCount(rp *rocketpool.RocketPool, opts *bind.CallOpts) (uint64, error) { + rocketMinipoolManager, err := getRocketMinipoolManager(rp, opts) + if err != nil { + return 0, err + } + minipoolCount := new(*big.Int) + if err := rocketMinipoolManager.Call(opts, minipoolCount, "getFinalisedMinipoolCount"); err != nil { + return 0, fmt.Errorf("error getting finalised minipool count: %w", err) + } + return (*minipoolCount).Uint64(), nil +} + +// Get the number of active minipools in the network +func GetActiveMinipoolCount(rp *rocketpool.RocketPool, opts *bind.CallOpts) (uint64, error) { + rocketMinipoolManager, err := getRocketMinipoolManager(rp, opts) + if err != nil { + return 0, err + } + minipoolCount := new(*big.Int) + if err := rocketMinipoolManager.Call(opts, minipoolCount, "getActiveMinipoolCount"); err != nil { + return 0, fmt.Errorf("error getting finalised minipool count: %w", err) + } + return (*minipoolCount).Uint64(), nil +} + +// Get the minipool count by status +func GetMinipoolCountPerStatus(rp *rocketpool.RocketPool, opts *bind.CallOpts) (MinipoolCountsPerStatus, error) { + rocketMinipoolManager, err := getRocketMinipoolManager(rp, opts) + if err != nil { + return MinipoolCountsPerStatus{}, err + } + + // Get the total number of minipools + totalMinipoolsUint, err := GetMinipoolCount(rp, nil) + if err != nil { + return MinipoolCountsPerStatus{}, err + } + + totalMinipools := int64(totalMinipoolsUint) + minipoolCounts := MinipoolCountsPerStatus{ + Initialized: big.NewInt(0), + Prelaunch: big.NewInt(0), + Staking: big.NewInt(0), + Dissolved: big.NewInt(0), + Withdrawable: big.NewInt(0), + } + limit := big.NewInt(MinipoolPrelaunchBatchSize) + for i := int64(0); i < totalMinipools; i += MinipoolPrelaunchBatchSize { + // Get a batch of counts + offset := big.NewInt(i) + newMinipoolCounts := new(MinipoolCountsPerStatus) + if err := rocketMinipoolManager.Call(opts, newMinipoolCounts, "getMinipoolCountPerStatus", offset, limit); err != nil { + return MinipoolCountsPerStatus{}, fmt.Errorf("error getting minipool counts: %w", err) + } + if newMinipoolCounts != nil { + if newMinipoolCounts.Initialized != nil { + minipoolCounts.Initialized.Add(minipoolCounts.Initialized, newMinipoolCounts.Initialized) + } + if newMinipoolCounts.Prelaunch != nil { + minipoolCounts.Prelaunch.Add(minipoolCounts.Prelaunch, newMinipoolCounts.Prelaunch) + } + if newMinipoolCounts.Staking != nil { + minipoolCounts.Staking.Add(minipoolCounts.Staking, newMinipoolCounts.Staking) + } + if newMinipoolCounts.Dissolved != nil { + minipoolCounts.Dissolved.Add(minipoolCounts.Dissolved, newMinipoolCounts.Dissolved) + } + if newMinipoolCounts.Withdrawable != nil { + minipoolCounts.Withdrawable.Add(minipoolCounts.Withdrawable, newMinipoolCounts.Withdrawable) + } + } + } + return minipoolCounts, nil +} + +// Get a minipool address by index +func GetMinipoolAt(rp *rocketpool.RocketPool, index uint64, opts *bind.CallOpts) (common.Address, error) { + rocketMinipoolManager, err := getRocketMinipoolManager(rp, opts) + if err != nil { + return common.Address{}, err + } + minipoolAddress := new(common.Address) + if err := rocketMinipoolManager.Call(opts, minipoolAddress, "getMinipoolAt", big.NewInt(int64(index))); err != nil { + return common.Address{}, fmt.Errorf("error getting minipool %d address: %w", index, err) + } + return *minipoolAddress, nil +} + +// Get a node's minipool count +func GetNodeMinipoolCount(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (uint64, error) { + rocketMinipoolManager, err := getRocketMinipoolManager(rp, opts) + if err != nil { + return 0, err + } + minipoolCount := new(*big.Int) + if err := rocketMinipoolManager.Call(opts, minipoolCount, "getNodeMinipoolCount", nodeAddress); err != nil { + return 0, fmt.Errorf("error getting node %s minipool count: %w", nodeAddress.Hex(), err) + } + return (*minipoolCount).Uint64(), nil +} + +// Get a node's minipool count +func GetNodeMinipoolCountRaw(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (*big.Int, error) { + rocketMinipoolManager, err := getRocketMinipoolManager(rp, opts) + if err != nil { + return nil, err + } + minipoolCount := new(*big.Int) + if err := rocketMinipoolManager.Call(opts, minipoolCount, "getNodeMinipoolCount", nodeAddress); err != nil { + return nil, fmt.Errorf("error getting node %s minipool count: %w", nodeAddress.Hex(), err) + } + return *minipoolCount, nil +} + +// Get the number of minipools owned by a node that are not finalised +func GetNodeActiveMinipoolCount(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (uint64, error) { + rocketMinipoolManager, err := getRocketMinipoolManager(rp, opts) + if err != nil { + return 0, err + } + minipoolCount := new(*big.Int) + if err := rocketMinipoolManager.Call(opts, minipoolCount, "getNodeActiveMinipoolCount", nodeAddress); err != nil { + return 0, fmt.Errorf("error getting node %s minipool count: %w", nodeAddress.Hex(), err) + } + return (*minipoolCount).Uint64(), nil +} + +// Get the number of minipools owned by a node that are finalised +func GetNodeFinalisedMinipoolCount(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (uint64, error) { + rocketMinipoolManager, err := getRocketMinipoolManager(rp, opts) + if err != nil { + return 0, err + } + minipoolCount := new(*big.Int) + if err := rocketMinipoolManager.Call(opts, minipoolCount, "getNodeFinalisedMinipoolCount", nodeAddress); err != nil { + return 0, fmt.Errorf("error getting node %s minipool count: %w", nodeAddress.Hex(), err) + } + return (*minipoolCount).Uint64(), nil +} + +// Get a node's minipool address by index +func GetNodeMinipoolAt(rp *rocketpool.RocketPool, nodeAddress common.Address, index uint64, opts *bind.CallOpts) (common.Address, error) { + rocketMinipoolManager, err := getRocketMinipoolManager(rp, opts) + if err != nil { + return common.Address{}, err + } + minipoolAddress := new(common.Address) + if err := rocketMinipoolManager.Call(opts, minipoolAddress, "getNodeMinipoolAt", nodeAddress, big.NewInt(int64(index))); err != nil { + return common.Address{}, fmt.Errorf("error getting node %s minipool %d address: %w", nodeAddress.Hex(), index, err) + } + return *minipoolAddress, nil +} + +// Get a node's validating minipool count +func GetNodeValidatingMinipoolCount(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (uint64, error) { + rocketMinipoolManager, err := getRocketMinipoolManager(rp, opts) + if err != nil { + return 0, err + } + minipoolCount := new(*big.Int) + if err := rocketMinipoolManager.Call(opts, minipoolCount, "getNodeValidatingMinipoolCount", nodeAddress); err != nil { + return 0, fmt.Errorf("error getting node %s validating minipool count: %w", nodeAddress.Hex(), err) + } + return (*minipoolCount).Uint64(), nil +} + +// Get a node's validating minipool address by index +func GetNodeValidatingMinipoolAt(rp *rocketpool.RocketPool, nodeAddress common.Address, index uint64, opts *bind.CallOpts) (common.Address, error) { + rocketMinipoolManager, err := getRocketMinipoolManager(rp, opts) + if err != nil { + return common.Address{}, err + } + minipoolAddress := new(common.Address) + if err := rocketMinipoolManager.Call(opts, minipoolAddress, "getNodeValidatingMinipoolAt", nodeAddress, big.NewInt(int64(index))); err != nil { + return common.Address{}, fmt.Errorf("error getting node %s validating minipool %d address: %w", nodeAddress.Hex(), index, err) + } + return *minipoolAddress, nil +} + +// Get a minipool address by validator pubkey +func GetMinipoolByPubkey(rp *rocketpool.RocketPool, pubkey rptypes.ValidatorPubkey, opts *bind.CallOpts) (common.Address, error) { + rocketMinipoolManager, err := getRocketMinipoolManager(rp, opts) + if err != nil { + return common.Address{}, err + } + minipoolAddress := new(common.Address) + if err := rocketMinipoolManager.Call(opts, minipoolAddress, "getMinipoolByPubkey", pubkey[:]); err != nil { + return common.Address{}, fmt.Errorf("error getting validator %s minipool address: %w", pubkey.Hex(), err) + } + return *minipoolAddress, nil +} + +// Check whether a minipool exists +func GetMinipoolExists(rp *rocketpool.RocketPool, minipoolAddress common.Address, opts *bind.CallOpts) (bool, error) { + rocketMinipoolManager, err := getRocketMinipoolManager(rp, opts) + if err != nil { + return false, err + } + exists := new(bool) + if err := rocketMinipoolManager.Call(opts, exists, "getMinipoolExists", minipoolAddress); err != nil { + return false, fmt.Errorf("error getting minipool %s exists status: %w", minipoolAddress.Hex(), err) + } + return *exists, nil +} + +// Get a minipool's validator pubkey +func GetMinipoolPubkey(rp *rocketpool.RocketPool, minipoolAddress common.Address, opts *bind.CallOpts) (rptypes.ValidatorPubkey, error) { + rocketMinipoolManager, err := getRocketMinipoolManager(rp, opts) + if err != nil { + return rptypes.ValidatorPubkey{}, err + } + pubkey := new(rptypes.ValidatorPubkey) + if err := rocketMinipoolManager.Call(opts, pubkey, "getMinipoolPubkey", minipoolAddress); err != nil { + return rptypes.ValidatorPubkey{}, fmt.Errorf("error getting minipool %s pubkey: %w", minipoolAddress.Hex(), err) + } + return *pubkey, nil +} + +// Get the 0x01-based Beacon Chain withdrawal credentials for a given minipool +func GetMinipoolWithdrawalCredentials(rp *rocketpool.RocketPool, minipoolAddress common.Address, opts *bind.CallOpts) (common.Hash, error) { + rocketMinipoolManager, err := getRocketMinipoolManager(rp, opts) + if err != nil { + return common.Hash{}, err + } + withdrawalCredentials := new(common.Hash) + if err := rocketMinipoolManager.Call(opts, withdrawalCredentials, "getMinipoolWithdrawalCredentials", minipoolAddress); err != nil { + return common.Hash{}, fmt.Errorf("error getting minipool withdrawal credentials: %w", err) + } + return *withdrawalCredentials, nil +} + +// Get the number of penalties applied to a minipool +func GetMinipoolPenaltyCount(rp *rocketpool.RocketPool, minipoolAddress common.Address, opts *bind.CallOpts) (uint64, error) { + key := crypto.Keccak256Hash([]byte("network.penalties.penalty"), minipoolAddress.Bytes()) + penalties, err := rp.RocketStorage.GetUint(opts, key) + if err != nil { + return 0, err + } + return penalties.Uint64(), nil +} + +// Get the vacant minipool count +func GetVacantMinipoolCount(rp *rocketpool.RocketPool, opts *bind.CallOpts) (uint64, error) { + rocketMinipoolManager, err := getRocketMinipoolManager(rp, opts) + if err != nil { + return 0, err + } + vacantMinipoolCount := new(*big.Int) + if err := rocketMinipoolManager.Call(opts, vacantMinipoolCount, "getVacantMinipoolCount"); err != nil { + return 0, fmt.Errorf("error getting vacant minipool count: %w", err) + } + return (*vacantMinipoolCount).Uint64(), nil +} + +// Get a vacant minipool address by index +func GetVacantMinipoolAt(rp *rocketpool.RocketPool, index uint64, opts *bind.CallOpts) (common.Address, error) { + rocketMinipoolManager, err := getRocketMinipoolManager(rp, opts) + if err != nil { + return common.Address{}, err + } + vacantMinipoolAddress := new(common.Address) + if err := rocketMinipoolManager.Call(opts, vacantMinipoolAddress, "getVacantMinipoolAt", big.NewInt(int64(index))); err != nil { + return common.Address{}, fmt.Errorf("error getting vacant minipool %d address: %w", index, err) + } + return *vacantMinipoolAddress, nil +} + +// Get a minipool's RPL slashing status +func GetMinipoolRPLSlashed(rp *rocketpool.RocketPool, minipoolAddress common.Address, opts *bind.CallOpts) (bool, error) { + rocketMinipoolManager, err := getRocketMinipoolManager(rp, opts) + if err != nil { + return false, err + } + value := new(bool) + if err := rocketMinipoolManager.Call(opts, value, "getMinipoolRPLSlashed", minipoolAddress); err != nil { + return false, fmt.Errorf("error getting minipool %s slashed status: %w", minipoolAddress.Hex(), err) + } + return *value, nil +} + +// Get a minipool's deposit type invariant of its delegate version +func GetMinipoolDepositType(rp *rocketpool.RocketPool, minipoolAddress common.Address, opts *bind.CallOpts) (types.MinipoolDeposit, error) { + rocketMinipoolManager, err := getRocketMinipoolManager(rp, opts) + if err != nil { + return types.None, err + } + value := new(uint8) + if err := rocketMinipoolManager.Call(opts, value, "getMinipoolDepositType", minipoolAddress); err != nil { + return types.None, fmt.Errorf("error getting minipool %s slashed status: %w", minipoolAddress.Hex(), err) + } + return types.MinipoolDeposit(*value), nil +} + +// Get contracts +var rocketMinipoolManagerLock sync.Mutex + +func getRocketMinipoolManager(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*rocketpool.Contract, error) { + rocketMinipoolManagerLock.Lock() + defer rocketMinipoolManagerLock.Unlock() + return rp.GetContract("rocketMinipoolManager", opts) +} diff --git a/bindings/minipool/queue.go b/bindings/minipool/queue.go new file mode 100644 index 000000000..e002bf7d9 --- /dev/null +++ b/bindings/minipool/queue.go @@ -0,0 +1,144 @@ +package minipool + +import ( + "fmt" + "math/big" + "sync" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "golang.org/x/sync/errgroup" + + "github.com/rocket-pool/smartnode/bindings/rocketpool" +) + +// Minipool queue capacity +type QueueCapacity struct { + Total *big.Int + Effective *big.Int +} + +// Minipools queue status details +type QueueDetails struct { + Position int64 +} + +// Get minipool queue capacity +func GetQueueCapacity(rp *rocketpool.RocketPool, opts *bind.CallOpts) (QueueCapacity, error) { + + // Data + var wg errgroup.Group + var total *big.Int + var effective *big.Int + + // Load data + wg.Go(func() error { + var err error + total, err = GetQueueTotalCapacity(rp, opts) + return err + }) + wg.Go(func() error { + var err error + effective, err = GetQueueEffectiveCapacity(rp, opts) + return err + }) + + // Wait for data + if err := wg.Wait(); err != nil { + return QueueCapacity{}, err + } + + // Return + return QueueCapacity{ + Total: total, + Effective: effective, + }, nil + +} + +// Get the total length of the minipool queue +func GetQueueTotalLength(rp *rocketpool.RocketPool, opts *bind.CallOpts) (uint64, error) { + rocketMinipoolQueue, err := getRocketMinipoolQueue(rp, opts) + if err != nil { + return 0, err + } + length := new(*big.Int) + if err := rocketMinipoolQueue.Call(opts, length, "getTotalLength"); err != nil { + return 0, fmt.Errorf("error getting total minipool queue length: %w", err) + } + return (*length).Uint64(), nil +} + +// Get the total capacity of the minipool queue +func GetQueueTotalCapacity(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*big.Int, error) { + rocketMinipoolQueue, err := getRocketMinipoolQueue(rp, opts) + if err != nil { + return nil, err + } + capacity := new(*big.Int) + if err := rocketMinipoolQueue.Call(opts, capacity, "getTotalCapacity"); err != nil { + return nil, fmt.Errorf("error getting minipool queue total capacity: %w", err) + } + return *capacity, nil +} + +// Get the total effective capacity of the minipool queue (used in node demand calculation) +func GetQueueEffectiveCapacity(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*big.Int, error) { + rocketMinipoolQueue, err := getRocketMinipoolQueue(rp, opts) + if err != nil { + return nil, err + } + capacity := new(*big.Int) + if err := rocketMinipoolQueue.Call(opts, capacity, "getEffectiveCapacity"); err != nil { + return nil, fmt.Errorf("error getting minipool queue effective capacity: %w", err) + } + return *capacity, nil +} + +// Get Queue position details of a minipool +func GetQueueDetails(rp *rocketpool.RocketPool, minipoolAddress common.Address, opts *bind.CallOpts) (QueueDetails, error) { + position, err := GetQueuePositionOfMinipool(rp, minipoolAddress, opts) + if err != nil { + return QueueDetails{}, err + } + + // Return + return QueueDetails{ + Position: position, + }, nil +} + +// Get a minipools position in queue (1-indexed). 0 means it is currently not queued. +func GetQueuePositionOfMinipool(rp *rocketpool.RocketPool, minipoolAddress common.Address, opts *bind.CallOpts) (int64, error) { + rocketMinipoolQueue, err := getRocketMinipoolQueue(rp, opts) + if err != nil { + return 0, err + } + position := new(*big.Int) + if err := rocketMinipoolQueue.Call(opts, position, "getMinipoolPosition", minipoolAddress); err != nil { + return 0, fmt.Errorf("error getting queue position for minipool %s: %w", minipoolAddress.Hex(), err) + } + return (*position).Int64() + 1, nil +} + +// Get the minipool at the specified position in queue (0-indexed). +func GetQueueMinipoolAtPosition(rp *rocketpool.RocketPool, position uint64, opts *bind.CallOpts) (common.Address, error) { + rocketMinipoolQueue, err := getRocketMinipoolQueue(rp, opts) + if err != nil { + return common.Address{}, err + } + address := new(common.Address) + if err := rocketMinipoolQueue.Call(opts, address, "getMinipoolAt", big.NewInt(int64(position))); err != nil { + return common.Address{}, fmt.Errorf("error getting minipool at queue position %d: %w", position, err) + } + return *address, nil +} + +// Get contracts +var rocketMinipoolQueueLock sync.Mutex + +func getRocketMinipoolQueue(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*rocketpool.Contract, error) { + rocketMinipoolQueueLock.Lock() + defer rocketMinipoolQueueLock.Unlock() + return rp.GetContract("rocketMinipoolQueue", opts) +} diff --git a/bindings/minipool/status.go b/bindings/minipool/status.go new file mode 100644 index 000000000..efd51e392 --- /dev/null +++ b/bindings/minipool/status.go @@ -0,0 +1,42 @@ +package minipool + +import ( + "fmt" + "sync" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + + "github.com/rocket-pool/smartnode/bindings/rocketpool" +) + +// Estimate the gas of SubmitMinipoolWithdrawable +func EstimateSubmitMinipoolWithdrawableGas(rp *rocketpool.RocketPool, minipoolAddress common.Address, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketMinipoolStatus, err := getRocketMinipoolStatus(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketMinipoolStatus.GetTransactionGasInfo(opts, "submitMinipoolWithdrawable", minipoolAddress) +} + +// Submit a minipool withdrawable event +func SubmitMinipoolWithdrawable(rp *rocketpool.RocketPool, minipoolAddress common.Address, opts *bind.TransactOpts) (common.Hash, error) { + rocketMinipoolStatus, err := getRocketMinipoolStatus(rp, nil) + if err != nil { + return common.Hash{}, err + } + tx, err := rocketMinipoolStatus.Transact(opts, "submitMinipoolWithdrawable", minipoolAddress) + if err != nil { + return common.Hash{}, fmt.Errorf("error submitting minipool withdrawable event: %w", err) + } + return tx.Hash(), nil +} + +// Get contracts +var rocketMinipoolStatusLock sync.Mutex + +func getRocketMinipoolStatus(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*rocketpool.Contract, error) { + rocketMinipoolStatusLock.Lock() + defer rocketMinipoolStatusLock.Unlock() + return rp.GetContract("rocketMinipoolStatus", opts) +} diff --git a/bindings/network/balances.go b/bindings/network/balances.go new file mode 100644 index 000000000..d88bbd634 --- /dev/null +++ b/bindings/network/balances.go @@ -0,0 +1,229 @@ +package network + +import ( + "fmt" + "math/big" + "sync" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/utils/eth" +) + +// Info for a balances updated event +type BalancesUpdatedEvent struct { + BlockNumber *big.Int `json:"blockNumber"` + SlotTimestamp *big.Int `json:"slotTimestamp"` + TotalEth *big.Int `json:"totalEth"` + StakingEth *big.Int `json:"stakingEth"` + RethSupply *big.Int `json:"rethSupply"` + BlockTimestamp *big.Int `json:"blockTimestamp"` +} + +// Get the block number which network balances are current for +func GetBalancesBlock(rp *rocketpool.RocketPool, opts *bind.CallOpts) (uint64, error) { + rocketNetworkBalances, err := getRocketNetworkBalances(rp, opts) + if err != nil { + return 0, err + } + balancesBlock := new(*big.Int) + if err := rocketNetworkBalances.Call(opts, balancesBlock, "getBalancesBlock"); err != nil { + return 0, fmt.Errorf("error getting network balances block: %w", err) + } + return (*balancesBlock).Uint64(), nil +} + +// Get the block number which network balances are current for +func GetBalancesBlockRaw(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*big.Int, error) { + rocketNetworkBalances, err := getRocketNetworkBalances(rp, opts) + if err != nil { + return nil, err + } + balancesBlock := new(*big.Int) + if err := rocketNetworkBalances.Call(opts, balancesBlock, "getBalancesBlock"); err != nil { + return nil, fmt.Errorf("error getting network balances block: %w", err) + } + return *balancesBlock, nil +} + +// Get the current network total ETH balance +func GetTotalETHBalance(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*big.Int, error) { + rocketNetworkBalances, err := getRocketNetworkBalances(rp, opts) + if err != nil { + return nil, err + } + totalEthBalance := new(*big.Int) + if err := rocketNetworkBalances.Call(opts, totalEthBalance, "getTotalETHBalance"); err != nil { + return nil, fmt.Errorf("error getting network total ETH balance: %w", err) + } + return *totalEthBalance, nil +} + +// Get the current network staking ETH balance +func GetStakingETHBalance(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*big.Int, error) { + rocketNetworkBalances, err := getRocketNetworkBalances(rp, opts) + if err != nil { + return nil, err + } + stakingEthBalance := new(*big.Int) + if err := rocketNetworkBalances.Call(opts, stakingEthBalance, "getStakingETHBalance"); err != nil { + return nil, fmt.Errorf("error getting network staking ETH balance: %w", err) + } + return *stakingEthBalance, nil +} + +// Get the current network total rETH supply +func GetTotalRETHSupply(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*big.Int, error) { + rocketNetworkBalances, err := getRocketNetworkBalances(rp, opts) + if err != nil { + return nil, err + } + totalRethSupply := new(*big.Int) + if err := rocketNetworkBalances.Call(opts, totalRethSupply, "getTotalRETHSupply"); err != nil { + return nil, fmt.Errorf("error getting network total rETH supply: %w", err) + } + return *totalRethSupply, nil +} + +// Get the current network ETH utilization rate +func GetETHUtilizationRate(rp *rocketpool.RocketPool, opts *bind.CallOpts) (float64, error) { + rocketNetworkBalances, err := getRocketNetworkBalances(rp, opts) + if err != nil { + return 0, err + } + ethUtilizationRate := new(*big.Int) + if err := rocketNetworkBalances.Call(opts, ethUtilizationRate, "getETHUtilizationRate"); err != nil { + return 0, fmt.Errorf("error getting network ETH utilization rate: %w", err) + } + return eth.WeiToEth(*ethUtilizationRate), nil +} + +// Estimate the gas of SubmitBalances +func EstimateSubmitBalancesGas(rp *rocketpool.RocketPool, block uint64, slotTimestamp uint64, totalEth, stakingEth, rethSupply *big.Int, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketNetworkBalances, err := getRocketNetworkBalances(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketNetworkBalances.GetTransactionGasInfo(opts, "submitBalances", big.NewInt(int64(block)), big.NewInt(int64(slotTimestamp)), totalEth, stakingEth, rethSupply) +} + +// Submit network balances for an epoch +func SubmitBalances(rp *rocketpool.RocketPool, block uint64, slotTimestamp uint64, totalEth, stakingEth, rethSupply *big.Int, opts *bind.TransactOpts) (common.Hash, error) { + rocketNetworkBalances, err := getRocketNetworkBalances(rp, nil) + if err != nil { + return common.Hash{}, err + } + tx, err := rocketNetworkBalances.Transact(opts, "submitBalances", big.NewInt(int64(block)), big.NewInt(int64(slotTimestamp)), totalEth, stakingEth, rethSupply) + if err != nil { + return common.Hash{}, fmt.Errorf("error submitting network balances: %w", err) + } + return tx.Hash(), nil +} + +// Returns an array of block numbers for balances submissions the given trusted node has submitted since fromBlock +func GetBalancesSubmissions(rp *rocketpool.RocketPool, nodeAddress common.Address, fromBlock uint64, intervalSize *big.Int, opts *bind.CallOpts) (*[]uint64, error) { + // Get contracts + rocketNetworkBalances, err := getRocketNetworkBalances(rp, opts) + if err != nil { + return nil, err + } + // Construct a filter query for relevant logs + addressFilter := []common.Address{*rocketNetworkBalances.Address} + topicFilter := [][]common.Hash{{rocketNetworkBalances.ABI.Events["BalancesSubmitted"].ID}, {common.BytesToHash(nodeAddress.Bytes())}} + + // Get the event logs + logs, err := eth.GetLogs(rp, addressFilter, topicFilter, intervalSize, big.NewInt(int64(fromBlock)), nil, nil) + if err != nil { + return nil, err + } + + timestamps := make([]uint64, len(logs)) + for i, log := range logs { + values := make(map[string]interface{}) + // Decode the event + if rocketNetworkBalances.ABI.Events["BalancesSubmitted"].Inputs.UnpackIntoMap(values, log.Data) != nil { + return nil, err + } + timestamps[i] = values["block"].(*big.Int).Uint64() + } + return ×tamps, nil +} + +// Returns an array of members who submitted a balance since fromBlock +func GetLatestBalancesSubmissions(rp *rocketpool.RocketPool, fromBlock uint64, intervalSize *big.Int, opts *bind.CallOpts) ([]common.Address, error) { + // Get contracts + rocketNetworkBalances, err := getRocketNetworkBalances(rp, opts) + if err != nil { + return nil, err + } + // Construct a filter query for relevant logs + addressFilter := []common.Address{*rocketNetworkBalances.Address} + topicFilter := [][]common.Hash{{rocketNetworkBalances.ABI.Events["BalancesSubmitted"].ID}} + + // Get the event logs + logs, err := eth.GetLogs(rp, addressFilter, topicFilter, intervalSize, big.NewInt(int64(fromBlock)), nil, nil) + if err != nil { + return nil, err + } + + results := make([]common.Address, len(logs)) + for i, log := range logs { + // Topic 0 is the event, topic 1 is the "from" address + address := common.BytesToAddress(log.Topics[1].Bytes()) + results[i] = address + } + return results, nil +} + +func GetBalancesUpdatedEvent(rp *rocketpool.RocketPool, blockNumber uint64, opts *bind.CallOpts) (bool, BalancesUpdatedEvent, error) { + // Get contracts + rocketNetworkBalances, err := getRocketNetworkBalances(rp, opts) + if err != nil { + return false, BalancesUpdatedEvent{}, err + } + + // Create the list of addresses to check + currentAddress := *rocketNetworkBalances.Address + rocketNetworkBalancesAddress := []common.Address{currentAddress} + + // Construct a filter query for relevant logs + balancesUpdatedEvent := rocketNetworkBalances.ABI.Events["BalancesUpdated"] + indexBytes := [32]byte{} + addressFilter := rocketNetworkBalancesAddress + topicFilter := [][]common.Hash{{balancesUpdatedEvent.ID}, {indexBytes}} + + // Get the event logs + logs, err := eth.GetLogs(rp, addressFilter, topicFilter, big.NewInt(1), big.NewInt(int64(blockNumber)), big.NewInt(int64(blockNumber)), nil) + if err != nil { + return false, BalancesUpdatedEvent{}, err + } + if len(logs) == 0 { + return false, BalancesUpdatedEvent{}, nil + } + + // Get the log info values + values, err := balancesUpdatedEvent.Inputs.Unpack(logs[0].Data) + if err != nil { + return false, BalancesUpdatedEvent{}, fmt.Errorf("error unpacking price updated event data: %w", err) + } + + // Convert to a native struct + var eventData BalancesUpdatedEvent + err = balancesUpdatedEvent.Inputs.Copy(&eventData, values) + if err != nil { + return false, BalancesUpdatedEvent{}, fmt.Errorf("error converting price updated event data to struct: %w", err) + } + + return true, eventData, nil +} + +// Get contracts +var rocketNetworkBalancesLock sync.Mutex + +func getRocketNetworkBalances(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*rocketpool.Contract, error) { + rocketNetworkBalancesLock.Lock() + defer rocketNetworkBalancesLock.Unlock() + return rp.GetContract("rocketNetworkBalances", opts) +} diff --git a/bindings/network/fees.go b/bindings/network/fees.go new file mode 100644 index 000000000..e8f78c3d6 --- /dev/null +++ b/bindings/network/fees.go @@ -0,0 +1,60 @@ +package network + +import ( + "fmt" + "math/big" + "sync" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/utils/eth" +) + +// Get the current network node demand in ETH +func GetNodeDemand(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*big.Int, error) { + rocketNetworkFees, err := getRocketNetworkFees(rp, opts) + if err != nil { + return nil, err + } + nodeDemand := new(*big.Int) + if err := rocketNetworkFees.Call(opts, nodeDemand, "getNodeDemand"); err != nil { + return nil, fmt.Errorf("error getting network node demand: %w", err) + } + return *nodeDemand, nil +} + +// Get the current network node commission rate +func GetNodeFee(rp *rocketpool.RocketPool, opts *bind.CallOpts) (float64, error) { + rocketNetworkFees, err := getRocketNetworkFees(rp, opts) + if err != nil { + return 0, err + } + nodeFee := new(*big.Int) + if err := rocketNetworkFees.Call(opts, nodeFee, "getNodeFee"); err != nil { + return 0, fmt.Errorf("error getting network node fee: %w", err) + } + return eth.WeiToEth(*nodeFee), nil +} + +// Get the network node fee for a node demand value +func GetNodeFeeByDemand(rp *rocketpool.RocketPool, nodeDemand *big.Int, opts *bind.CallOpts) (float64, error) { + rocketNetworkFees, err := getRocketNetworkFees(rp, opts) + if err != nil { + return 0, err + } + nodeFee := new(*big.Int) + if err := rocketNetworkFees.Call(opts, nodeFee, "getNodeFeeByDemand", nodeDemand); err != nil { + return 0, fmt.Errorf("error getting node fee by node demand: %w", err) + } + return eth.WeiToEth(*nodeFee), nil +} + +// Get contracts +var rocketNetworkFeesLock sync.Mutex + +func getRocketNetworkFees(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*rocketpool.Contract, error) { + rocketNetworkFeesLock.Lock() + defer rocketNetworkFeesLock.Unlock() + return rp.GetContract("rocketNetworkFees", opts) +} diff --git a/bindings/network/penalties.go b/bindings/network/penalties.go new file mode 100644 index 000000000..5fafd95c0 --- /dev/null +++ b/bindings/network/penalties.go @@ -0,0 +1,43 @@ +package network + +import ( + "fmt" + "math/big" + "sync" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + + "github.com/rocket-pool/smartnode/bindings/rocketpool" +) + +// Estimate the gas of SubmitPenalty +func EstimateSubmitPenaltyGas(rp *rocketpool.RocketPool, minipoolAddress common.Address, block *big.Int, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketNetworkPenalties, err := getRocketNetworkPenalties(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketNetworkPenalties.GetTransactionGasInfo(opts, "submitPenalty", minipoolAddress, block) +} + +// Submit penalty for given minipool +func SubmitPenalty(rp *rocketpool.RocketPool, minipoolAddress common.Address, block *big.Int, opts *bind.TransactOpts) (common.Hash, error) { + rocketNetworkPrices, err := getRocketNetworkPenalties(rp, nil) + if err != nil { + return common.Hash{}, err + } + tx, err := rocketNetworkPrices.Transact(opts, "submitPenalty", minipoolAddress, block) + if err != nil { + return common.Hash{}, fmt.Errorf("error submitting penalty: %w", err) + } + return tx.Hash(), nil +} + +// Get contracts +var rocketNetworkPenaltiesLock sync.Mutex + +func getRocketNetworkPenalties(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*rocketpool.Contract, error) { + rocketNetworkPenaltiesLock.Lock() + defer rocketNetworkPenaltiesLock.Unlock() + return rp.GetContract("rocketNetworkPenalties", opts) +} diff --git a/bindings/network/prices.go b/bindings/network/prices.go new file mode 100644 index 000000000..6b1aabf9b --- /dev/null +++ b/bindings/network/prices.go @@ -0,0 +1,178 @@ +package network + +import ( + "fmt" + "math/big" + "sync" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/utils/eth" +) + +// Info for a price updated event +type PriceUpdatedEvent struct { + BlockNumber *big.Int `json:"blockNumber"` + SlotTimestamp *big.Int `json:"slotTimestamp"` + RplPrice *big.Int `json:"rplPrice"` + Time *big.Int `json:"time"` +} + +// Get the block number which network prices are current for +func GetPricesBlock(rp *rocketpool.RocketPool, opts *bind.CallOpts) (uint64, error) { + rocketNetworkPrices, err := getRocketNetworkPrices(rp, opts) + if err != nil { + return 0, err + } + pricesBlock := new(*big.Int) + if err := rocketNetworkPrices.Call(opts, pricesBlock, "getPricesBlock"); err != nil { + return 0, fmt.Errorf("error getting network prices block: %w", err) + } + return (*pricesBlock).Uint64(), nil +} + +// Get the current network RPL price in ETH +func GetRPLPrice(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*big.Int, error) { + rocketNetworkPrices, err := getRocketNetworkPrices(rp, opts) + if err != nil { + return nil, err + } + rplPrice := new(*big.Int) + if err := rocketNetworkPrices.Call(opts, rplPrice, "getRPLPrice"); err != nil { + return nil, fmt.Errorf("error getting network RPL price: %w", err) + } + return *rplPrice, nil +} + +// Estimate the gas of SubmitPrices +func EstimateSubmitPricesGas(rp *rocketpool.RocketPool, block uint64, slotTimestamp uint64, rplPrice *big.Int, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketNetworkPrices, err := getRocketNetworkPrices(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketNetworkPrices.GetTransactionGasInfo(opts, "submitPrices", big.NewInt(int64(block)), big.NewInt(int64(slotTimestamp)), rplPrice) +} + +// Submit network prices and total effective RPL stake for an epoch +func SubmitPrices(rp *rocketpool.RocketPool, block uint64, slotTimestamp uint64, rplPrice *big.Int, opts *bind.TransactOpts) (common.Hash, error) { + rocketNetworkPrices, err := getRocketNetworkPrices(rp, nil) + if err != nil { + return common.Hash{}, err + } + tx, err := rocketNetworkPrices.Transact(opts, "submitPrices", big.NewInt(int64(block)), big.NewInt(int64(slotTimestamp)), rplPrice) + if err != nil { + return common.Hash{}, fmt.Errorf("error submitting network prices: %w", err) + } + return tx.Hash(), nil +} + +// Returns an array of block numbers for prices submissions the given trusted node has submitted since fromBlock +func GetPricesSubmissions(rp *rocketpool.RocketPool, nodeAddress common.Address, fromBlock uint64, intervalSize *big.Int, opts *bind.CallOpts) (*[]uint64, error) { + // Get contracts + rocketNetworkPrices, err := getRocketNetworkPrices(rp, opts) + if err != nil { + return nil, err + } + // Construct a filter query for relevant logs + addressFilter := []common.Address{*rocketNetworkPrices.Address} + topicFilter := [][]common.Hash{{rocketNetworkPrices.ABI.Events["PricesSubmitted"].ID}, {common.BytesToHash(nodeAddress.Bytes())}} + + // Get the event logs + logs, err := eth.GetLogs(rp, addressFilter, topicFilter, intervalSize, big.NewInt(int64(fromBlock)), nil, nil) + if err != nil { + return nil, err + } + timestamps := make([]uint64, len(logs)) + for i, log := range logs { + values := make(map[string]interface{}) + // Decode the event + if rocketNetworkPrices.ABI.Events["PricesSubmitted"].Inputs.UnpackIntoMap(values, log.Data) != nil { + return nil, err + } + timestamps[i] = values["block"].(*big.Int).Uint64() + } + return ×tamps, nil +} + +// Returns an array of members who submitted prices since fromBlock +func GetLatestPricesSubmissions(rp *rocketpool.RocketPool, fromBlock uint64, intervalSize *big.Int, opts *bind.CallOpts) ([]common.Address, error) { + // Get contracts + rocketNetworkPrices, err := getRocketNetworkPrices(rp, opts) + if err != nil { + return nil, err + } + // Construct a filter query for relevant logs + addressFilter := []common.Address{*rocketNetworkPrices.Address} + topicFilter := [][]common.Hash{{rocketNetworkPrices.ABI.Events["PricesSubmitted"].ID}} + + // Get the event logs + logs, err := eth.GetLogs(rp, addressFilter, topicFilter, intervalSize, big.NewInt(int64(fromBlock)), nil, nil) + if err != nil { + return nil, err + } + + results := make([]common.Address, len(logs)) + for i, log := range logs { + // Topic 0 is the event, topic 1 is the "from" address + address := common.BytesToAddress(log.Topics[1].Bytes()) + results[i] = address + } + return results, nil +} + +// Get the event info for a price update +func GetPriceUpdatedEvent(rp *rocketpool.RocketPool, blockNumber uint64, opts *bind.CallOpts) (bool, PriceUpdatedEvent, error) { + // Get contracts + rocketNetworkPrices, err := getRocketNetworkPrices(rp, opts) + if err != nil { + return false, PriceUpdatedEvent{}, err + } + + indexBig := big.NewInt(0).SetUint64(blockNumber) + + // Create the list of addresses to check + currentAddress := *rocketNetworkPrices.Address + rocketNetworkPricesAddress := []common.Address{currentAddress} + + // Construct a filter query for relevant logs + pricesUpdatedEvent := rocketNetworkPrices.ABI.Events["PricesUpdated"] + indexBytes := [32]byte{} + indexBig.FillBytes(indexBytes[:]) + addressFilter := rocketNetworkPricesAddress + topicFilter := [][]common.Hash{{pricesUpdatedEvent.ID}, {indexBytes}} + + // Get the event logs + logs, err := eth.GetLogs(rp, addressFilter, topicFilter, big.NewInt(100), big.NewInt(int64(blockNumber)), big.NewInt(int64(blockNumber+1000)), nil) + if err != nil { + return false, PriceUpdatedEvent{}, err + } + if len(logs) == 0 { + return false, PriceUpdatedEvent{}, nil + } + + // Get the log info values + values, err := pricesUpdatedEvent.Inputs.Unpack(logs[0].Data) + if err != nil { + return false, PriceUpdatedEvent{}, fmt.Errorf("error unpacking price updated event data: %w", err) + } + + // Convert to a native struct + var eventData PriceUpdatedEvent + err = pricesUpdatedEvent.Inputs.Copy(&eventData, values) + if err != nil { + return false, PriceUpdatedEvent{}, fmt.Errorf("error converting price updated event data to struct: %w", err) + } + + return true, eventData, nil +} + +// Get contracts +var rocketNetworkPricesLock sync.Mutex + +func getRocketNetworkPrices(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*rocketpool.Contract, error) { + rocketNetworkPricesLock.Lock() + defer rocketNetworkPricesLock.Unlock() + return rp.GetContract("rocketNetworkPrices", opts) +} diff --git a/bindings/network/revenues.go b/bindings/network/revenues.go new file mode 100644 index 000000000..5ad006a6d --- /dev/null +++ b/bindings/network/revenues.go @@ -0,0 +1,51 @@ +package network + +import ( + "fmt" + "math/big" + "sync" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/rocket-pool/smartnode/bindings/rocketpool" +) + +type RevenueSplit struct { + NodeShare *big.Int `abi:"nodeShare"` + VoterShare *big.Int `abi:"voterShare"` + RethShare *big.Int `abi:"rethShare"` +} + +// Get the current node share +func GetCurrentNodeShare(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*big.Int, error) { + rocketNetworkRevenues, err := getRocketNetworkRevenues(rp, opts) + if err != nil { + return nil, err + } + nodeShare := new(*big.Int) + if err := rocketNetworkRevenues.Call(opts, nodeShare, "getCurrentNodeShare"); err != nil { + return nil, fmt.Errorf("error getting network node share: %w", err) + } + return *nodeShare, nil +} + +// Calculates the time-weighted average revenue split values between the supplied block number and now +func CalculateSplit(rp *rocketpool.RocketPool, sinceBlock uint64, opts *bind.CallOpts) (RevenueSplit, error) { + rocketNetworkRevenues, err := getRocketNetworkRevenues(rp, opts) + if err != nil { + return RevenueSplit{}, err + } + revenueSplit := new(RevenueSplit) + if err := rocketNetworkRevenues.Call(opts, revenueSplit, "calculateSplit", big.NewInt(int64(sinceBlock))); err != nil { + return RevenueSplit{}, fmt.Errorf("error calculating the revenue split %w", err) + } + return *revenueSplit, nil +} + +// Get contracts +var rocketNetworkRevenuesLock sync.Mutex + +func getRocketNetworkRevenues(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*rocketpool.Contract, error) { + rocketNetworkRevenuesLock.Lock() + defer rocketNetworkRevenuesLock.Unlock() + return rp.GetContract("rocketNetworkRevenues", opts) +} diff --git a/bindings/network/voting.go b/bindings/network/voting.go new file mode 100644 index 000000000..db39eae30 --- /dev/null +++ b/bindings/network/voting.go @@ -0,0 +1,229 @@ +package network + +import ( + "fmt" + "math/big" + "sync" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/rocket-pool/smartnode/bindings/node" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/types" + "github.com/rocket-pool/smartnode/bindings/utils/multicall" + "golang.org/x/sync/errgroup" +) + +const ( + nodeVotingDetailsBatchSize uint64 = 250 +) + +// Get the version of the Rocket Network Voting Contract +func GetRocketNetworkVotingVersion(rp *rocketpool.RocketPool, opts *bind.CallOpts) (uint8, error) { + rocketNetworkVoting, err := getRocketNetworkVoting(rp, opts) + if err != nil { + return 0, err + } + return rocketpool.GetContractVersion(rp, *rocketNetworkVoting.Address, opts) +} + +// Gets the voting power and delegation info for every node at the specified block using multicall +func GetNodeInfoSnapshotFast(rp *rocketpool.RocketPool, blockNumber uint32, multicallAddress common.Address, opts *bind.CallOpts) ([]types.NodeVotingInfo, error) { + rocketNetworkVoting, err := getRocketNetworkVoting(rp, opts) + if err != nil { + return nil, err + } + + // Get the number of voting nodes + nodeCountBig, err := GetVotingNodeCount(rp, blockNumber, opts) + if err != nil { + return nil, fmt.Errorf("error getting voting node count: %w", err) + } + nodeCount := nodeCountBig.Uint64() + + // Get the node addresses + nodeAddresses, err := node.GetNodeAddressesFast(rp, multicallAddress, opts) + if err != nil { + return nil, fmt.Errorf("error getting node addresses: %w", err) + } + + // Sync + var wg errgroup.Group + + // Run the getters in batches + votingInfos := make([]types.NodeVotingInfo, nodeCount) + for i := uint64(0); i < nodeCount; i += nodeVotingDetailsBatchSize { + i := i + max := i + nodeVotingDetailsBatchSize + if max > nodeCount { + max = nodeCount + } + + // Load details + wg.Go(func() error { + var err error + mc, err := multicall.NewMultiCaller(rp.Client, multicallAddress) + if err != nil { + return err + } + for j := i; j < max; j++ { + nodeAddress := nodeAddresses[j] + votingInfos[j].NodeAddress = nodeAddress + mc.AddCall(rocketNetworkVoting, &votingInfos[j].VotingPower, "getVotingPower", nodeAddress, blockNumber) + mc.AddCall(rocketNetworkVoting, &votingInfos[j].Delegate, "getDelegate", nodeAddress, blockNumber) + } + _, err = mc.FlexibleCall(true, opts) + if err != nil { + return fmt.Errorf("error executing multicall: %w", err) + } + return nil + }) + } + + // Wait for data + if err := wg.Wait(); err != nil { + return nil, err + } + + return votingInfos, nil +} + +// Check whether or not on-chain voting has been initialized for the given node +func GetVotingInitialized(rp *rocketpool.RocketPool, address common.Address, opts *bind.CallOpts) (bool, error) { + rocketNetworkVoting, err := getRocketNetworkVoting(rp, nil) + if err != nil { + return false, err + } + value := new(bool) + if err := rocketNetworkVoting.Call(opts, value, "getVotingInitialised", address); err != nil { + return false, fmt.Errorf("error getting voting initialized status: %w", err) + } + return *value, nil +} + +// Estimate the gas of InitializeVoting +func EstimateInitializeVotingGas(rp *rocketpool.RocketPool, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketNetworkVoting, err := getRocketNetworkVoting(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketNetworkVoting.GetTransactionGasInfo(opts, "initialiseVoting") +} + +// Initialize on-chain voting for the node +func InitializeVoting(rp *rocketpool.RocketPool, opts *bind.TransactOpts) (common.Hash, error) { + rocketNetworkVoting, err := getRocketNetworkVoting(rp, nil) + if err != nil { + return common.Hash{}, err + } + tx, err := rocketNetworkVoting.Transact(opts, "initialiseVoting") + if err != nil { + return common.Hash{}, fmt.Errorf("error initializing voting: %w", err) + } + return tx.Hash(), nil +} + +// Estimate the gas of InitializeVotingWithDelegate +func EstimateInitializeVotingWithDelegateGas(rp *rocketpool.RocketPool, delegateAddress common.Address, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketNetworkVoting, err := getRocketNetworkVoting(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketNetworkVoting.GetTransactionGasInfo(opts, "initialiseVotingWithDelegate", delegateAddress) +} + +// Initialize on-chain voting for the node and delegate voting power at the same transaction +func InitializeVotingWithDelegate(rp *rocketpool.RocketPool, delegateAddress common.Address, opts *bind.TransactOpts) (common.Hash, error) { + rocketNetworkVoting, err := getRocketNetworkVoting(rp, nil) + if err != nil { + return common.Hash{}, err + } + tx, err := rocketNetworkVoting.Transact(opts, "initialiseVotingWithDelegate", delegateAddress) + if err != nil { + return common.Hash{}, fmt.Errorf("error initializing voting with delegate: %w", err) + } + return tx.Hash(), nil +} + +// Get the number of nodes that were present in the network at the provided block +func GetVotingNodeCount(rp *rocketpool.RocketPool, blockNumber uint32, opts *bind.CallOpts) (*big.Int, error) { + rocketNetworkVoting, err := getRocketNetworkVoting(rp, nil) + if err != nil { + return nil, err + } + value := new(*big.Int) + if err := rocketNetworkVoting.Call(opts, value, "getNodeCount", blockNumber); err != nil { + return nil, fmt.Errorf("error getting node count for block %d: %w", blockNumber, err) + } + return *value, nil +} + +// Get the voting power of the given node on the provided block +func GetVotingPower(rp *rocketpool.RocketPool, address common.Address, blockNumber uint32, opts *bind.CallOpts) (*big.Int, error) { + rocketNetworkVoting, err := getRocketNetworkVoting(rp, nil) + if err != nil { + return nil, err + } + value := new(*big.Int) + if err := rocketNetworkVoting.Call(opts, value, "getVotingPower", address, blockNumber); err != nil { + return nil, fmt.Errorf("error getting voting power for node %s on block %d: %w", address.Hex(), blockNumber, err) + } + return *value, nil +} + +// Get the address that the provided node has delegated voting power to on the given block +func GetVotingDelegate(rp *rocketpool.RocketPool, address common.Address, blockNumber uint32, opts *bind.CallOpts) (common.Address, error) { + rocketNetworkVoting, err := getRocketNetworkVoting(rp, nil) + if err != nil { + return common.Address{}, err + } + value := new(common.Address) + if err := rocketNetworkVoting.Call(opts, value, "getDelegate", address, blockNumber); err != nil { + return common.Address{}, fmt.Errorf("error getting delegate for node %s on block %d: %w", address.Hex(), blockNumber, err) + } + return *value, nil +} + +// Get the address that the provided node has currently delegated voting power to +func GetCurrentVotingDelegate(rp *rocketpool.RocketPool, address common.Address, opts *bind.CallOpts) (common.Address, error) { + rocketNetworkVoting, err := getRocketNetworkVoting(rp, nil) + if err != nil { + return common.Address{}, err + } + value := new(common.Address) + if err := rocketNetworkVoting.Call(opts, value, "getCurrentDelegate", address); err != nil { + return common.Address{}, fmt.Errorf("error getting current delegate for node %s: %w", address.Hex(), err) + } + return *value, nil +} + +// Estimate the gas of SetVotingDelegate +func EstimateSetVotingDelegateGas(rp *rocketpool.RocketPool, newDelegate common.Address, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketNetworkVoting, err := getRocketNetworkVoting(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketNetworkVoting.GetTransactionGasInfo(opts, "setDelegate", newDelegate) +} + +// Set the voting delegate for the node +func SetVotingDelegate(rp *rocketpool.RocketPool, newDelegate common.Address, opts *bind.TransactOpts) (common.Hash, error) { + rocketNetworkVoting, err := getRocketNetworkVoting(rp, nil) + if err != nil { + return common.Hash{}, err + } + tx, err := rocketNetworkVoting.Transact(opts, "setDelegate", newDelegate) + if err != nil { + return common.Hash{}, fmt.Errorf("error setting voting delegate: %w", err) + } + return tx.Hash(), nil +} + +// Get contracts +var rocketNetworkVotingLock sync.Mutex + +func getRocketNetworkVoting(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*rocketpool.Contract, error) { + rocketNetworkVotingLock.Lock() + defer rocketNetworkVotingLock.Unlock() + return rp.GetContract("rocketNetworkVoting", opts) +} diff --git a/bindings/node/deposit.go b/bindings/node/deposit.go new file mode 100644 index 000000000..903a2f227 --- /dev/null +++ b/bindings/node/deposit.go @@ -0,0 +1,225 @@ +package node + +import ( + "fmt" + "math/big" + "sync" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + + "github.com/rocket-pool/smartnode/bindings/rocketpool" + rptypes "github.com/rocket-pool/smartnode/bindings/types" + "github.com/rocket-pool/smartnode/bindings/utils/eth" +) + +// Estimate the gas of Deposit +func EstimateDepositGas(rp *rocketpool.RocketPool, bondAmount *big.Int, useExpressTicket bool, validatorPubkey rptypes.ValidatorPubkey, validatorSignature rptypes.ValidatorSignature, depositDataRoot common.Hash, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketNodeDeposit, err := getRocketNodeDeposit(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketNodeDeposit.GetTransactionGasInfo(opts, "deposit", bondAmount, useExpressTicket, validatorPubkey[:], validatorSignature[:], depositDataRoot) +} + +// Make a node deposit +func Deposit(rp *rocketpool.RocketPool, bondAmount *big.Int, useExpressTicket bool, validatorPubkey rptypes.ValidatorPubkey, validatorSignature rptypes.ValidatorSignature, depositDataRoot common.Hash, opts *bind.TransactOpts) (*types.Transaction, error) { + rocketNodeDeposit, err := getRocketNodeDeposit(rp, nil) + if err != nil { + return nil, err + } + tx, err := rocketNodeDeposit.Transact(opts, "deposit", bondAmount, useExpressTicket, validatorPubkey[:], validatorSignature[:], depositDataRoot) + if err != nil { + return nil, fmt.Errorf("error making node deposit: %w", err) + } + return tx, nil +} + +// Estimate the gas to WithdrawETH +func EstimateWithdrawEthGas(rp *rocketpool.RocketPool, nodeAccount common.Address, ethAmount *big.Int, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketNodeDeposit, err := getRocketNodeDeposit(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketNodeDeposit.GetTransactionGasInfo(opts, "withdrawEth", nodeAccount, ethAmount) +} + +// Withdraw unused Ether that was staked on behalf of the node +func WithdrawEth(rp *rocketpool.RocketPool, nodeAccount common.Address, ethAmount *big.Int, opts *bind.TransactOpts) (*types.Transaction, error) { + rocketNodeDeposit, err := getRocketNodeDeposit(rp, nil) + if err != nil { + return nil, err + } + tx, err := rocketNodeDeposit.Transact(opts, "withdrawEth", nodeAccount, ethAmount) + if err != nil { + return nil, fmt.Errorf("error trying to withdraw ETH: %w", err) + } + return tx, nil +} + +// Estimate the gas required to withdraw credit +func EstimateWithdrawCreditGas(rp *rocketpool.RocketPool, amount *big.Int, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketDepositPool, err := getRocketDepositPool(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketDepositPool.GetTransactionGasInfo(opts, "withdrawCredit", amount) +} + +// Withdraws credit store on a node as rETH +func WithdrawCredit(rp *rocketpool.RocketPool, amount *big.Int, opts *bind.TransactOpts) (*types.Transaction, error) { + rocketDepositPool, err := getRocketDepositPool(rp, nil) + if err != nil { + return nil, err + } + tx, err := rocketDepositPool.Transact(opts, "withdrawCredit", amount) + if err != nil { + return nil, fmt.Errorf("error withdrawing credit: %w", err) + } + return tx, nil +} + +// Estimate the gas of DepositWithCredit +func EstimateDepositWithCreditGas(rp *rocketpool.RocketPool, bondAmount *big.Int, useExpressTicket bool, validatorPubkey rptypes.ValidatorPubkey, validatorSignature rptypes.ValidatorSignature, depositDataRoot common.Hash, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketNodeDeposit, err := getRocketNodeDeposit(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketNodeDeposit.GetTransactionGasInfo(opts, "depositWithCredit", bondAmount, useExpressTicket, validatorPubkey[:], validatorSignature[:], depositDataRoot) +} + +// Make a node deposit by using the credit balance +func DepositWithCredit(rp *rocketpool.RocketPool, bondAmount *big.Int, useExpressTicket bool, validatorPubkey rptypes.ValidatorPubkey, validatorSignature rptypes.ValidatorSignature, depositDataRoot common.Hash, opts *bind.TransactOpts) (*types.Transaction, error) { + rocketNodeDeposit, err := getRocketNodeDeposit(rp, nil) + if err != nil { + return nil, err + } + tx, err := rocketNodeDeposit.Transact(opts, "depositWithCredit", bondAmount, useExpressTicket, validatorPubkey[:], validatorSignature[:], depositDataRoot) + if err != nil { + return nil, fmt.Errorf("error making node deposit with credit: %w", err) + } + return tx, nil +} + +// Estimate the gas of CreateVacantMinipool +func EstimateCreateVacantMinipoolGas(rp *rocketpool.RocketPool, bondAmount *big.Int, minimumNodeFee float64, validatorPubkey rptypes.ValidatorPubkey, salt *big.Int, expectedMinipoolAddress common.Address, currentBalance *big.Int, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketNodeDeposit, err := getRocketNodeDeposit(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketNodeDeposit.GetTransactionGasInfo(opts, "createVacantMinipool", bondAmount, eth.EthToWei(minimumNodeFee), validatorPubkey[:], salt, expectedMinipoolAddress, currentBalance) +} + +// Make a vacant minipool for solo staker migration +func CreateVacantMinipool(rp *rocketpool.RocketPool, bondAmount *big.Int, minimumNodeFee float64, validatorPubkey rptypes.ValidatorPubkey, salt *big.Int, expectedMinipoolAddress common.Address, currentBalance *big.Int, opts *bind.TransactOpts) (*types.Transaction, error) { + rocketNodeDeposit, err := getRocketNodeDeposit(rp, nil) + if err != nil { + return nil, err + } + tx, err := rocketNodeDeposit.Transact(opts, "createVacantMinipool", bondAmount, eth.EthToWei(minimumNodeFee), validatorPubkey[:], salt, expectedMinipoolAddress, currentBalance) + if err != nil { + return nil, fmt.Errorf("error creating vacant minipool: %w", err) + } + return tx, nil +} + +// Get the amount of ETH in the node's deposit credit bank +func GetNodeDepositCredit(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (*big.Int, error) { + rocketNodeDeposit, err := getRocketNodeDeposit(rp, opts) + if err != nil { + return nil, err + } + + creditBalance := new(*big.Int) + if err := rocketNodeDeposit.Call(opts, creditBalance, "getNodeDepositCredit", nodeAddress); err != nil { + return nil, fmt.Errorf("error getting node deposit credit: %w", err) + } + return *creditBalance, nil +} + +// Get the current ETH balance for the given node operator +func GetNodeEthBalance(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (*big.Int, error) { + rocketNodeDeposit, err := getRocketNodeDeposit(rp, opts) + if err != nil { + return nil, err + } + + creditBalance := new(*big.Int) + if err := rocketNodeDeposit.Call(opts, creditBalance, "getNodeEthBalance", nodeAddress); err != nil { + return nil, fmt.Errorf("error getting node ETH balance: %w", err) + } + return *creditBalance, nil +} + +// Get the sum of the credit balance of a given node operator and their ETH balance +func GetNodeCreditAndBalance(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (*big.Int, error) { + rocketNodeDeposit, err := getRocketNodeDeposit(rp, opts) + if err != nil { + return nil, err + } + + creditAndBalance := new(*big.Int) + if err := rocketNodeDeposit.Call(opts, creditAndBalance, "getNodeCreditAndBalance", nodeAddress); err != nil { + return nil, fmt.Errorf("error getting node credit and ETH balance: %w", err) + } + return *creditAndBalance, nil +} + +// Get the sum of the amount of ETH credit currently usable by a given node operator and their balance +func GetNodeUsableCreditAndBalance(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (*big.Int, error) { + rocketNodeDeposit, err := getRocketNodeDeposit(rp, opts) + if err != nil { + return nil, err + } + + usableCreditBalance := new(*big.Int) + if err := rocketNodeDeposit.Call(opts, usableCreditBalance, "getNodeUsableCreditAndBalance", nodeAddress); err != nil { + return nil, fmt.Errorf("error getting node usable credit and ETH balance: %w", err) + } + return *usableCreditBalance, nil +} + +// Get the amount of ETH credit currently usable by a given node operator +func GetNodeUsableCredit(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (*big.Int, error) { + rocketNodeDeposit, err := getRocketNodeDeposit(rp, opts) + if err != nil { + return nil, err + } + + usableCredit := new(*big.Int) + if err := rocketNodeDeposit.Call(opts, usableCredit, "getNodeUsableCredit", nodeAddress); err != nil { + return nil, fmt.Errorf("error getting node usable credit: %w", err) + } + return *usableCredit, nil +} + +func GetBondRequirement(rp *rocketpool.RocketPool, numValidators *big.Int, opts *bind.CallOpts) (*big.Int, error) { + rocketNodeDeposit, err := getRocketNodeDeposit(rp, opts) + if err != nil { + return nil, err + } + + bondRequirement := new(*big.Int) + if err := rocketNodeDeposit.Call(opts, bondRequirement, "getBondRequirement", numValidators); err != nil { + return nil, fmt.Errorf("error getting the bond requirement: %w", err) + } + return *bondRequirement, nil +} + +// Get contracts +var rocketNodeDepositLock sync.Mutex + +func getRocketNodeDeposit(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*rocketpool.Contract, error) { + rocketNodeDepositLock.Lock() + defer rocketNodeDepositLock.Unlock() + return rp.GetContract("rocketNodeDeposit", opts) +} + +var rocketDepositPoolLock sync.Mutex + +func getRocketDepositPool(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*rocketpool.Contract, error) { + rocketDepositPoolLock.Lock() + defer rocketDepositPoolLock.Unlock() + return rp.GetContract("rocketDepositPool", opts) +} diff --git a/bindings/node/distributor.go b/bindings/node/distributor.go new file mode 100644 index 000000000..9563437ed --- /dev/null +++ b/bindings/node/distributor.go @@ -0,0 +1,99 @@ +package node + +import ( + "fmt" + "math/big" + "sync" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + + "github.com/rocket-pool/smartnode/bindings/rocketpool" +) + +// Distributor contract +type Distributor struct { + Address common.Address + Contract *rocketpool.Contract + RocketPool *rocketpool.RocketPool +} + +// Create new distributor contract +func NewDistributor(rp *rocketpool.RocketPool, address common.Address, opts *bind.CallOpts) (*Distributor, error) { + + // Get contract + contract, err := getDistributorContract(rp, address, opts) + if err != nil { + return nil, err + } + + // Create and return + return &Distributor{ + Address: address, + Contract: contract, + RocketPool: rp, + }, nil +} + +// Gets the deterministic address for a node's reward distributor contract +func GetDistributorAddress(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (common.Address, error) { + rocketNodeDistributorFactory, err := getRocketNodeDistributorFactory(rp, opts) + if err != nil { + return common.Address{}, err + } + var address common.Address + if err := rocketNodeDistributorFactory.Call(opts, &address, "getProxyAddress", nodeAddress); err != nil { + return common.Address{}, fmt.Errorf("error getting distributor address: %w", err) + } + return address, nil +} + +// Estimate the gas of a distribute +func (d *Distributor) EstimateDistributeGas(opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return d.Contract.GetTransactionGasInfo(opts, "distribute") +} + +// Distribute the contract's balance to the rETH contract and the user +func (d *Distributor) Distribute(opts *bind.TransactOpts) (common.Hash, error) { + tx, err := d.Contract.Transact(opts, "distribute") + if err != nil { + return common.Hash{}, fmt.Errorf("error distributing fee distributor balance: %w", err) + } + return tx.Hash(), nil +} + +// Gets the node share of the distributor's current balance +func (d *Distributor) GetNodeShare(opts *bind.CallOpts) (*big.Int, error) { + nodeShare := new(*big.Int) + if err := d.Contract.Call(opts, nodeShare, "getNodeShare"); err != nil { + return nil, fmt.Errorf("error getting distributor %s node share: %w", d.Address.Hex(), err) + } + return *nodeShare, nil +} + +// Gets the user share of the distributor's current balance +func (d *Distributor) GetUserShare(opts *bind.CallOpts) (*big.Int, error) { + userShare := new(*big.Int) + if err := d.Contract.Call(opts, userShare, "getUserShare"); err != nil { + return nil, fmt.Errorf("error getting distributor %s user share: %w", d.Address.Hex(), err) + } + return *userShare, nil +} + +// Get contracts +var rocketNodeDistributorFactoryLock sync.Mutex + +func getRocketNodeDistributorFactory(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*rocketpool.Contract, error) { + rocketNodeDistributorFactoryLock.Lock() + defer rocketNodeDistributorFactoryLock.Unlock() + return rp.GetContract("rocketNodeDistributorFactory", opts) +} + +// Get a distributor contract +var rocketDistributorLock sync.Mutex + +func getDistributorContract(rp *rocketpool.RocketPool, distributorAddress common.Address, opts *bind.CallOpts) (*rocketpool.Contract, error) { + rocketDistributorLock.Lock() + defer rocketDistributorLock.Unlock() + return rp.MakeContract("rocketNodeDistributorDelegate", distributorAddress, opts) +} diff --git a/bindings/node/node.go b/bindings/node/node.go new file mode 100644 index 000000000..d1d10f520 --- /dev/null +++ b/bindings/node/node.go @@ -0,0 +1,790 @@ +package node + +import ( + "fmt" + "math" + "math/big" + "sync" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "golang.org/x/sync/errgroup" + + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/storage" + "github.com/rocket-pool/smartnode/bindings/utils/eth" + "github.com/rocket-pool/smartnode/bindings/utils/multicall" + "github.com/rocket-pool/smartnode/bindings/utils/strings" +) + +// Settings +const ( + nodeAddressFastBatchSize int = 1000 + NodeAddressBatchSize = 50 + NodeDetailsBatchSize = 20 + SmoothingPoolCountBatchSize uint64 = 2000 + NativeNodeDetailsBatchSize = 10000 +) + +// Node details +type NodeDetails struct { + Address common.Address `json:"address"` + Exists bool `json:"exists"` + PrimaryWithdrawalAddress common.Address `json:"primaryWithdrawalAddress"` + PendingPrimaryWithdrawalAddress common.Address `json:"pendingPrimaryWithdrawalAddress"` + IsRPLWithdrawalAddressSet bool `json:"isRPLWithdrawalAddressSet"` + RPLWithdrawalAddress common.Address `json:"rplWithdrawalAddress"` + PendingRPLWithdrawalAddress common.Address `json:"pendingRPLWithdrawalAddress"` + TimezoneLocation string `json:"timezoneLocation"` +} + +// Count of nodes belonging to a timezone +type TimezoneCount struct { + Timezone string `abi:"timezone"` + Count *big.Int `abi:"count"` +} + +// The results of the trusted node participation calculation +type TrustedNodeParticipation struct { + StartBlock uint64 + UpdateFrequency uint64 + UpdateCount uint64 + Probability float64 + ExpectedSubmissions float64 + ActualSubmissions map[common.Address]float64 + Participation map[common.Address][]bool +} + +// Get the version of the Node Manager contract +func GetNodeManagerVersion(rp *rocketpool.RocketPool, opts *bind.CallOpts) (uint8, error) { + rocketNodeManager, err := getRocketNodeManager(rp, opts) + if err != nil { + return 0, err + } + return rocketpool.GetContractVersion(rp, *rocketNodeManager.Address, opts) +} + +// Get all node details +// The 'includeRplWithdrawalAddress' flag is used for backwards compatibility with Atlas, - set it to `false` if Houston hasn't been deployed yet +func GetNodes(rp *rocketpool.RocketPool, includeRplWithdrawalAddress bool, opts *bind.CallOpts) ([]NodeDetails, error) { + + // Get node addresses + nodeAddresses, err := GetNodeAddresses(rp, opts) + if err != nil { + return []NodeDetails{}, err + } + + // Load node details in batches + details := make([]NodeDetails, len(nodeAddresses)) + for bsi := 0; bsi < len(nodeAddresses); bsi += NodeDetailsBatchSize { + + // Get batch start & end index + nsi := bsi + nei := bsi + NodeDetailsBatchSize + if nei > len(nodeAddresses) { + nei = len(nodeAddresses) + } + + // Load details + var wg errgroup.Group + for ni := nsi; ni < nei; ni++ { + ni := ni + wg.Go(func() error { + nodeAddress := nodeAddresses[ni] + nodeDetails, err := GetNodeDetails(rp, nodeAddress, includeRplWithdrawalAddress, opts) + if err == nil { + details[ni] = nodeDetails + } + return err + }) + } + if err := wg.Wait(); err != nil { + return []NodeDetails{}, err + } + + } + + // Return + return details, nil + +} + +// Get all node addresses +func GetNodeAddresses(rp *rocketpool.RocketPool, opts *bind.CallOpts) ([]common.Address, error) { + + // Get node count + nodeCount, err := GetNodeCount(rp, opts) + if err != nil { + return []common.Address{}, err + } + + // Load node addresses in batches + addresses := make([]common.Address, nodeCount) + for bsi := uint64(0); bsi < nodeCount; bsi += NodeAddressBatchSize { + + // Get batch start & end index + nsi := bsi + nei := bsi + NodeAddressBatchSize + if nei > nodeCount { + nei = nodeCount + } + + // Load addresses + var wg errgroup.Group + for ni := nsi; ni < nei; ni++ { + ni := ni + wg.Go(func() error { + address, err := GetNodeAt(rp, ni, opts) + if err == nil { + addresses[ni] = address + } + return err + }) + } + if err := wg.Wait(); err != nil { + return []common.Address{}, err + } + + } + + // Return + return addresses, nil + +} + +// Get all node addresses using a multicaller +func GetNodeAddressesFast(rp *rocketpool.RocketPool, multicallAddress common.Address, opts *bind.CallOpts) ([]common.Address, error) { + rocketNodeManager, err := getRocketNodeManager(rp, opts) + if err != nil { + return nil, err + } + + // Get minipool count + nodeCount, err := GetNodeCount(rp, opts) + if err != nil { + return []common.Address{}, err + } + + // Sync + var wg errgroup.Group + addresses := make([]common.Address, nodeCount) + + // Run the getters in batches + count := int(nodeCount) + for i := 0; i < count; i += nodeAddressFastBatchSize { + i := i + max := i + nodeAddressFastBatchSize + if max > count { + max = count + } + + wg.Go(func() error { + var err error + mc, err := multicall.NewMultiCaller(rp.Client, multicallAddress) + if err != nil { + return err + } + for j := i; j < max; j++ { + mc.AddCall(rocketNodeManager, &addresses[j], "getNodeAt", big.NewInt(int64(j))) + } + _, err = mc.FlexibleCall(true, opts) + if err != nil { + return fmt.Errorf("error executing multicall: %w", err) + } + return nil + }) + } + + if err := wg.Wait(); err != nil { + return nil, fmt.Errorf("error getting node addresses: %w", err) + } + + return addresses, nil +} + +// Get a node's details +// The 'includeRplWithdrawalAddress' flag is used for backwards compatibility with Atlas, - set it to `false` if Houston hasn't been deployed yet +func GetNodeDetails(rp *rocketpool.RocketPool, nodeAddress common.Address, includeRplWithdrawalAddress bool, opts *bind.CallOpts) (NodeDetails, error) { + + // Data + var wg errgroup.Group + var exists bool + var primaryWithdrawalAddress common.Address + var pendingPrimaryWithdrawalAddress common.Address + var isRPLWithdrawalAddressSet bool + var rplWithdrawalAddress common.Address + var pendingRPLWithdrawalAddress common.Address + var timezoneLocation string + + // Load data + wg.Go(func() error { + var err error + exists, err = GetNodeExists(rp, nodeAddress, opts) + return err + }) + wg.Go(func() error { + var err error + primaryWithdrawalAddress, err = storage.GetNodeWithdrawalAddress(rp, nodeAddress, opts) + return err + }) + wg.Go(func() error { + var err error + pendingPrimaryWithdrawalAddress, err = storage.GetNodePendingWithdrawalAddress(rp, nodeAddress, opts) + return err + }) + if includeRplWithdrawalAddress { + wg.Go(func() error { + var err error + isRPLWithdrawalAddressSet, err = GetNodeRPLWithdrawalAddressIsSet(rp, nodeAddress, opts) + return err + }) + wg.Go(func() error { + var err error + rplWithdrawalAddress, err = GetNodeRPLWithdrawalAddress(rp, nodeAddress, opts) + return err + }) + wg.Go(func() error { + var err error + pendingRPLWithdrawalAddress, err = GetNodePendingRPLWithdrawalAddress(rp, nodeAddress, opts) + return err + }) + } + wg.Go(func() error { + var err error + timezoneLocation, err = GetNodeTimezoneLocation(rp, nodeAddress, opts) + return err + }) + + // Wait for data + if err := wg.Wait(); err != nil { + return NodeDetails{}, err + } + + // Return + return NodeDetails{ + Address: nodeAddress, + Exists: exists, + PrimaryWithdrawalAddress: primaryWithdrawalAddress, + PendingPrimaryWithdrawalAddress: pendingPrimaryWithdrawalAddress, + IsRPLWithdrawalAddressSet: isRPLWithdrawalAddressSet, + RPLWithdrawalAddress: rplWithdrawalAddress, + PendingRPLWithdrawalAddress: pendingRPLWithdrawalAddress, + TimezoneLocation: timezoneLocation, + }, nil + +} + +// Get the number of nodes in the network +func GetNodeCount(rp *rocketpool.RocketPool, opts *bind.CallOpts) (uint64, error) { + rocketNodeManager, err := getRocketNodeManager(rp, opts) + if err != nil { + return 0, err + } + nodeCount := new(*big.Int) + if err := rocketNodeManager.Call(opts, nodeCount, "getNodeCount"); err != nil { + return 0, fmt.Errorf("error getting node count: %w", err) + } + return (*nodeCount).Uint64(), nil +} + +// Get a breakdown of the number of nodes per timezone +func GetNodeCountPerTimezone(rp *rocketpool.RocketPool, offset, limit *big.Int, opts *bind.CallOpts) ([]TimezoneCount, error) { + rocketNodeManager, err := getRocketNodeManager(rp, opts) + if err != nil { + return []TimezoneCount{}, err + } + timezoneCounts := new([]TimezoneCount) + if err := rocketNodeManager.Call(opts, timezoneCounts, "getNodeCountPerTimezone", offset, limit); err != nil { + return []TimezoneCount{}, fmt.Errorf("error getting node count: %w", err) + } + return *timezoneCounts, nil +} + +// Get a node address by index +func GetNodeAt(rp *rocketpool.RocketPool, index uint64, opts *bind.CallOpts) (common.Address, error) { + rocketNodeManager, err := getRocketNodeManager(rp, opts) + if err != nil { + return common.Address{}, err + } + nodeAddress := new(common.Address) + if err := rocketNodeManager.Call(opts, nodeAddress, "getNodeAt", big.NewInt(int64(index))); err != nil { + return common.Address{}, fmt.Errorf("error getting node %d address: %w", index, err) + } + return *nodeAddress, nil +} + +// Check whether a node exists +func GetNodeExists(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (bool, error) { + rocketNodeManager, err := getRocketNodeManager(rp, opts) + if err != nil { + return false, err + } + exists := new(bool) + if err := rocketNodeManager.Call(opts, exists, "getNodeExists", nodeAddress); err != nil { + return false, fmt.Errorf("error getting node %s exists status: %w", nodeAddress.Hex(), err) + } + return *exists, nil +} + +// Get a node's timezone location +func GetNodeTimezoneLocation(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (string, error) { + rocketNodeManager, err := getRocketNodeManager(rp, opts) + if err != nil { + return "", err + } + timezoneLocation := new(string) + if err := rocketNodeManager.Call(opts, timezoneLocation, "getNodeTimezoneLocation", nodeAddress); err != nil { + return "", fmt.Errorf("error getting node %s timezone location: %w", nodeAddress.Hex(), err) + } + return strings.Sanitize(*timezoneLocation), nil +} + +// Estimate the gas of RegisterNode +func EstimateRegisterNodeGas(rp *rocketpool.RocketPool, timezoneLocation string, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketNodeManager, err := getRocketNodeManager(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + _, err = time.LoadLocation(timezoneLocation) + if err != nil { + return rocketpool.GasInfo{}, fmt.Errorf("error verifying timezone [%s]: %w", timezoneLocation, err) + } + return rocketNodeManager.GetTransactionGasInfo(opts, "registerNode", timezoneLocation) +} + +// Register a node +func RegisterNode(rp *rocketpool.RocketPool, timezoneLocation string, opts *bind.TransactOpts) (common.Hash, error) { + rocketNodeManager, err := getRocketNodeManager(rp, nil) + if err != nil { + return common.Hash{}, err + } + _, err = time.LoadLocation(timezoneLocation) + if err != nil { + return common.Hash{}, fmt.Errorf("error verifying timezone [%s]: %w", timezoneLocation, err) + } + tx, err := rocketNodeManager.Transact(opts, "registerNode", timezoneLocation) + if err != nil { + return common.Hash{}, fmt.Errorf("error registering node: %w", err) + } + return tx.Hash(), nil +} + +// Estimate the gas of SetTimezoneLocation +func EstimateSetTimezoneLocationGas(rp *rocketpool.RocketPool, timezoneLocation string, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketNodeManager, err := getRocketNodeManager(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + _, err = time.LoadLocation(timezoneLocation) + if err != nil { + return rocketpool.GasInfo{}, fmt.Errorf("error verifying timezone [%s]: %w", timezoneLocation, err) + } + return rocketNodeManager.GetTransactionGasInfo(opts, "setTimezoneLocation", timezoneLocation) +} + +// Set a node's timezone location +func SetTimezoneLocation(rp *rocketpool.RocketPool, timezoneLocation string, opts *bind.TransactOpts) (common.Hash, error) { + rocketNodeManager, err := getRocketNodeManager(rp, nil) + if err != nil { + return common.Hash{}, err + } + _, err = time.LoadLocation(timezoneLocation) + if err != nil { + return common.Hash{}, fmt.Errorf("error verifying timezone [%s]: %w", timezoneLocation, err) + } + tx, err := rocketNodeManager.Transact(opts, "setTimezoneLocation", timezoneLocation) + if err != nil { + return common.Hash{}, fmt.Errorf("error setting node timezone location: %w", err) + } + return tx.Hash(), nil +} + +// Get the network ID for a node's rewards +func GetRewardNetwork(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (uint64, error) { + rocketNodeManager, err := getRocketNodeManager(rp, opts) + if err != nil { + return 0, err + } + rewardNetwork := new(*big.Int) + if err := rocketNodeManager.Call(opts, rewardNetwork, "getRewardNetwork", nodeAddress); err != nil { + return 0, fmt.Errorf("error getting node %s reward network: %w", nodeAddress.Hex(), err) + } + return (*rewardNetwork).Uint64(), nil +} + +// Get the network ID for a node's rewards +func GetRewardNetworkRaw(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (*big.Int, error) { + rocketNodeManager, err := getRocketNodeManager(rp, opts) + if err != nil { + return nil, err + } + rewardNetwork := new(*big.Int) + if err := rocketNodeManager.Call(opts, rewardNetwork, "getRewardNetwork", nodeAddress); err != nil { + return nil, fmt.Errorf("error getting node %s reward network: %w", nodeAddress.Hex(), err) + } + return *rewardNetwork, nil +} + +// Check if a node's fee distributor has been initialized yet +func GetFeeDistributorInitialized(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (bool, error) { + rocketNodeManager, err := getRocketNodeManager(rp, opts) + if err != nil { + return false, err + } + isInitialized := new(bool) + if err := rocketNodeManager.Call(opts, isInitialized, "getFeeDistributorInitialised", nodeAddress); err != nil { + return false, fmt.Errorf("error checking if node %s's fee distributor is initialized: %w", nodeAddress.Hex(), err) + } + return *isInitialized, nil +} + +// Estimate the gas for creating the fee distributor contract for a node +func EstimateInitializeFeeDistributorGas(rp *rocketpool.RocketPool, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketNodeManager, err := getRocketNodeManager(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketNodeManager.GetTransactionGasInfo(opts, "initialiseFeeDistributor") +} + +// Create the fee distributor contract for a node +func InitializeFeeDistributor(rp *rocketpool.RocketPool, opts *bind.TransactOpts) (common.Hash, error) { + rocketNodeManager, err := getRocketNodeManager(rp, nil) + if err != nil { + return common.Hash{}, err + } + tx, err := rocketNodeManager.Transact(opts, "initialiseFeeDistributor") + if err != nil { + return common.Hash{}, fmt.Errorf("error initializing fee distributor: %w", err) + } + return tx.Hash(), nil +} + +// Get a node's average minipool fee +func GetNodeAverageFee(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (float64, error) { + rocketNodeManager, err := getRocketNodeManager(rp, opts) + if err != nil { + return 0, err + } + avgFee := new(*big.Int) + if err := rocketNodeManager.Call(opts, avgFee, "getAverageNodeFee", nodeAddress); err != nil { + return 0, fmt.Errorf("error getting node %s average fee: %w", nodeAddress.Hex(), err) + } + return eth.WeiToEth(*avgFee), nil +} + +// Get a node's average minipool fee +func GetNodeAverageFeeRaw(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (*big.Int, error) { + rocketNodeManager, err := getRocketNodeManager(rp, opts) + if err != nil { + return nil, err + } + avgFee := new(*big.Int) + if err := rocketNodeManager.Call(opts, avgFee, "getAverageNodeFee", nodeAddress); err != nil { + return nil, fmt.Errorf("error getting node %s average fee: %w", nodeAddress.Hex(), err) + } + return *avgFee, nil +} + +// Get the time that the user registered as a claimer +func GetNodeRegistrationTime(rp *rocketpool.RocketPool, address common.Address, opts *bind.CallOpts) (time.Time, error) { + rocketNodeManager, err := getRocketNodeManager(rp, opts) + if err != nil { + return time.Time{}, err + } + registrationTime := new(*big.Int) + if err := rocketNodeManager.Call(opts, registrationTime, "getNodeRegistrationTime", address); err != nil { + return time.Time{}, fmt.Errorf("error getting registration time for %s: %w", address.Hex(), err) + } + return time.Unix((*registrationTime).Int64(), 0), nil +} + +// Get the time that the user registered as a claimer +func GetNodeRegistrationTimeRaw(rp *rocketpool.RocketPool, address common.Address, opts *bind.CallOpts) (*big.Int, error) { + rocketNodeManager, err := getRocketNodeManager(rp, opts) + if err != nil { + return nil, err + } + registrationTime := new(*big.Int) + if err := rocketNodeManager.Call(opts, registrationTime, "getNodeRegistrationTime", address); err != nil { + return nil, fmt.Errorf("error getting registration time for %s: %w", address.Hex(), err) + } + return *registrationTime, nil +} + +// Get the smoothing pool opt-in status of a node +func GetSmoothingPoolRegistrationState(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (bool, error) { + rocketNodeManager, err := getRocketNodeManager(rp, opts) + if err != nil { + return false, err + } + state := new(bool) + if err := rocketNodeManager.Call(opts, state, "getSmoothingPoolRegistrationState", nodeAddress); err != nil { + return false, fmt.Errorf("error getting node %s smoothing pool registration status: %w", nodeAddress.Hex(), err) + } + return *state, nil +} + +// Get the time of the previous smoothing pool opt-in / opt-out +func GetSmoothingPoolRegistrationChanged(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (time.Time, error) { + rocketNodeManager, err := getRocketNodeManager(rp, opts) + if err != nil { + return time.Time{}, err + } + timestamp := new(*big.Int) + if err := rocketNodeManager.Call(opts, timestamp, "getSmoothingPoolRegistrationChanged", nodeAddress); err != nil { + return time.Time{}, fmt.Errorf("error getting node %s's last smoothing pool registration change time: %w", nodeAddress.Hex(), err) + } + return time.Unix((*timestamp).Int64(), 0), nil +} + +// Get the time of the previous smoothing pool opt-in / opt-out +func GetSmoothingPoolRegistrationChangedRaw(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (*big.Int, error) { + rocketNodeManager, err := getRocketNodeManager(rp, opts) + if err != nil { + return nil, err + } + timestamp := new(*big.Int) + if err := rocketNodeManager.Call(opts, timestamp, "getSmoothingPoolRegistrationChanged", nodeAddress); err != nil { + return nil, fmt.Errorf("error getting node %s's last smoothing pool registration change time: %w", nodeAddress.Hex(), err) + } + return *timestamp, nil +} + +// Estimate the gas for opting into / out of the smoothing pool +func EstimateSetSmoothingPoolRegistrationStateGas(rp *rocketpool.RocketPool, optIn bool, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketNodeManager, err := getRocketNodeManager(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketNodeManager.GetTransactionGasInfo(opts, "setSmoothingPoolRegistrationState", optIn) +} + +// Opt into / out of the smoothing pool +func SetSmoothingPoolRegistrationState(rp *rocketpool.RocketPool, optIn bool, opts *bind.TransactOpts) (common.Hash, error) { + rocketNodeManager, err := getRocketNodeManager(rp, nil) + if err != nil { + return common.Hash{}, err + } + tx, err := rocketNodeManager.Transact(opts, "setSmoothingPoolRegistrationState", optIn) + if err != nil { + return common.Hash{}, fmt.Errorf("error setting smoothing pool registration state: %w", err) + } + return tx.Hash(), nil +} + +// Get the number of nodes in the Smoothing Pool +func GetSmoothingPoolRegisteredNodeCount(rp *rocketpool.RocketPool, opts *bind.CallOpts) (uint64, error) { + rocketNodeManager, err := getRocketNodeManager(rp, opts) + if err != nil { + return 0, err + } + + // Get the number of nodes + nodeCount, err := GetNodeCount(rp, opts) + if err != nil { + return 0, err + } + + iterations := uint64(math.Ceil(float64(nodeCount) / float64(SmoothingPoolCountBatchSize))) + iterationCounts := make([]*big.Int, iterations) + + // Load addresses + var wg errgroup.Group + for i := uint64(0); i < iterations; i++ { + i := i + offset := i * SmoothingPoolCountBatchSize + limit := SmoothingPoolCountBatchSize + if nodeCount-offset < SmoothingPoolCountBatchSize { + limit = nodeCount - offset + } + wg.Go(func() error { + count := new(*big.Int) + err := rocketNodeManager.Call(opts, count, "getSmoothingPoolRegisteredNodeCount", big.NewInt(int64(offset)), big.NewInt(int64(limit))) + if err != nil { + return fmt.Errorf("error getting smoothing pool opt-in count for batch starting at %d: %w", offset, err) + } + + iterationCounts[i] = *count + return nil + }) + } + + if err := wg.Wait(); err != nil { + return 0, err + } + + total := uint64(0) + for _, count := range iterationCounts { + total += count.Uint64() + } + + return total, nil + +} + +// Check if the RPL-specific withdrawal address has been set +func GetNodeRPLWithdrawalAddressIsSet(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (bool, error) { + rocketNodeManager, err := getRocketNodeManager(rp, opts) + if err != nil { + return false, err + } + value := new(bool) + if err := rocketNodeManager.Call(opts, value, "getNodeRPLWithdrawalAddressIsSet", nodeAddress); err != nil { + return false, fmt.Errorf("error getting node %s's RPL withdrawal address status: %w", nodeAddress.Hex(), err) + } + return *value, nil +} + +// Get the RPL-specific withdrawal address +func GetNodeRPLWithdrawalAddress(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (common.Address, error) { + rocketNodeManager, err := getRocketNodeManager(rp, opts) + if err != nil { + return common.Address{}, err + } + value := new(common.Address) + if err := rocketNodeManager.Call(opts, value, "getNodeRPLWithdrawalAddress", nodeAddress); err != nil { + return common.Address{}, fmt.Errorf("error getting node %s's RPL withdrawal address: %w", nodeAddress.Hex(), err) + } + return *value, nil +} + +// Get the pending RPL-specific withdrawal address +func GetNodePendingRPLWithdrawalAddress(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (common.Address, error) { + rocketNodeManager, err := getRocketNodeManager(rp, opts) + if err != nil { + return common.Address{}, err + } + value := new(common.Address) + if err := rocketNodeManager.Call(opts, value, "getNodePendingRPLWithdrawalAddress", nodeAddress); err != nil { + return common.Address{}, fmt.Errorf("error getting node %s's pending RPL withdrawal address: %w", nodeAddress.Hex(), err) + } + return *value, nil +} + +// Estimate the gas for setting the RPL-specific withdrawal address +func EstimateSetRPLWithdrawalAddressGas(rp *rocketpool.RocketPool, nodeAddress common.Address, withdrawalAddress common.Address, confirm bool, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketNodeManager, err := getRocketNodeManager(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketNodeManager.GetTransactionGasInfo(opts, "setRPLWithdrawalAddress", nodeAddress, withdrawalAddress, confirm) +} + +// Set the RPL-specific withdrawal address +func SetRPLWithdrawalAddress(rp *rocketpool.RocketPool, nodeAddress common.Address, withdrawalAddress common.Address, confirm bool, opts *bind.TransactOpts) (common.Hash, error) { + rocketNodeManager, err := getRocketNodeManager(rp, nil) + if err != nil { + return common.Hash{}, err + } + tx, err := rocketNodeManager.Transact(opts, "setRPLWithdrawalAddress", nodeAddress, withdrawalAddress, confirm) + if err != nil { + return common.Hash{}, fmt.Errorf("error setting RPL withdrawal address: %w", err) + } + return tx.Hash(), nil +} + +// Estimate the gas for confirming the RPL-specific withdrawal address +func EstimateConfirmRPLWithdrawalAddressGas(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketNodeManager, err := getRocketNodeManager(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketNodeManager.GetTransactionGasInfo(opts, "confirmRPLWithdrawalAddress", nodeAddress) +} + +// Confirm the RPL-specific withdrawal address +func ConfirmRPLWithdrawalAddress(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.TransactOpts) (common.Hash, error) { + rocketNodeManager, err := getRocketNodeManager(rp, nil) + if err != nil { + return common.Hash{}, err + } + tx, err := rocketNodeManager.Transact(opts, "confirmRPLWithdrawalAddress", nodeAddress) + if err != nil { + return common.Hash{}, fmt.Errorf("error confirming RPL withdrawal address: %w", err) + } + return tx.Hash(), nil +} + +func EstimateDeployMegapool(rp *rocketpool.RocketPool, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketNodeManager, err := getRocketNodeManager(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketNodeManager.GetTransactionGasInfo(opts, "deployMegapool") +} + +// Deploys a Megapool contract +func DeployMegapool(rp *rocketpool.RocketPool, opts *bind.TransactOpts) (common.Hash, error) { + rocketNodeManager, err := getRocketNodeManager(rp, nil) + if err != nil { + return common.Hash{}, nil + } + + tx, err := rocketNodeManager.Transact(opts, "deployMegapool") + if err != nil { + return common.Hash{}, fmt.Errorf("error calling deployMegapool: %w", err) + } + return tx.Hash(), nil +} + +// Get express ticket count for a node +func GetExpressTicketCount(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (uint64, error) { + rocketMegapoolFactory, err := getRocketNodeManager(rp, opts) + if err != nil { + return 0, err + } + expressTicketCount := new(*big.Int) + if err := rocketMegapoolFactory.Call(opts, expressTicketCount, "getExpressTicketCount", nodeAddress); err != nil { + return 0, fmt.Errorf("error getting express ticket count for node %s: %w", nodeAddress, err) + } + return (*expressTicketCount).Uint64(), nil +} + +// Consume an express ticket for the given node operator +func UseExpressTicket(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.TransactOpts) (common.Hash, error) { + rocketNodeManager, err := getRocketNodeManager(rp, nil) + if err != nil { + return common.Hash{}, nil + } + + tx, err := rocketNodeManager.Transact(opts, "useExpressTicket") + if err != nil { + return common.Hash{}, fmt.Errorf("error calling useExpressticket: %w", err) + } + return tx.Hash(), nil +} + +// Get contracts +var rocketNodeManagerLock sync.Mutex + +func getRocketNodeManager(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*rocketpool.Contract, error) { + rocketNodeManagerLock.Lock() + defer rocketNodeManagerLock.Unlock() + return rp.GetContract("rocketNodeManager", opts) +} + +var rocketNetworkPricesLock sync.Mutex + +func getRocketNetworkPrices(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*rocketpool.Contract, error) { + rocketNetworkPricesLock.Lock() + defer rocketNetworkPricesLock.Unlock() + return rp.GetContract("rocketNetworkPrices", opts) +} + +var rocketNetworkBalancesLock sync.Mutex + +func getRocketNetworkBalances(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*rocketpool.Contract, error) { + rocketNetworkBalancesLock.Lock() + defer rocketNetworkBalancesLock.Unlock() + return rp.GetContract("rocketNetworkBalances", opts) +} + +var rocketDAONodeTrustedActionsLock sync.Mutex + +func getRocketDAONodeTrustedActions(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*rocketpool.Contract, error) { + rocketDAONodeTrustedActionsLock.Lock() + defer rocketDAONodeTrustedActionsLock.Unlock() + return rp.GetContract("rocketDAONodeTrustedActions", opts) +} diff --git a/bindings/node/staking.go b/bindings/node/staking.go new file mode 100644 index 000000000..4c0f21f21 --- /dev/null +++ b/bindings/node/staking.go @@ -0,0 +1,296 @@ +package node + +import ( + "fmt" + "math/big" + "sync" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + + "github.com/rocket-pool/smartnode/bindings/rocketpool" +) + +// Get the version of the Node Staking contract +func GetNodeStakingVersion(rp *rocketpool.RocketPool, opts *bind.CallOpts) (uint8, error) { + rocketNodeStaking, err := getRocketNodeStaking(rp, opts) + if err != nil { + return 0, err + } + return rocketpool.GetContractVersion(rp, *rocketNodeStaking.Address, opts) +} + +// Get the total RPL staked in the network +func GetTotalRPLStake(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*big.Int, error) { + rocketNodeStaking, err := getRocketNodeStaking(rp, opts) + if err != nil { + return nil, err + } + totalRplStake := new(*big.Int) + if err := rocketNodeStaking.Call(opts, totalRplStake, "getTotalRPLStake"); err != nil { + return nil, fmt.Errorf("error getting total network RPL stake: %w", err) + } + return *totalRplStake, nil +} + +// Get a node's RPL stake +func GetNodeRPLStake(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (*big.Int, error) { + rocketNodeStaking, err := getRocketNodeStaking(rp, opts) + if err != nil { + return nil, err + } + nodeRplStake := new(*big.Int) + if err := rocketNodeStaking.Call(opts, nodeRplStake, "getNodeRPLStake", nodeAddress); err != nil { + return nil, fmt.Errorf("error getting total node RPL stake: %w", err) + } + return *nodeRplStake, nil +} + +// Get a node's effective RPL stake +func GetNodeEffectiveRPLStake(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (*big.Int, error) { + rocketNodeStaking, err := getRocketNodeStaking(rp, opts) + if err != nil { + return nil, err + } + nodeEffectiveRplStakeWrapper := new(*big.Int) + if err := rocketNodeStaking.Call(opts, nodeEffectiveRplStakeWrapper, "getNodeEffectiveRPLStake", nodeAddress); err != nil { + return nil, fmt.Errorf("error getting effective node RPL stake: %w", err) + } + + minimumStake, err := GetNodeMinimumRPLStake(rp, nodeAddress, opts) + if err != nil { + return nil, fmt.Errorf("error getting minimum node RPL stake to verify effective stake: %w", err) + } + + nodeEffectiveRplStake := *nodeEffectiveRplStakeWrapper + if nodeEffectiveRplStake.Cmp(minimumStake) == -1 { + // Effective stake should be zero if it's less than the minimum RPL stake + return big.NewInt(0), nil + } + + return nodeEffectiveRplStake, nil +} + +// Get a node's minimum RPL stake to collateralize their minipools +func GetNodeMinimumRPLStake(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (*big.Int, error) { + rocketNodeStaking, err := getRocketNodeStaking(rp, opts) + if err != nil { + return nil, err + } + nodeMinimumRplStake := new(*big.Int) + if err := rocketNodeStaking.Call(opts, nodeMinimumRplStake, "getNodeMinimumRPLStake", nodeAddress); err != nil { + return nil, fmt.Errorf("error getting minimum node RPL stake: %w", err) + } + return *nodeMinimumRplStake, nil +} + +// Get a node's maximum RPL stake to collateralize their minipools +func GetNodeMaximumRPLStake(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (*big.Int, error) { + rocketNodeStaking, err := getRocketNodeStaking(rp, opts) + if err != nil { + return nil, err + } + nodeMaximumRplStake := new(*big.Int) + if err := rocketNodeStaking.Call(opts, nodeMaximumRplStake, "getNodeMaximumRPLStake", nodeAddress); err != nil { + return nil, fmt.Errorf("error getting maximum node RPL stake: %w", err) + } + return *nodeMaximumRplStake, nil +} + +// Get the time a node last staked RPL +func GetNodeRPLStakedTime(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (uint64, error) { + rocketNodeStaking, err := getRocketNodeStaking(rp, opts) + if err != nil { + return 0, err + } + nodeRplStakedTime := new(*big.Int) + if err := rocketNodeStaking.Call(opts, nodeRplStakedTime, "getNodeRPLStakedTime", nodeAddress); err != nil { + return 0, fmt.Errorf("error getting node RPL staked time: %w", err) + } + return (*nodeRplStakedTime).Uint64(), nil +} + +// Get the amount of ETH the node has borrowed from the deposit pool to create its minipools +func GetNodeEthMatched(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (*big.Int, error) { + rocketNodeStaking, err := getRocketNodeStaking(rp, opts) + if err != nil { + return nil, err + } + nodeEthMatched := new(*big.Int) + if err := rocketNodeStaking.Call(opts, nodeEthMatched, "getNodeETHMatched", nodeAddress); err != nil { + return nil, fmt.Errorf("error getting node ETH matched: %w", err) + } + return *nodeEthMatched, nil +} + +// Get the amount of ETH the node can borrow from the deposit pool to create its minipools +func GetNodeEthMatchedLimit(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (*big.Int, error) { + rocketNodeStaking, err := getRocketNodeStaking(rp, opts) + if err != nil { + return nil, err + } + nodeEthMatchedLimit := new(*big.Int) + if err := rocketNodeStaking.Call(opts, nodeEthMatchedLimit, "getNodeETHMatchedLimit", nodeAddress); err != nil { + return nil, fmt.Errorf("error getting node ETH matched limit: %w", err) + } + return *nodeEthMatchedLimit, nil +} + +// Estimate the gas of Stake +func EstimateStakeGas(rp *rocketpool.RocketPool, rplAmount *big.Int, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketNodeStaking, err := getRocketNodeStaking(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketNodeStaking.GetTransactionGasInfo(opts, "stakeRPL", rplAmount) +} + +// Stake RPL +func StakeRPL(rp *rocketpool.RocketPool, rplAmount *big.Int, opts *bind.TransactOpts) (common.Hash, error) { + rocketNodeStaking, err := getRocketNodeStaking(rp, nil) + if err != nil { + return common.Hash{}, err + } + tx, err := rocketNodeStaking.Transact(opts, "stakeRPL", rplAmount) + if err != nil { + return common.Hash{}, fmt.Errorf("error staking RPL: %w", err) + } + return tx.Hash(), nil +} + +// Estimate the gas of Burn RPL +func EstimateBurnRpl(rp *rocketpool.RocketPool, from common.Address, rplAmount *big.Int, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketNodeStaking, err := getRocketNodeStaking(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketNodeStaking.GetTransactionGasInfo(opts, "burnRPL", from, rplAmount) +} + +// Burn RPL +func BurnRPL(rp *rocketpool.RocketPool, from common.Address, rplAmount *big.Int, opts *bind.TransactOpts) (common.Hash, error) { + rocketNodeStaking, err := getRocketNodeStaking(rp, nil) + if err != nil { + return common.Hash{}, err + } + tx, err := rocketNodeStaking.Transact(opts, "burnRPL", from, rplAmount) + if err != nil { + return common.Hash{}, fmt.Errorf("error burning RPL: %w", err) + } + return tx.Hash(), nil + +} + +// Estimate the gas of set RPL locking allowed +func EstimateSetRPLLockingAllowedGas(rp *rocketpool.RocketPool, caller common.Address, allowed bool, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketNodeStaking, err := getRocketNodeStaking(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketNodeStaking.GetTransactionGasInfo(opts, "setRPLLockingAllowed", caller, allowed) +} + +// Set RPL locking allowed +func SetRPLLockingAllowed(rp *rocketpool.RocketPool, caller common.Address, allowed bool, opts *bind.TransactOpts) (common.Hash, error) { + rocketNodeStaking, err := getRocketNodeStaking(rp, nil) + if err != nil { + return common.Hash{}, err + } + tx, err := rocketNodeStaking.Transact(opts, "setRPLLockingAllowed", caller, allowed) + if err != nil { + return common.Hash{}, fmt.Errorf("error setting RPL locking allowed: %w", err) + } + return tx.Hash(), nil +} + +// Get RPL locking allowed state for a node +func GetRPLLockedAllowed(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (bool, error) { + rocketNodeStaking, err := getRocketNodeStaking(rp, opts) + if err != nil { + return false, err + } + value := new(bool) + if err := rocketNodeStaking.Call(opts, value, "getRPLLockingAllowed", nodeAddress); err != nil { + return false, fmt.Errorf("error getting node RPL locked: %w", err) + } + return *value, nil +} + +// Estimate the gas of set stake RPL for allowed +func EstimateSetStakeRPLForAllowedGas(rp *rocketpool.RocketPool, caller common.Address, allowed bool, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketNodeStaking, err := getRocketNodeStaking(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketNodeStaking.GetTransactionGasInfo(opts, "setStakeRPLForAllowed", caller, allowed) +} + +// Set stake RPL for allowed +func SetStakeRPLForAllowed(rp *rocketpool.RocketPool, caller common.Address, allowed bool, opts *bind.TransactOpts) (common.Hash, error) { + rocketNodeStaking, err := getRocketNodeStaking(rp, nil) + if err != nil { + return common.Hash{}, err + } + tx, err := rocketNodeStaking.Transact(opts, "setStakeRPLForAllowed", caller, allowed) + if err != nil { + return common.Hash{}, fmt.Errorf("error setting stake RPL for allowed: %w", err) + } + return tx.Hash(), nil +} + +// Estimate the gas of WithdrawRPL +func EstimateWithdrawRPLGas(rp *rocketpool.RocketPool, nodeAddress common.Address, rplAmount *big.Int, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketNodeStaking, err := getRocketNodeStaking(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketNodeStaking.GetTransactionGasInfo(opts, "withdrawRPL", nodeAddress, rplAmount) +} + +// Withdraw staked RPL +func WithdrawRPL(rp *rocketpool.RocketPool, nodeAddress common.Address, rplAmount *big.Int, opts *bind.TransactOpts) (common.Hash, error) { + rocketNodeStaking, err := getRocketNodeStaking(rp, nil) + if err != nil { + return common.Hash{}, err + } + tx, err := rocketNodeStaking.Transact(opts, "withdrawRPL", nodeAddress, rplAmount) + if err != nil { + return common.Hash{}, fmt.Errorf("error withdrawing staked RPL: %w", err) + } + return tx.Hash(), nil +} + +// Calculate total effective RPL stake +func CalculateTotalEffectiveRPLStake(rp *rocketpool.RocketPool, offset, limit, rplPrice *big.Int, opts *bind.CallOpts) (*big.Int, error) { + rocketNodeStaking, err := getRocketNodeStaking(rp, opts) + if err != nil { + return nil, err + } + totalEffectiveRplStake := new(*big.Int) + if err := rocketNodeStaking.Call(opts, totalEffectiveRplStake, "calculateTotalEffectiveRPLStake", offset, limit, rplPrice); err != nil { + return nil, fmt.Errorf("error getting total effective RPL stake: %w", err) + } + return *totalEffectiveRplStake, nil +} + +// Get the amount of RPL locked as part of active PDAO proposals or challenges +func GetNodeRPLLocked(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (*big.Int, error) { + rocketNodeStaking, err := getRocketNodeStaking(rp, opts) + if err != nil { + return nil, err + } + value := new(*big.Int) + if err := rocketNodeStaking.Call(opts, value, "getNodeRPLLocked", nodeAddress); err != nil { + return nil, fmt.Errorf("error getting node RPL locked: %w", err) + } + return *value, nil +} + +// Get contracts +var rocketNodeStakingLock sync.Mutex + +func getRocketNodeStaking(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*rocketpool.Contract, error) { + rocketNodeStakingLock.Lock() + defer rocketNodeStakingLock.Unlock() + return rp.GetContract("rocketNodeStaking", opts) +} diff --git a/bindings/rewards/distributor-mainnet.go b/bindings/rewards/distributor-mainnet.go new file mode 100644 index 000000000..ff0e4c829 --- /dev/null +++ b/bindings/rewards/distributor-mainnet.go @@ -0,0 +1,90 @@ +package rewards + +import ( + "fmt" + "math/big" + "sync" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/rocket-pool/smartnode/bindings/rocketpool" +) + +// Check if the given node has already claimed rewards for the given interval +func IsClaimed(rp *rocketpool.RocketPool, index *big.Int, claimerAddress common.Address, opts *bind.CallOpts) (bool, error) { + rocketDistributorMainnet, err := getRocketDistributorMainnet(rp, opts) + if err != nil { + return false, err + } + isClaimed := new(bool) + if err := rocketDistributorMainnet.Call(opts, isClaimed, "isClaimed", index, claimerAddress); err != nil { + return false, fmt.Errorf("error getting rewards claim status for interval %s, node %s: %w", index.String(), claimerAddress.Hex(), err) + } + return *isClaimed, nil +} + +// Get the Merkle root for an interval +func MerkleRoots(rp *rocketpool.RocketPool, interval *big.Int, opts *bind.CallOpts) ([]byte, error) { + rocketDistributorMainnet, err := getRocketDistributorMainnet(rp, opts) + if err != nil { + return nil, err + } + bytes := new([32]byte) + if err := rocketDistributorMainnet.Call(opts, bytes, "merkleRoots", interval); err != nil { + return nil, fmt.Errorf("error getting Merkle root for interval %s: %w", interval.String(), err) + } + return (*bytes)[:], nil +} + +// Estimate claim rewards gas +func EstimateClaimGas(rp *rocketpool.RocketPool, address common.Address, indices []*big.Int, amountRPL []*big.Int, amountETH []*big.Int, merkleProofs [][]common.Hash, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketDistributorMainnet, err := getRocketDistributorMainnet(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketDistributorMainnet.GetTransactionGasInfo(opts, "claim", address, indices, amountRPL, amountETH, merkleProofs) +} + +// Claim rewards +func Claim(rp *rocketpool.RocketPool, address common.Address, indices []*big.Int, amountRPL []*big.Int, amountETH []*big.Int, merkleProofs [][]common.Hash, opts *bind.TransactOpts) (common.Hash, error) { + rocketDistributorMainnet, err := getRocketDistributorMainnet(rp, nil) + if err != nil { + return common.Hash{}, err + } + tx, err := rocketDistributorMainnet.Transact(opts, "claim", address, indices, amountRPL, amountETH, merkleProofs) + if err != nil { + return common.Hash{}, fmt.Errorf("error claiming rewards: %w", err) + } + return tx.Hash(), nil +} + +// Estimate claim and restake rewards gas +func EstimateClaimAndStakeGas(rp *rocketpool.RocketPool, address common.Address, indices []*big.Int, amountRPL []*big.Int, amountETH []*big.Int, merkleProofs [][]common.Hash, stakeAmount *big.Int, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketDistributorMainnet, err := getRocketDistributorMainnet(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketDistributorMainnet.GetTransactionGasInfo(opts, "claimAndStake", address, indices, amountRPL, amountETH, merkleProofs, stakeAmount) +} + +// Claim and restake rewards +func ClaimAndStake(rp *rocketpool.RocketPool, address common.Address, indices []*big.Int, amountRPL []*big.Int, amountETH []*big.Int, merkleProofs [][]common.Hash, stakeAmount *big.Int, opts *bind.TransactOpts) (common.Hash, error) { + rocketDistributorMainnet, err := getRocketDistributorMainnet(rp, nil) + if err != nil { + return common.Hash{}, err + } + tx, err := rocketDistributorMainnet.Transact(opts, "claimAndStake", address, indices, amountRPL, amountETH, merkleProofs, stakeAmount) + if err != nil { + return common.Hash{}, fmt.Errorf("error claiming rewards: %w", err) + } + return tx.Hash(), nil +} + +// Get contracts +var rocketDistributorMainnetLock sync.Mutex + +func getRocketDistributorMainnet(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*rocketpool.Contract, error) { + rocketDistributorMainnetLock.Lock() + defer rocketDistributorMainnetLock.Unlock() + return rp.GetContract("rocketMerkleDistributorMainnet", opts) +} diff --git a/bindings/rewards/rewards.go b/bindings/rewards/rewards.go new file mode 100644 index 000000000..e4d9e9b32 --- /dev/null +++ b/bindings/rewards/rewards.go @@ -0,0 +1,327 @@ +package rewards + +import ( + "fmt" + "math/big" + "sync" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/utils/eth" +) + +const ( + rewardsSnapshotSubmittedNodeKey string = "rewards.snapshot.submitted.node.key" +) + +// Info for a rewards snapshot event +type RewardsEvent struct { + Index *big.Int + ExecutionBlock *big.Int + ConsensusBlock *big.Int + MerkleRoot common.Hash + MerkleTreeCID string + IntervalsPassed *big.Int + TreasuryRPL *big.Int + TrustedNodeRPL []*big.Int + NodeRPL []*big.Int + NodeETH []*big.Int + UserETH *big.Int + IntervalStartTime time.Time + IntervalEndTime time.Time + SubmissionTime time.Time +} + +// Struct for submitting the rewards for a checkpoint +type RewardSubmission struct { + RewardIndex *big.Int `json:"rewardIndex"` + ExecutionBlock *big.Int `json:"executionBlock"` + ConsensusBlock *big.Int `json:"consensusBlock"` + MerkleRoot [32]byte `json:"merkleRoot"` + MerkleTreeCID string `json:"merkleTreeCID"` + IntervalsPassed *big.Int `json:"intervalsPassed"` + TreasuryRPL *big.Int `json:"treasuryRPL"` + TrustedNodeRPL []*big.Int `json:"trustedNodeRPL"` + NodeRPL []*big.Int `json:"nodeRPL"` + NodeETH []*big.Int `json:"nodeETH"` + UserETH *big.Int `json:"userETH"` +} + +// Internal struct - this is the structure of what gets returned by the RewardSnapshot event +type rewardSnapshot struct { + RewardIndex *big.Int `json:"rewardIndex"` + Submission RewardSubmission `json:"submission"` + IntervalStartTime *big.Int `json:"intervalStartTime"` + IntervalEndTime *big.Int `json:"intervalEndTime"` + Time *big.Int `json:"time"` +} + +// Get the timestamp that the current rewards interval started +func GetClaimIntervalTimeStart(rp *rocketpool.RocketPool, opts *bind.CallOpts) (time.Time, error) { + rocketRewardsPool, err := getRocketRewardsPool(rp, opts) + if err != nil { + return time.Time{}, err + } + unixTime := new(*big.Int) + if err := rocketRewardsPool.Call(opts, unixTime, "getClaimIntervalTimeStart"); err != nil { + return time.Time{}, fmt.Errorf("error getting claim interval time start: %w", err) + } + return time.Unix((*unixTime).Int64(), 0), nil +} + +// Get the number of seconds in a claim interval +func GetClaimIntervalTime(rp *rocketpool.RocketPool, opts *bind.CallOpts) (time.Duration, error) { + rocketRewardsPool, err := getRocketRewardsPool(rp, opts) + if err != nil { + return 0, err + } + unixTime := new(*big.Int) + if err := rocketRewardsPool.Call(opts, unixTime, "getClaimIntervalTime"); err != nil { + return 0, fmt.Errorf("error getting claim interval time: %w", err) + } + return time.Duration((*unixTime).Int64()) * time.Second, nil +} + +// Get the percent of checkpoint rewards that goes to node operators +func GetNodeOperatorRewardsPercent(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*big.Int, error) { + rocketRewardsPool, err := getRocketRewardsPool(rp, opts) + if err != nil { + return nil, err + } + perc := new(*big.Int) + if err := rocketRewardsPool.Call(opts, perc, "getClaimingContractPerc", "rocketClaimNode"); err != nil { + return nil, fmt.Errorf("error getting node operator rewards percent: %w", err) + } + return *perc, nil +} + +// Get the percent of checkpoint rewards that goes to ODAO members +func GetTrustedNodeOperatorRewardsPercent(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*big.Int, error) { + rocketRewardsPool, err := getRocketRewardsPool(rp, opts) + if err != nil { + return nil, err + } + perc := new(*big.Int) + if err := rocketRewardsPool.Call(opts, perc, "getClaimingContractPerc", "rocketClaimTrustedNode"); err != nil { + return nil, fmt.Errorf("error getting trusted node operator rewards percent: %w", err) + } + return *perc, nil +} + +// Get the percent of checkpoint rewards that goes to the PDAO +func GetProtocolDaoRewardsPercent(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*big.Int, error) { + rocketRewardsPool, err := getRocketRewardsPool(rp, opts) + if err != nil { + return nil, err + } + perc := new(*big.Int) + if err := rocketRewardsPool.Call(opts, perc, "getClaimingContractPerc", "rocketClaimDAO"); err != nil { + return nil, fmt.Errorf("error getting protocol DAO rewards percent: %w", err) + } + return *perc, nil +} + +// Get the amount of RPL rewards that will be provided to node operators +func GetPendingRPLRewards(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*big.Int, error) { + rocketRewardsPool, err := getRocketRewardsPool(rp, opts) + if err != nil { + return nil, err + } + rewards := new(*big.Int) + if err := rocketRewardsPool.Call(opts, rewards, "getPendingRPLRewards"); err != nil { + return nil, fmt.Errorf("error getting pending RPL rewards: %w", err) + } + return *rewards, nil +} + +// Get the amount of ETH rewards that will be provided to node operators +func GetPendingETHRewards(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*big.Int, error) { + rocketRewardsPool, err := getRocketRewardsPool(rp, opts) + if err != nil { + return nil, err + } + rewards := new(*big.Int) + if err := rocketRewardsPool.Call(opts, rewards, "getPendingETHRewards"); err != nil { + return nil, fmt.Errorf("error getting pending ETH rewards: %w", err) + } + return *rewards, nil +} + +// Check whether or not the given address has submitted for the given rewards interval +func GetTrustedNodeSubmitted(rp *rocketpool.RocketPool, nodeAddress common.Address, rewardsIndex uint64, opts *bind.CallOpts) (bool, error) { + rocketRewardsPool, err := getRocketRewardsPool(rp, opts) + if err != nil { + return false, err + } + + indexBig := big.NewInt(0).SetUint64(rewardsIndex) + hasSubmitted := new(bool) + if err := rocketRewardsPool.Call(opts, hasSubmitted, "getTrustedNodeSubmitted", nodeAddress, indexBig); err != nil { + return false, fmt.Errorf("error getting trusted node submission status: %w", err) + } + return *hasSubmitted, nil +} + +// Check whether or not the given address has submitted specific rewards info +func GetTrustedNodeSubmittedSpecificRewards(rp *rocketpool.RocketPool, nodeAddress common.Address, submission RewardSubmission, opts *bind.CallOpts) (bool, error) { + // NOTE: this doesn't have a view yet so we have to construct it manually, and RLP + stringTy, _ := abi.NewType("string", "string", nil) + addressTy, _ := abi.NewType("address", "address", nil) + + submissionTy, _ := abi.NewType("tuple", "struct RewardSubmission", []abi.ArgumentMarshaling{ + {Name: "rewardIndex", Type: "uint256"}, + {Name: "executionBlock", Type: "uint256"}, + {Name: "consensusBlock", Type: "uint256"}, + {Name: "merkleRoot", Type: "bytes32"}, + {Name: "merkleTreeCID", Type: "string"}, + {Name: "intervalsPassed", Type: "uint256"}, + {Name: "treasuryRPL", Type: "uint256"}, + {Name: "trustedNodeRPL", Type: "uint256[]"}, + {Name: "nodeRPL", Type: "uint256[]"}, + {Name: "nodeETH", Type: "uint256[]"}, + {Name: "userETH", Type: "uint256"}, + }) + + args := abi.Arguments{ + {Type: stringTy, Name: "key"}, + {Type: addressTy, Name: "trustedNodeAddress"}, + {Type: submissionTy, Name: "submission"}, + } + + bytes, err := args.Pack(rewardsSnapshotSubmittedNodeKey, nodeAddress, &submission) + if err != nil { + return false, fmt.Errorf("error encoding submission data into ABI format: %w", err) + } + + key := crypto.Keccak256Hash(bytes) + result, err := rp.RocketStorage.GetBool(opts, key) + if err != nil { + return false, fmt.Errorf("error checking if trusted node submitted specific rewards: %w", err) + } + return result, nil +} + +// Estimate the gas for submiting a Merkle Tree-based snapshot for a rewards interval +func EstimateSubmitRewardSnapshotGas(rp *rocketpool.RocketPool, submission RewardSubmission, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketRewardsPool, err := getRocketRewardsPool(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketRewardsPool.GetTransactionGasInfo(opts, "submitRewardSnapshot", submission) +} + +// Submit a Merkle Tree-based snapshot for a rewards interval +func SubmitRewardSnapshot(rp *rocketpool.RocketPool, submission RewardSubmission, opts *bind.TransactOpts) (common.Hash, error) { + rocketRewardsPool, err := getRocketRewardsPool(rp, nil) + if err != nil { + return common.Hash{}, err + } + tx, err := rocketRewardsPool.Transact(opts, "submitRewardSnapshot", submission) + if err != nil { + return common.Hash{}, fmt.Errorf("error submitting rewards snapshot: %w", err) + } + return tx.Hash(), nil +} + +// Get the event info for a rewards snapshot using the Atlas getter +func GetRewardsEvent(rp *rocketpool.RocketPool, index uint64, rocketRewardsPoolAddresses []common.Address, opts *bind.CallOpts) (bool, RewardsEvent, error) { + // Get contracts + rocketRewardsPool, err := getRocketRewardsPool(rp, opts) + if err != nil { + return false, RewardsEvent{}, err + } + + // Get the block that the event was emitted on + indexBig := big.NewInt(0).SetUint64(index) + blockWrapper := new(*big.Int) + if err := rocketRewardsPool.Call(opts, blockWrapper, "getClaimIntervalExecutionBlock", indexBig); err != nil { + return false, RewardsEvent{}, fmt.Errorf("error getting the event block for interval %d: %w", index, err) + } + block := *blockWrapper + + // Create the list of addresses to check + currentAddress := *rocketRewardsPool.Address + if rocketRewardsPoolAddresses == nil { + rocketRewardsPoolAddresses = []common.Address{currentAddress} + } else { + found := false + for _, address := range rocketRewardsPoolAddresses { + if address == currentAddress { + found = true + break + } + } + if !found { + rocketRewardsPoolAddresses = append(rocketRewardsPoolAddresses, currentAddress) + } + } + + // Construct a filter query for relevant logs + rewardsSnapshotEvent := rocketRewardsPool.ABI.Events["RewardSnapshot"] + indexBytes := [32]byte{} + indexBig.FillBytes(indexBytes[:]) + addressFilter := rocketRewardsPoolAddresses + topicFilter := [][]common.Hash{{rewardsSnapshotEvent.ID}, {indexBytes}} + + // Get the event logs + logs, err := eth.GetLogs(rp, addressFilter, topicFilter, big.NewInt(1), block, block, nil) + if err != nil { + return false, RewardsEvent{}, err + } + if len(logs) == 0 { + return false, RewardsEvent{}, nil + } + + // Get the log info values + values, err := rewardsSnapshotEvent.Inputs.Unpack(logs[0].Data) + if err != nil { + return false, RewardsEvent{}, fmt.Errorf("error unpacking rewards snapshot event data: %w", err) + } + + // Convert to a native struct + var snapshot rewardSnapshot + err = rewardsSnapshotEvent.Inputs.Copy(&snapshot, values) + if err != nil { + return false, RewardsEvent{}, fmt.Errorf("error converting rewards snapshot event data to struct: %w", err) + } + + // Get the decoded data + submission := snapshot.Submission + eventData := RewardsEvent{ + Index: indexBig, + ExecutionBlock: submission.ExecutionBlock, + ConsensusBlock: submission.ConsensusBlock, + IntervalsPassed: submission.IntervalsPassed, + TreasuryRPL: submission.TreasuryRPL, + TrustedNodeRPL: submission.TrustedNodeRPL, + NodeRPL: submission.NodeRPL, + NodeETH: submission.NodeETH, + UserETH: submission.UserETH, + MerkleRoot: submission.MerkleRoot, + MerkleTreeCID: submission.MerkleTreeCID, + IntervalStartTime: time.Unix(snapshot.IntervalStartTime.Int64(), 0), + IntervalEndTime: time.Unix(snapshot.IntervalEndTime.Int64(), 0), + SubmissionTime: time.Unix(snapshot.Time.Int64(), 0), + } + + // Convert v1.1.0-rc1 events to modern ones + if eventData.UserETH == nil { + eventData.UserETH = big.NewInt(0) + } + + return true, eventData, nil +} + +// Get contracts +var rocketRewardsPoolLock sync.Mutex + +func getRocketRewardsPool(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*rocketpool.Contract, error) { + rocketRewardsPoolLock.Lock() + defer rocketRewardsPoolLock.Unlock() + return rp.GetContract("rocketRewardsPool", opts) +} diff --git a/bindings/rocketpool/abi.go b/bindings/rocketpool/abi.go new file mode 100644 index 000000000..46e17a014 --- /dev/null +++ b/bindings/rocketpool/abi.go @@ -0,0 +1,70 @@ +package rocketpool + +import ( + "bytes" + "compress/zlib" + "encoding/base64" + "fmt" + "sync" + + "github.com/ethereum/go-ethereum/accounts/abi" +) + +var decoderCache sync.Map + +// Decode, decompress and parse a zlib-compressed, base64-encoded ABI +func DecodeAbi(abiEncoded string) (*abi.ABI, error) { + + if cached, ok := decoderCache.Load(abiEncoded); ok { + return cached.(*abi.ABI), nil + } + + // base64 decode + abiCompressed, err := base64.StdEncoding.DecodeString(abiEncoded) + if err != nil { + return nil, fmt.Errorf("error decoding base64 data: %w", err) + } + + // zlib decompress + byteReader := bytes.NewReader(abiCompressed) + zlibReader, err := zlib.NewReader(byteReader) + if err != nil { + return nil, fmt.Errorf("error decompressing zlib data: %w", err) + } + defer func() { + _ = zlibReader.Close() + }() + + // Parse ABI + abiParsed, err := abi.JSON(zlibReader) + if err != nil { + return nil, fmt.Errorf("error parsing JSON: %w", err) + } + + decoderCache.Store(abiEncoded, &abiParsed) + + // Return + return &abiParsed, nil + +} + +// zlib-compress and base64-encode an ABI JSON string +func EncodeAbiStr(abiStr string) (string, error) { + + // zlib compress + var abiCompressed bytes.Buffer + zlibWriter := zlib.NewWriter(&abiCompressed) + if _, err := zlibWriter.Write([]byte(abiStr)); err != nil { + return "", fmt.Errorf("error zlib-compressing ABI string: %w", err) + } + if err := zlibWriter.Flush(); err != nil { + return "", fmt.Errorf("error zlib-compressing ABI string: %w", err) + } + if err := zlibWriter.Close(); err != nil { + return "", fmt.Errorf("error zlib-compressing ABI string: %w", err) + } + + // base64 encode & return + return base64.StdEncoding.EncodeToString(abiCompressed.Bytes()), nil + +} diff --git a/bindings/rocketpool/contract.go b/bindings/rocketpool/contract.go new file mode 100644 index 000000000..598ed4140 --- /dev/null +++ b/bindings/rocketpool/contract.go @@ -0,0 +1,252 @@ +package rocketpool + +import ( + "bytes" + "context" + "encoding/hex" + "errors" + "fmt" + "math/big" + "reflect" + "regexp" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +// Transaction settings +const ( + GasLimitMultiplier float64 = 1.5 + MaxGasLimit uint64 = 30000000 + NethermindRevertRegex string = "Reverted 0x(?P[0-9a-fA-F]+).*" +) + +// Contract type wraps go-ethereum bound contract +type Contract struct { + Contract *bind.BoundContract + Address *common.Address + ABI *abi.ABI + Client ExecutionClient +} + +// Response for gas limits from network and from user request +type GasInfo struct { + EstGasLimit uint64 `json:"estGasLimit"` + SafeGasLimit uint64 `json:"safeGasLimit"` +} + +// Call a contract method +func (c *Contract) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + results := make([]interface{}, 1) + results[0] = result + return c.Contract.Call(opts, &results, method, params...) +} + +// Get Gas Limit for transaction +func (c *Contract) GetTransactionGasInfo(opts *bind.TransactOpts, method string, params ...interface{}) (GasInfo, error) { + + response := GasInfo{} + + // Pack transaction Info + input, err := c.ABI.Pack(method, params...) + if err != nil { + return response, fmt.Errorf("Error getting transaction gas info: Could not encode input data: %w", err) + } + + // Estimate gas limit + estGasLimit, safeGasLimit, err := c.estimateGasLimit(opts, input) + + if err != nil { + return response, fmt.Errorf("Error getting transaction gas info: could not estimate gas limit: %w", err) + } + response.EstGasLimit = estGasLimit + response.SafeGasLimit = safeGasLimit + + return response, err +} + +// Transact on a contract method and wait for a receipt +func (c *Contract) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + + // Estimate gas limit + if opts.GasLimit == 0 { + input, err := c.ABI.Pack(method, params...) + if err != nil { + return nil, fmt.Errorf("error encoding input data: %w", err) + } + _, safeGasLimit, err := c.estimateGasLimit(opts, input) + if err != nil { + return nil, err + } + opts.GasLimit = safeGasLimit + } + + // Send transaction + tx, err := c.Contract.Transact(opts, method, params...) + if err != nil { + return nil, c.normalizeErrorMessage(err) + } + + return tx, nil + +} + +// Get gas limit for a transfer call +func (c *Contract) GetTransferGasInfo(opts *bind.TransactOpts) (GasInfo, error) { + + response := GasInfo{} + + // Estimate gas limit + estGasLimit, safeGasLimit, err := c.estimateGasLimit(opts, []byte{}) + if err != nil { + return response, fmt.Errorf("Error getting transfer gas info: could not estimate gas limit: %w", err) + } + response.EstGasLimit = estGasLimit + response.SafeGasLimit = safeGasLimit + + return response, nil +} + +// Transfer ETH to a contract and wait for a receipt +func (c *Contract) Transfer(opts *bind.TransactOpts) (common.Hash, error) { + + // Estimate gas limit + if opts.GasLimit == 0 { + _, safeGasLimit, err := c.estimateGasLimit(opts, []byte{}) + if err != nil { + return common.Hash{}, err + } + opts.GasLimit = safeGasLimit + } + + // Send transaction + tx, err := c.Contract.Transfer(opts) + if err != nil { + return common.Hash{}, c.normalizeErrorMessage(err) + } + + return tx.Hash(), nil + +} + +// Estimate the expected and safe gas limits for a contract transaction +func (c *Contract) estimateGasLimit(opts *bind.TransactOpts, input []byte) (uint64, uint64, error) { + + // Estimate gas limit + gasLimit, err := c.Client.EstimateGas(context.Background(), ethereum.CallMsg{ + From: opts.From, + To: c.Address, + GasPrice: big.NewInt(0), // use 0 gwei for simulation + Value: opts.Value, + Data: input, + }) + + if err != nil { + return 0, 0, fmt.Errorf("error estimating gas needed: %w", c.normalizeErrorMessage(err)) + } + + // Pad and return gas limit + safeGasLimit := uint64(float64(gasLimit) * GasLimitMultiplier) + if gasLimit > MaxGasLimit { + return 0, 0, fmt.Errorf("estimated gas of %d is greater than the max gas limit of %d", gasLimit, MaxGasLimit) + } + if safeGasLimit > MaxGasLimit { + safeGasLimit = MaxGasLimit + } + return gasLimit, safeGasLimit, nil + +} + +// Wait for a transaction to be mined and get a tx receipt +func (c *Contract) getTransactionReceipt(tx *types.Transaction) (*types.Receipt, error) { + + // Wait for transaction to be mined + txReceipt, err := bind.WaitMined(context.Background(), c.Client, tx) + if err != nil { + return nil, err + } + + // Check transaction status + if txReceipt.Status == 0 { + return txReceipt, errors.New("Transaction failed with status 0") + } + + // Return + return txReceipt, nil + +} + +// Get contract events from a transaction +// eventPrototype must be an event struct type +// Returns a slice of untyped values; assert returned events to event struct type +func (c *Contract) GetTransactionEvents(txReceipt *types.Receipt, eventName string, eventPrototype interface{}) ([]interface{}, error) { + + // Get event type + eventType := reflect.TypeOf(eventPrototype) + if eventType.Kind() != reflect.Struct { + return nil, errors.New("Invalid event type") + } + + // Get ABI event + abiEvent, ok := c.ABI.Events[eventName] + if !ok { + return nil, fmt.Errorf("Event '%s' does not exist on contract", eventName) + } + + // Process transaction receipt logs + events := make([]interface{}, 0) + for _, log := range txReceipt.Logs { + + // Check log address matches contract address + if !bytes.Equal(log.Address.Bytes(), c.Address.Bytes()) { + continue + } + + // Check log first topic matches event ID + if len(log.Topics) == 0 || !bytes.Equal(log.Topics[0].Bytes(), abiEvent.ID.Bytes()) { + continue + } + + // Unpack event + event := reflect.New(eventType) + if err := c.Contract.UnpackLog(event.Interface(), eventName, *log); err != nil { + return nil, fmt.Errorf("error unpacking event data: %w", err) + } + events = append(events, reflect.Indirect(event).Interface()) + + } + + // Return events + return events, nil + +} + +// Normalize error messages so they're all in ASCII format +func (c *Contract) normalizeErrorMessage(err error) error { + if err == nil { + return err + } + + // Get the message in hex format, if it exists + reg := regexp.MustCompile(NethermindRevertRegex) + matches := reg.FindStringSubmatch(err.Error()) + if matches == nil { + return err + } + messageIndex := reg.SubexpIndex("message") + if messageIndex == -1 { + return err + } + message := matches[messageIndex] + + // Convert the hex message to ASCII + bytes, err2 := hex.DecodeString(message) + if err2 != nil { + return err // Return the original error if decoding failed somehow + } + + return fmt.Errorf("Reverted: %s", string(bytes)) +} diff --git a/bindings/rocketpool/ec-interface.go b/bindings/rocketpool/ec-interface.go new file mode 100644 index 000000000..5e44a2377 --- /dev/null +++ b/bindings/rocketpool/ec-interface.go @@ -0,0 +1,105 @@ +package rocketpool + +import ( + "context" + "math/big" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +// This is the common interface for execution clients. +type ExecutionClient interface { + + /// ======================== + /// ContractCaller Functions + /// ======================== + + // CodeAt returns the code of the given account. This is needed to differentiate + // between contract internal errors and the local chain being out of sync. + CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) + + // CallContract executes an Ethereum contract call with the specified data as the + // input. + CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) + + /// ============================ + /// ContractTransactor Functions + /// ============================ + + // HeaderByHash returns the block header with the given hash. + HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) + + // HeaderByNumber returns a block header from the current canonical chain. If number is + // nil, the latest known header is returned. + HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) + + // PendingCodeAt returns the code of the given account in the pending state. + PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error) + + // PendingNonceAt retrieves the current pending nonce associated with an account. + PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) + + // SuggestGasPrice retrieves the currently suggested gas price to allow a timely + // execution of a transaction. + SuggestGasPrice(ctx context.Context) (*big.Int, error) + + // SuggestGasTipCap retrieves the currently suggested 1559 priority fee to allow + // a timely execution of a transaction. + SuggestGasTipCap(ctx context.Context) (*big.Int, error) + + // EstimateGas tries to estimate the gas needed to execute a specific + // transaction based on the current pending state of the backend blockchain. + // There is no guarantee that this is the true gas limit requirement as other + // transactions may be added or removed by miners, but it should provide a basis + // for setting a reasonable default. + EstimateGas(ctx context.Context, call ethereum.CallMsg) (gas uint64, err error) + + // SendTransaction injects the transaction into the pending pool for execution. + SendTransaction(ctx context.Context, tx *types.Transaction) error + + /// ========================== + /// ContractFilterer Functions + /// ========================== + + // FilterLogs executes a log filter operation, blocking during execution and + // returning all the results in one batch. + // + // TODO(karalabe): Deprecate when the subscription one can return past data too. + FilterLogs(ctx context.Context, query ethereum.FilterQuery) ([]types.Log, error) + + // SubscribeFilterLogs creates a background log filtering operation, returning + // a subscription immediately, which can be used to stream the found events. + SubscribeFilterLogs(ctx context.Context, query ethereum.FilterQuery, ch chan<- types.Log) (ethereum.Subscription, error) + + /// ======================= + /// DeployBackend Functions + /// ======================= + + // TransactionReceipt returns the receipt of a transaction by transaction hash. + // Note that the receipt is not available for pending transactions. + TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) + + /// ================ + /// Client functions + /// ================ + + // BlockNumber returns the most recent block number + BlockNumber(ctx context.Context) (uint64, error) + + // BalanceAt returns the wei balance of the given account. + // The block number can be nil, in which case the balance is taken from the latest known block. + BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error) + + // TransactionByHash returns the transaction with the given hash. + TransactionByHash(ctx context.Context, hash common.Hash) (tx *types.Transaction, isPending bool, err error) + + // NonceAt returns the account nonce of the given account. + // The block number can be nil, in which case the nonce is taken from the latest known block. + NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) + + // SyncProgress retrieves the current progress of the sync algorithm. If there's + // no sync currently running, it returns nil. + SyncProgress(ctx context.Context) (*ethereum.SyncProgress, error) +} diff --git a/bindings/rocketpool/rewards.go b/bindings/rocketpool/rewards.go new file mode 100644 index 000000000..85204074f --- /dev/null +++ b/bindings/rocketpool/rewards.go @@ -0,0 +1,35 @@ +package rocketpool + +import ( + "fmt" + "math/big" + "sync" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" +) + +// rocketpool-go's dependencies are all inverted: the subfolders have dependencies back to +// the main package. This is less than ideal, but hard to fix- instead, I will be migrating content +// out of the subpackages into the main package to fulfill interfaces as needed. + +// Get the index of the active rewards period +func (rp *RocketPool) GetRewardIndex(opts *bind.CallOpts) (*big.Int, error) { + rocketRewardsPool, err := getRocketRewardsPool(rp, opts) + if err != nil { + return nil, err + } + index := new(*big.Int) + if err := rocketRewardsPool.Call(opts, index, "getRewardIndex"); err != nil { + return nil, fmt.Errorf("error getting current reward index: %w", err) + } + return *index, nil +} + +// Get contracts +var rocketRewardsPoolLock sync.Mutex + +func getRocketRewardsPool(rp *RocketPool, opts *bind.CallOpts) (*Contract, error) { + rocketRewardsPoolLock.Lock() + defer rocketRewardsPoolLock.Unlock() + return rp.GetContract("rocketRewardsPool", opts) +} diff --git a/bindings/rocketpool/rocketpool.go b/bindings/rocketpool/rocketpool.go new file mode 100644 index 000000000..61061fc7e --- /dev/null +++ b/bindings/rocketpool/rocketpool.go @@ -0,0 +1,365 @@ +package rocketpool + +import ( + "fmt" + "strings" + "sync" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "golang.org/x/sync/errgroup" + + "github.com/rocket-pool/smartnode/bindings/contracts" +) + +// Cache settings +const CacheTTL = 300 // 5 minutes + +// Cached data types +type cachedAddress struct { + address *common.Address + time int64 +} +type cachedABI struct { + abi *abi.ABI + time int64 +} +type cachedContract struct { + contract *Contract + time int64 +} + +// Rocket Pool contract manager +type RocketPool struct { + Client ExecutionClient + RocketStorage *contracts.RocketStorage + RocketStorageContract *Contract + VersionManager *VersionManager + addresses map[string]cachedAddress + abis map[string]cachedABI + contracts map[string]cachedContract + addressesLock sync.RWMutex + abisLock sync.RWMutex + contractsLock sync.RWMutex +} + +// Create new contract manager +func NewRocketPool(client ExecutionClient, rocketStorageAddress common.Address) (*RocketPool, error) { + + // Initialize RocketStorage contract + rocketStorage, err := contracts.NewRocketStorage(rocketStorageAddress, client) + if err != nil { + return nil, fmt.Errorf("error initializing Rocket Pool storage contract: %w", err) + } + + // Create a Contract for it + rsAbi, err := abi.JSON(strings.NewReader(contracts.RocketStorageABI)) + if err != nil { + return nil, err + } + contract := &Contract{ + Contract: bind.NewBoundContract(rocketStorageAddress, rsAbi, client, client, client), + Address: &rocketStorageAddress, + ABI: &rsAbi, + Client: client, + } + + // Create and return + rp := &RocketPool{ + Client: client, + RocketStorage: rocketStorage, + RocketStorageContract: contract, + addresses: make(map[string]cachedAddress), + abis: make(map[string]cachedABI), + contracts: make(map[string]cachedContract), + } + rp.VersionManager = NewVersionManager(rp) + + return rp, nil + +} + +// Load Rocket Pool contract addresses +func (rp *RocketPool) GetAddress(contractName string, opts *bind.CallOpts) (*common.Address, error) { + + // Check for cached address + if opts == nil { + if cached, ok := rp.getCachedAddress(contractName); ok { + if time.Now().Unix()-cached.time <= CacheTTL { + return cached.address, nil + } else { + rp.deleteCachedAddress(contractName) + } + } + } + + // Get address + address, err := rp.RocketStorage.GetAddress(opts, crypto.Keccak256Hash([]byte("contract.address"), []byte(contractName))) + if err != nil { + return nil, fmt.Errorf("error loading contract %s address: %w", contractName, err) + } + + // Cache address + if opts == nil { + rp.setCachedAddress(contractName, cachedAddress{ + address: &address, + time: time.Now().Unix(), + }) + } + + // Return + return &address, nil + +} + +func (rp *RocketPool) GetAddresses(opts *bind.CallOpts, contractNames ...string) ([]*common.Address, error) { + + // Data + var wg errgroup.Group + addresses := make([]*common.Address, len(contractNames)) + + // Load addresses + for ci, contractName := range contractNames { + ci, contractName := ci, contractName + wg.Go(func() error { + address, err := rp.GetAddress(contractName, opts) + if err == nil { + addresses[ci] = address + } + return err + }) + } + + // Wait for data + if err := wg.Wait(); err != nil { + return nil, err + } + + // Return + return addresses, nil + +} + +// Load Rocket Pool contract ABIs +func (rp *RocketPool) GetABI(contractName string, opts *bind.CallOpts) (*abi.ABI, error) { + + // Check for cached ABI + if opts == nil { + if cached, ok := rp.getCachedABI(contractName); ok { + if time.Now().Unix()-cached.time <= CacheTTL { + return cached.abi, nil + } else { + rp.deleteCachedABI(contractName) + } + } + } + + // Get ABI + abiEncoded, err := rp.RocketStorage.GetString(opts, crypto.Keccak256Hash([]byte("contract.abi"), []byte(contractName))) + if err != nil { + return nil, fmt.Errorf("error loading contract %s ABI: %w", contractName, err) + } + + // Decode ABI + abi, err := DecodeAbi(abiEncoded) + if err != nil { + return nil, fmt.Errorf("error decoding contract %s ABI: %w", contractName, err) + } + + // Cache ABI + if opts == nil { + rp.setCachedABI(contractName, cachedABI{ + abi: abi, + time: time.Now().Unix(), + }) + } + + // Return + return abi, nil + +} +func (rp *RocketPool) GetABIs(opts *bind.CallOpts, contractNames ...string) ([]*abi.ABI, error) { + + // Data + var wg errgroup.Group + abis := make([]*abi.ABI, len(contractNames)) + + // Load ABIs + for ci, contractName := range contractNames { + ci, contractName := ci, contractName + wg.Go(func() error { + abi, err := rp.GetABI(contractName, opts) + if err == nil { + abis[ci] = abi + } + return err + }) + } + + // Wait for data + if err := wg.Wait(); err != nil { + return nil, err + } + + // Return + return abis, nil + +} + +// Load Rocket Pool contracts +func (rp *RocketPool) GetContract(contractName string, opts *bind.CallOpts) (*Contract, error) { + + // Check for cached contract + if opts == nil { + if cached, ok := rp.getCachedContract(contractName); ok { + if time.Now().Unix()-cached.time <= CacheTTL { + return cached.contract, nil + } else { + rp.deleteCachedContract(contractName) + } + } + } + + // Data + var wg errgroup.Group + var address *common.Address + var abi *abi.ABI + + // Load data + wg.Go(func() error { + var err error + address, err = rp.GetAddress(contractName, opts) + return err + }) + wg.Go(func() error { + var err error + abi, err = rp.GetABI(contractName, opts) + return err + }) + + // Wait for data + if err := wg.Wait(); err != nil { + return nil, err + } + + // Create contract + contract := &Contract{ + Contract: bind.NewBoundContract(*address, *abi, rp.Client, rp.Client, rp.Client), + Address: address, + ABI: abi, + Client: rp.Client, + } + + // Cache contract + rp.setCachedContract(contractName, cachedContract{ + contract: contract, + time: time.Now().Unix(), + }) + + // Return + return contract, nil + +} +func (rp *RocketPool) GetContracts(opts *bind.CallOpts, contractNames ...string) ([]*Contract, error) { + + // Data + var wg errgroup.Group + contracts := make([]*Contract, len(contractNames)) + + // Load contracts + for ci, contractName := range contractNames { + ci, contractName := ci, contractName + wg.Go(func() error { + contract, err := rp.GetContract(contractName, opts) + if err == nil { + contracts[ci] = contract + } + return err + }) + } + + // Wait for data + if err := wg.Wait(); err != nil { + return nil, err + } + + // Return + return contracts, nil + +} + +// Create a Rocket Pool contract instance +func (rp *RocketPool) MakeContract(contractName string, address common.Address, opts *bind.CallOpts) (*Contract, error) { + + // Load ABI + abi, err := rp.GetABI(contractName, opts) + if err != nil { + return nil, err + } + + // Create and return + return &Contract{ + Contract: bind.NewBoundContract(address, *abi, rp.Client, rp.Client, rp.Client), + Address: &address, + ABI: abi, + Client: rp.Client, + }, nil + +} + +// Address cache control +func (rp *RocketPool) getCachedAddress(contractName string) (cachedAddress, bool) { + rp.addressesLock.RLock() + defer rp.addressesLock.RUnlock() + value, ok := rp.addresses[contractName] + return value, ok +} +func (rp *RocketPool) setCachedAddress(contractName string, value cachedAddress) { + rp.addressesLock.Lock() + defer rp.addressesLock.Unlock() + rp.addresses[contractName] = value +} +func (rp *RocketPool) deleteCachedAddress(contractName string) { + rp.addressesLock.Lock() + defer rp.addressesLock.Unlock() + delete(rp.addresses, contractName) +} + +// ABI cache control +func (rp *RocketPool) getCachedABI(contractName string) (cachedABI, bool) { + rp.abisLock.RLock() + defer rp.abisLock.RUnlock() + value, ok := rp.abis[contractName] + return value, ok +} +func (rp *RocketPool) setCachedABI(contractName string, value cachedABI) { + rp.abisLock.Lock() + defer rp.abisLock.Unlock() + rp.abis[contractName] = value +} +func (rp *RocketPool) deleteCachedABI(contractName string) { + rp.abisLock.Lock() + defer rp.abisLock.Unlock() + delete(rp.abis, contractName) +} + +// Contract cache control +func (rp *RocketPool) getCachedContract(contractName string) (cachedContract, bool) { + rp.contractsLock.RLock() + defer rp.contractsLock.RUnlock() + value, ok := rp.contracts[contractName] + return value, ok +} +func (rp *RocketPool) setCachedContract(contractName string, value cachedContract) { + rp.contractsLock.Lock() + defer rp.contractsLock.Unlock() + rp.contracts[contractName] = value +} +func (rp *RocketPool) deleteCachedContract(contractName string) { + rp.contractsLock.Lock() + defer rp.contractsLock.Unlock() + delete(rp.contracts, contractName) +} diff --git a/bindings/rocketpool/v1.0.0-manager.go b/bindings/rocketpool/v1.0.0-manager.go new file mode 100644 index 000000000..a8f7084f4 --- /dev/null +++ b/bindings/rocketpool/v1.0.0-manager.go @@ -0,0 +1,60 @@ +package rocketpool + +import ( + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/hashicorp/go-version" +) + +// A wrapper that holds the updated contract information for this version +type LegacyVersionWrapper_v1_0_0 struct { + rp *RocketPool + rpVersion *version.Version + contractNameMap map[string]string + abiMap map[string]string +} + +// Creates a new wrapper for this version +func newLegacyVersionWrapper_v1_0_0(rp *RocketPool) *LegacyVersionWrapper_v1_0_0 { + rpVersion, _ := version.NewSemver("1.0.0") + return &LegacyVersionWrapper_v1_0_0{ + rp: rp, + rpVersion: rpVersion, + contractNameMap: map[string]string{ + "rocketRewardsPool": "rocketRewardsPool.v1", + "rocketClaimNode": "rocketClaimNode.v1", + "rocketClaimTrustedNode": "rocketClaimTrustedNode.v1", + "rocketMinipoolManager": "rocketMinipoolManager.v1"}, + abiMap: map[string]string{ + "rocketRewardsPool": "eJzVWN9vmzAQ/lcmnvPADxtD37po0ia1VZVmT1U1HfYRoRKobJMmqvq/zxCSQBOatMsYe0vs43zfd2ffZ9+/WEn2VGhlXdyXPzXKDNLp6gmtC4vnmZbA9ZdJzh9R3+lcwgx/lEYxcLRGVgbz0vCXbBpcCiFRKTOt136gHnh9GFlKg8brQkOUpIlemdksz55gBVGKuy/MykrLghuHZlAlswx0Id/OvI5eLDCfr+Z5YQDEkCoctfEIXKKwLswX1UwLHmzjrGHwFJJ5ks3GNe4DCEafdtrNStPnFkPLaWH+u9TfOQWDOGvEtzH4jC+dzPGAp4etweT2apo/YqbGJRTjeWuMC6yiaCbIXlKMHYfboU89QpkTeg4NYrQjwimNInBsLlzhskAg4bHHfRFyZCSmFG2fUofXKOos7uJYoFRJnpn18kJ31WwZf7AD1wYWdJTgIsHnnWVcZFyvF6qqDQzGOtlvgJJYEEHtroBnqA13XyGFrNotR8JuZeVwRs4WuuuEAfpuJ9cm9Crb1WZfmDBN4u80SD0sGBF4hEck+jCMcT43xlUtDwmOZ3uIGJwKR92CUkMDYRMvYEy8tyv2cjIsBNQGN2ZIjyEoI78Cpa9BDAwBD23PYduG0BWW6aSmMzX6+Kbj30CzJ9Rmr/sMNJrlt2WitHqfhijP00McVONnJYARQhwRiD4JyEoNc2Qz9seAj8INKCUfZqBTBJ3EwjTXkO6EwoD2BBo54vgOPULIXlhrRjanVdU9pocl0+gc7O472ZOUay8o3xXa3Sn6qVB+B3VSkvqrV4f5IYt85/z12g+jE5yZAxAliuE1NJdEkTBa/H/ldgxZNTyUWvVC4hIW9322llSsz9dCyvXNa0BFZkgJGMW/UGSnkXKDy4ExAuZm66Af9czILUpeytJhkWGj6zJOnVOvBRN8BilUldqBIRGUUebCP0jrsIiIffBiH+OeibhM0/x5eE8qNoS+Z3N3oE1230mH0jVOqtfFut663wU3qbncPEYOKBk+w4Cw4FiHPguPbd3xC7f3wLf6o7aQtVJcC3DZIu7kF/IGE23kJLLRE+6xa85ZkO870XmfFcj3ROGf8xeGPo/AL5+KfwPvAY9J", + "rocketClaimNode": "eJzNlMFuwjAMhl9l6rmHUtoOuKFphx0mIbYbQshJ3CpaSVDiwtDEuy+ljNKNwg7dxK2Nnfj77V+efXhSrQqy3mhWfhIaBfnrdoXeyONakQFOd1PN35BeSBvI8KlMSoGj53sKlmXiwpwmjIUwaK0LU/UOHA52c9+zBITPBQGTuaStiyqtVrAFlmN9w1W2ZAruHnSHVmYKqDDfIzv/hH5+pFmjsVIrd1EX1KatcP+DWkFdugqcR11L3NSZaaE4VYX2VKDIGzk2bBIH73GUikjEQRtwhvSoygaIy8xM6/wc8v68U+LeQGAAqWgS/wCC46S/jKC0uDz/WvJDDnI50dbKavQ3ITyMwoClSfLnwqe4ASPsBA2/btQwTtqsWoa6NWu/z1nEgv/qwHipC8dyUz3oB3EUJ9C1/f0rxl6gaq7Bo78PCQYzad0DjW79eqmeyG/KBRb0WJQM2/YTL8fVcc0I73tiGLoWzz8BB105fQ==", + "rocketClaimTrustedNode": "eJzNlMFuwjAMhl9l6rmHUtoOuKFphx02IbYbQshN3CpaSVDiwNDEuy+ljNKNwg4McWtsx/7s/PXk0xNyYcl4g0n5SaglFG/rBXoDjylJGhjdjRV7R3olpSHHpzIoA4ae70mYl4EzfRgw5FyjMc5NVR7YGTZT3zMEhM+WIBWFoLXzSiUXsIa0wPqGq2xIW+YSOqMRuQSy+qdn4x/QT/c0S9RGKOkuKkttvVl37tUd1KUrx3HUpcBVHZlZyagqtKUCSd7AsWGTOPiIo4xHPA7agHOkR1kOgJ9mTpUqjiFv7Rcl7vQ4BpDxJvEvINi/9LcQXDJDyF8UPy2DuvOHAsR8pIwRlQJuov8wCoM0S5Jr9T/GFWhuRqjZedmGcdIm3NJ1Wel2uyyN0uDKgxjOlXVINzWKbhBHcQL/9E/4Z9Q+Q9lckXvR7wI05sKV0Y2h/XnhHkyh2TWkQSeNkn7b7mLlq124ZoT3Hd4P3aSnX+6rRSk=", + "rocketMinipoolManager": "eJztWU1v2zgQ/SuFzj5QEimSuWU/CvSQRbBdbA9BEAzJYSrElgyJcmIU/e+lbCuyHduSY7tQgd5scTic9zjDGQ7vvgVpNq1cGVzd1T8dFhmM/5tPMbgKdJ65ArT78G+un9B9dnkBj/ipFrKgMRgFGUxqwYdiXeDamALL0g+7pR5Yffh+PwpKBw5vKgcqHadu7kezPJvCHNQY2xl+5dIVlfYKg++jbwF4ofkkr7yZFsYljjatNviCJrjyMxYjGyDg1ZqVsZM0S6d5Pt5h3+hoZVlusEPRq8Ebmir/P2JJq8mlkzVNzXBN2UrgZmX3nwV6Ck0rizPMnP9bpo8ZuKqov5EXIhRVMlRgwUoZhZRpZgxHIoWx2qg4soJLHlIdMqoTFuqIMwGUag4ISH/zvsX7X+hdMp93Mx8TybUilsWJNkKEIVMipDJkhpOYmMiiZELSOGZcm0iFIDE0CgQkPEadyBWMFc+tITMsyjTP/Hp55fbFbA1AtOg2kYk9IThL8bmVtFWm3XKhRRyCx7jalk2gjFpDDSP7DH5E9+q0ebVgqsPyjZ3ZvStns977uCHKb+d+6z87eEqzxwGDSLjSEMXsAIiPqTcvLdEMGIYJERKNhzzp2mua4YAxaJSKS9jyp26zcmtLdDuMG3XPHaeTdNfU/TF4i4X3aleVR5Lndbh06UcN9++wd1rgGDyhX0/QUS6D8gQNz6n7agp4rmuOE9SYtCzz8Ww/Ief0rlgx1GhhcN5122xp42YdnrXK1Xf3uwKzHTxvojIWrUzYseQ9LIqJvvF13XEgvSli+lbI70SN1vI4lLYD9RuzHuri6mAR32L/x4sO+EwO/aGMOo4vTcHgk1MoKEVNwksT8SsUG0oIENLyS3Mx/OqRcUXRJurMTPRILf1O1vXTZWinq1AxISSSl/ai/308GXDDdiQrSZRwTYfsSG+JHJpLSQOEE9KVsNXc4TqH00o94bxdbzm+u0b5Y37bSA8IuLYJUJ4cn6abnlLPeGpY+PslLV1HnaqWrao3BCy+n/cOTxKlkgh/Fvr1RtYgCADOuBS8625zNgL6hMBWkO2KrvPd7VCxmOHxqeSd+L8099+6l2wwqy/2XdHwHjqmNcDj6Yi04omOL1efpZkucOJx96rSej9arAHcOtcl4ZEQ+mKADP5cQJwpJjW/3M1yY4d63ClOh6QIRSEScfEKBrNq8qE9iqd5ueixNOrM8stCeLt336ccKmF8uGWjF89HN+1jzIGo33oAbCbteAHsWyActScsFMhJrPd1hc0yke3EcgYnV4oSgLNXY+XBRHS63ZEPzIQaeqCX3laDDvXyJW0gZ78VTCWS+krw/gfIvWxo", + }, + } +} + +// Get the version for this manager +func (m *LegacyVersionWrapper_v1_0_0) GetVersion() *version.Version { + return m.rpVersion +} + +// Get the versioned name of the contract if it was upgraded as part of this deployment +func (m *LegacyVersionWrapper_v1_0_0) GetVersionedContractName(contractName string) (string, bool) { + legacyName, exists := m.contractNameMap[contractName] + return legacyName, exists +} + +// Get the ABI for the provided contract +func (m *LegacyVersionWrapper_v1_0_0) GetEncodedABI(contractName string) string { + return m.abiMap[contractName] +} + +// Get the contract with the provided name for this version of Rocket Pool +func (m *LegacyVersionWrapper_v1_0_0) GetContract(contractName string, opts *bind.CallOpts) (*Contract, error) { + return getLegacyContract(m.rp, contractName, m, opts) +} + +func (m *LegacyVersionWrapper_v1_0_0) GetContractWithAddress(contractName string, address common.Address) (*Contract, error) { + return getLegacyContractWithAddress(m.rp, contractName, address, m) +} diff --git a/bindings/rocketpool/v1.1.0-manager.go b/bindings/rocketpool/v1.1.0-manager.go new file mode 100644 index 000000000..162484d8d --- /dev/null +++ b/bindings/rocketpool/v1.1.0-manager.go @@ -0,0 +1,67 @@ +package rocketpool + +import ( + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/hashicorp/go-version" +) + +// A wrapper that holds the updated contract information for this version +type LegacyVersionWrapper_v1_1_0 struct { + rp *RocketPool + rpVersion *version.Version + contractNameMap map[string]string + abiMap map[string]string +} + +// Creates a new wrapper for this version +func newLegacyVersionWrapper_v1_1_0(rp *RocketPool) *LegacyVersionWrapper_v1_1_0 { + rpVersion, _ := version.NewSemver("1.1.0") + return &LegacyVersionWrapper_v1_1_0{ + rp: rp, + rpVersion: rpVersion, + contractNameMap: map[string]string{ + "rocketNetworkPrices": "rocketNetworkPrices.v1", + "rocketNodeStaking": "rocketNodeStaking.v2", + "rocketNodeDeposit": "rocketNodeDeposit.v2", + "rocketMinipoolQueue": "rocketMinipoolQueue.v1", + "rocketMinipoolFactory": "rocketMinipoolFactory.v1", + }, + abiMap: map[string]string{ + "rocketNetworkPrices": "eJztVslu2zAQ/ZVCZx+0kKKYW1v0UMAFDKc9BUEwJIeBEJkSSMqNEeTfSyuKFSVektYpjKIHAzI5y3sz5ONc3EWlaVrvorOL9adHa6D6vmowOotkbbwF6T/Ma3mD/tzXFq7x69pIg8RoEhlYrA2v7FODj0pZdC5s+4c40C/cX04i58Hjt9aDKKvSr8KuqU0DKxAVDh4hs/O2lSFgWHTltQHf2uc795O7CIL7alG3gYCGyuFkzEfhLaroLHh0OyN6sMHZ09C2XmxBPXkSaJNjFKkN/1OaD5FEFQoyhHrc/51QtqlmtpR4nGioNUpfLnHeVOcebo4U1peLbZEuNwYdBXfeikXpfQi8scUlGv+sx/FtDimNmS6IykETolLgMqMUmE4wkyTniUhIHtNEIqdxIkjKVUEIE8BplsjsDUfjf0f/pKM/GgWv6SfqVOsiyVBLHn4sZzzN8lwSkJDmjAkWukshyUBrSYniwSOjShbIhWBZT6Hv3gBiidaVtQn56tbvkrE1+GJgNmZV7FClZYk/B0vdmlDjLlEnQBA49qIyJkqJVkTReBfga/QPhfvUn6cDuEc92d6Po2GPNZJYSLUH+3w2fTy6JwScpUQRiPke4F82t2Q2fbwlJ8SASlGknONbGPRX7wQPEtJcJIoVYzaHYV1BUOtOPXbLTmmkRXC4v6GvHjSecBpzELRQBVfZe3BQ+Hc4xDQNrSDxmznsfewO+B563Q64v+Y5GyrpuoHiQVGPfQAgTYjM5L9bvDAPyNb3OvIuNdSMKgYi3yVrpfkchASNa8eZX1AVdV1tk69u/ajalQW8NI7JHiWehlTOz7GprV8X4gQVGMLUlORhLru//AWSfqX7", + + "rocketNodeStaking": "eJzdV8FOAjEQ/RXT855MNIabB01M0BglejCEDN1Zaey2m3YKEuO/OwXcdbMsIkJEbix9ffPem+5k+/QmlCkCedF5ij8JnQHdmxYoOkJaQw4kHd1Z+YJ0T9bBM15FUAYSRSIM5BE4cF8B52nq0HtepjkPLP547yfCExBeB4Kh0oqmvGqsKWAKQ43VDq7syQXJhOI9eRPAoGluA8vMQHtM6qpTfMVUdHjHbKVmAko1C7HGprhEW/KFqKxRYwr8fHxyWjEBKzJUcX0CNuFCGj2ADrgdNlL5MqZ+Cbi77d5r8CPmLGE4Rnaz7bgzZ/P9i3utgAhedp8P2f+ZzqOiUepgYpYFVMZRbRmj88pGtA3UNnFiqbNKR13DWcsAGSucVMgsGEmxUIuOZ6SeJdCfDf5eUC2a5bFsJqpRrXE4BnFYrZynlasbhh60qbQ3P5Z/5ax5hi6yDBk43svcG9VslnlsnRCr92qVqw23ukLfOiVXDxQJWgbNvvc/11+f58N2d62MykN+mN7g9XC9cd8Ka3V38abvjbNGtUHrp05lysf+cJ9qRta+fuwo8zWG5U+8XfLlaJf2NhI3WXwW/sfs+Q42uxSdr9GDiNuCx/4HCbNAvw==", + + "rocketNodeDeposit": "eJytk11rwjAUhv/KyHWvHBvi3UAGu3AM3Z1IOU1PJdgkJTnpLOJ/X2pr084qm+yuad7znPd8ZH1gQhWOLJut609CoyD/rApkM8a1IgOcHpaa75BWpA1s8a0WZcCRRUyBrIWx6Qte0tSgtf6aGg60P46biFkCwoUjSEQuqPK3SqsCKkhyDBE+syXjuAeyY3Rg4EWV1M7bzCC3GA1dp7jHlM18xOlmUAR0blqzmdFyxFvUA3U5BiTnz5On50AC70hRYJ0F97BISBwhbTrBHAttBS2Royg9uNNiid5Ek7PtSYgq0VihlVdrR9fmXGebBitDG9MrYysFfgVl5hSnOtHAx2imQdmxFEpIJ991iq841oHoApJUhL15xiXkIgW/Kh8u2WEVGI3wL4SV2CogZ/C3kMdJD5M2I5oDwVJr+sHw0hHKZUcs5Fd36vZmx7gvkBOmC9/UQuv85ktsg1rTgw0ZHfjFI7175lffTXC1RWoX/kS5ub6onHw4lzzvyvmXbd58Aw32wtE=", + + "rocketMinipoolQueue": "eJztV0tv2zAM/iuDzzlIsiTLve3RQ4F1h663oigoicyMJXZmy1mDov99yqN1nGcPbZdDb45Ikd/3USKVm4ekKCdtaJKzm/lnwLqE0fVsgslZ4qoy1ODCp6vK/cbwM1Q1DPFi7kTgMBkkJYznjnf1usNn72tsmmgOyziwWni8HSRNgICXbQBbjIowi9ayKicwAzvCbkfM3IS6dTFgXGyKYQmhrTctj4OHBOL22bhqIwGCUYODPh+P9+iTs7hjYenRg2ecKxrjoiwmVTXagXxwLJidBWxS0QX702KLF76L9eTRi/WMuResjb+F0l2wUIzX1Hkyz/VcOVyuoH/DRd61tDjFMmyIyO5d5nOPDNB545URuc+1J2+FcEJbqYXyEL8Vy41TKkNjWSpBGcYVcZPDh/bb2p+XL9PeWsaMQnJpxo3NgIRgkjwnTh4MUu5TsgCSkSGXkbRMAubM61ygJPeh/Q7tr3BcTV8gvfZakjAkuXUamdaWyEqpSFvHyIs0t4Kj45Rl3KFUnMALneo0syitXJFYCd3BmGLdFFUZ81Vt2NdO5/BNx63Py+zpjtMC/3ae1JYuLBMtGiFEjqu69ImqeKCkV2wf4CGG6yrA6DuWw/DrOO5eVXZX5NWwi9TmiG4D+xYsLNvxp67vTaqmCGsjyS9XFs47lO50OEUJcsyy2AzoWPm+wgTcPNNJoZdSOG4oPYD+nAhjsCmeJgPUzEpmD+n/A+/DaYJXXHORMnUEfHdlDmA/fMm2Ltbgv5PXYISMo/Ptmsc2x62BendgonZ1wOV74bLz7Qrx4pfymjAbo06gIszte3RRjz0mX2YvOlt73yGv8AdirywmdlbDxN6bvUHlBBl4w5E5Z96jsPXiWfVGJ9Qjt96ZWIrbf2ct65Y=", + + "rocketMinipoolFactory": "eNqlkU9LAzEQxb9KyXlPgiK9+efioRf1VkqZzU5LMJ0JyaQlSL+7ibtttxgWxVvIe/Pml5flpzLkogQ1X5ajoCew78mhmivNJB60zF5Zf6C8CXvY4ksxbUCjahTBrhjXfmx46DqPIWRZ+hwYLo6rRgUBwUUUaI01krJKTA4StBYvE3lzEB91DlTHZooRzstOLMTdBELzIwEp7mYLQ8Yx22d0HIyM4rr+5tt8jos5474WVoSb27vRfAAr14NFL1UMjrzAcnoaus5WjvLr1/6n5E0kLYbpuuEL2BblVMtjEtS512m6NruqbL1QJ3PR/4Fpjz4UeZKj/50Kx/BtVY69wUONY/UF4uEWyg==", + }, + } +} + +// Get the version for this manager +func (m *LegacyVersionWrapper_v1_1_0) GetVersion() *version.Version { + return m.rpVersion +} + +// Get the versioned name of the contract if it was upgraded as part of this deployment +func (m *LegacyVersionWrapper_v1_1_0) GetVersionedContractName(contractName string) (string, bool) { + legacyName, exists := m.contractNameMap[contractName] + return legacyName, exists +} + +// Get the ABI for the provided contract +func (m *LegacyVersionWrapper_v1_1_0) GetEncodedABI(contractName string) string { + return m.abiMap[contractName] +} + +// Get the contract with the provided name for this version of Rocket Pool +func (m *LegacyVersionWrapper_v1_1_0) GetContract(contractName string, opts *bind.CallOpts) (*Contract, error) { + return getLegacyContract(m.rp, contractName, m, opts) +} + +func (m *LegacyVersionWrapper_v1_1_0) GetContractWithAddress(contractName string, address common.Address) (*Contract, error) { + return getLegacyContractWithAddress(m.rp, contractName, address, m) +} diff --git a/bindings/rocketpool/v1.1.0-rc1-manager.go b/bindings/rocketpool/v1.1.0-rc1-manager.go new file mode 100644 index 000000000..43f6dd540 --- /dev/null +++ b/bindings/rocketpool/v1.1.0-rc1-manager.go @@ -0,0 +1,55 @@ +package rocketpool + +import ( + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/hashicorp/go-version" +) + +// A wrapper that holds the updated contract information for this version +type LegacyVersionWrapper_v1_1_0_rc1 struct { + rp *RocketPool + rpVersion *version.Version + contractNameMap map[string]string + abiMap map[string]string +} + +// Creates a new wrapper for this version +func newLegacyVersionWrapper_v1_1_0_rc1(rp *RocketPool) *LegacyVersionWrapper_v1_1_0_rc1 { + rpVersion, _ := version.NewSemver("1.1.0-rc1") + return &LegacyVersionWrapper_v1_1_0_rc1{ + rp: rp, + rpVersion: rpVersion, + contractNameMap: map[string]string{ + "rocketRewardsPool": "rocketRewardsPool.v2", + }, + abiMap: map[string]string{ + "rocketRewardsPool": "eJztWdtu2zgQ/ZWFnv2gC6lL3rbZAhugWxiJ3wLDGJHDVKhMGSSV1ij676UUNZJs+ZZ1ARXVk21yeDjnjGY4lB+/OZnclEY7N4/VV4NKQr7YbtC5cVghjQJm/rov2Gc0D6ZQ8IR3lZEAhs7MkbCuDFeqa/A35wq1ttPmBQeage/LmaMNGPyvNJBmeWa2dlYWcgNbSHNsV9idtVEls4B2UGdPEkypdme+z745YJdv10VpCQjINc76fDh+Re7c2BX1TI9eaX/7NGxpKPwCit9Vi1pXflpVm7FivSkkymG5Lsc7BWCdZ6XJCvkutwK/DaMSDKUu9fkY6dagDvwWY43qc473RWHa9T+NBtbbAGXyaXf5QiHe3v3TIjRm55Co559tfOegtQ3om5QwCkGXans//3AJwOOyC1Fqg/xjwXEIxZqehyOvAfB+8e8gwHLWPvivObEboLJK6/r5fCjTdaa1fcpaeN0da3Yw5cam6It7R9EPRu/BgDKLbI2H5H8b7HvJrwdqhpGWrwaNaBI2+lM3H/DZ1oWdYuV+5SkPOKaEhC53eYiYMkIJJiKlHjKG3Is540lkPykK4XHqh24EFHjIYzdM/m+Ng9da3LgvVLEeqMyzqVhOxXIqliMolhcWoNohY7phHq5EiUiCIGEh8xM3JIS5AefCVibBiE8x4BEVGIacB7EgFKkPwL3IgwTQDVLfTxoyTd1p3XlG1VAvSnOok6xoxC3HPr/4QGP4nOGX1lKUkpmXjeoeECzHplT1iVIiOOHUPeTwE5r7Xq054XcvOsORuZrvwveYR112zPf5h3eQg6z77xG57ntJjKF/zPU5Sm6LmGXwEgA9LgaRTQThYXSagS0mo2SQxl7KgXpHGNzmkK3vmhOiapvqpmxkNCAgLCXpJTTGxYC64IsI6bkM2qN6RCRcEsRRxHcK6ckGZsUqZnbotnl9sN/E7AvRMZ+jYuMSQoQQiBDFWUJ0G489KfSuFk0LclgNfbYc3Y0PtTpXk4Rxe8iix05Isnf9WHWawsPvic7oT1fH7ws9TRftnt126YikaVHkQ2rW49c9Nl2CPriwK+R0e5puT7/J7emy29LqyHWpl7Xt4tuilCNrUWgCjARs70iY0nZK2z84bWsjs/eKtE3cs/8B6mRiP/MCFgL47pR5U+ZNmbfceRzxl6YeCUgc8foo/QHwlCkA", + }, + } +} + +// Get the version for this manager +func (m *LegacyVersionWrapper_v1_1_0_rc1) GetVersion() *version.Version { + return m.rpVersion +} + +// Get the versioned name of the contract if it was upgraded as part of this deployment +func (m *LegacyVersionWrapper_v1_1_0_rc1) GetVersionedContractName(contractName string) (string, bool) { + legacyName, exists := m.contractNameMap[contractName] + return legacyName, exists +} + +// Get the ABI for the provided contract +func (m *LegacyVersionWrapper_v1_1_0_rc1) GetEncodedABI(contractName string) string { + return m.abiMap[contractName] +} + +// Get the contract with the provided name for this version of Rocket Pool +func (m *LegacyVersionWrapper_v1_1_0_rc1) GetContract(contractName string, opts *bind.CallOpts) (*Contract, error) { + return getLegacyContract(m.rp, contractName, m, opts) +} + +func (m *LegacyVersionWrapper_v1_1_0_rc1) GetContractWithAddress(contractName string, address common.Address) (*Contract, error) { + return getLegacyContractWithAddress(m.rp, contractName, address, m) +} diff --git a/bindings/rocketpool/v1.2.0-manager.go b/bindings/rocketpool/v1.2.0-manager.go new file mode 100644 index 000000000..6291dc899 --- /dev/null +++ b/bindings/rocketpool/v1.2.0-manager.go @@ -0,0 +1,57 @@ +package rocketpool + +import ( + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/hashicorp/go-version" +) + +// A wrapper that holds the updated contract information for this version +type LegacyVersionWrapper_v1_2_0 struct { + rp *RocketPool + rpVersion *version.Version + contractNameMap map[string]string + abiMap map[string]string +} + +// Creates a new wrapper for this version +func newLegacyVersionWrapper_v1_2_0(rp *RocketPool) *LegacyVersionWrapper_v1_2_0 { + rpVersion, _ := version.NewSemver("1.2.0") + return &LegacyVersionWrapper_v1_2_0{ + rp: rp, + rpVersion: rpVersion, + contractNameMap: map[string]string{ + "rocketNetworkPrices": "rocketNetworkPrices.v2", + "rocketNetworkBalances": "rocketNetworkBalances.v2", + }, + abiMap: map[string]string{ + "rocketNetworkPrices": "eJzlVE1v2zAM/SuDzznIimRLuW23AR1QpNupKApKogNjjm1IdNag6H+fkrhx890NHRBgN1skH997lHj/nJR121FIJverT0JfQ/V92WIySWxTkwdLn6aN/Yl0R42HGX5dJRVgMRklNcxXiY/+bcJn5zyGEMO0wYH+4OVhlAQCwm8dgSmrkpYxWjd1C0swFQ4VsXMg39kIGA9DOauBOr8feRk9JxDLl/OmiwIKqAKOdvU4fEKXTGLFOrIjD7Y8exmFb+ZHWI/eAG177CB18Z/LbEAyVTRkgHqN/w2Ub6tbX1r8GDQq58eQHrYJ617hrjPzkigCb3NxgTXtDYM98VyqtDBGaa0BlRijhkwxlaHBnGVKKJSFVpKhcxpAa25S7kCIXI9jZ/sHM/xPrP/ROniH8ZAZNWYmFyi5y12aCoZ5xiUvOJfO5gYdV7niMtVKyIwxY9w4VXwsUmfTVPYSTj39A/aPZ329UHvGyEF+NNN2hBv9Gy9iftNRz/Hdy6PoaktlUx94pguTiULrXe0DgxnSTWwQaIpt42mF+qVXPdC4LPe4ykPuixJ/HWW93nEQh97vrb3Ja2PSLN6Q0yo27l0hd1agYMae4z69vXm9K1dEPOfCCWD6+p5NWK/qf/JeuEanGYdT01qgD5u6C5NSp+akPnZKUhROOMki6m+WhvGX", + "rocketNetworkBalances": "eJztVk1vm0AQ/SsVZx/4XMC3RoqUSu3Fdk6RFc3uzjooGBAMbtwo/71jm5g4tiGpXIlDfMK7M2/e2zfscPdsJVlRU2WN7zaPhGUG6WxdoDW2VJ5RCYq+TXL1iDSlvIQF/tgEGVBojawMlpvA+/JtwHetS6wq3qYdDjQLL/ORVREQ/qoJZJImtObdLM8KWINMsc3gyhWVtWJAXqySRQZUl+93XkbPFnD6epnXLMBAWuHoUI/GJ9TWmDO2OwfyYM+zkWHKfHmC9egN0L7GAVLN/91AtEgy5QNpoV73/wWKcoL0mh4ug8bH/5hki4vhlUgP07oo0vWF1CZLPIE03wdcQQqZwmpay2VCxND7aFxhRu/axX5CEYQgtIiD0PgGpPRCGQnta153MHK1sYUjtasj8FBjoJ04lLaMIwiA180nuuyrOQbSHLeFhg+0Rij553ih0aB9z9PCEZF0VBio0PFV6MoIJEbaDn1hG1vZkoNs4WEojWO82G9EnLs+j/jfdzrfk9tndU96v7c9AJ1mthawoaom3Dnw6gdn5DU15/ThIWDqTFGSZ0e+BcIYT7jB4fm3HBZIr5WvmhNvy/dLPa3vmPMqwd8n2W5nFHDDNXPnkH3s2LZyPN3B/np2c0tc5g9sECdceGAStAQhwHRI+MmlKppgkZe0sXWARkDMb7/ALiOmzVszu2kaalgKjIMahOd3KJhtr42B8o+Fr23fVX38Jyxgf/cMiL/yFY8H0F+D4HgQVNsPtP80ATxXy8AP7XONs8Ky2uX1dEt0rleiy3ZK4Bv+5AyY8PwvSEB9Dw==", + }, + } +} + +// Get the version for this manager +func (m *LegacyVersionWrapper_v1_2_0) GetVersion() *version.Version { + return m.rpVersion +} + +// Get the versioned name of the contract if it was upgraded as part of this deployment +func (m *LegacyVersionWrapper_v1_2_0) GetVersionedContractName(contractName string) (string, bool) { + legacyName, exists := m.contractNameMap[contractName] + return legacyName, exists +} + +// Get the ABI for the provided contract +func (m *LegacyVersionWrapper_v1_2_0) GetEncodedABI(contractName string) string { + return m.abiMap[contractName] +} + +// Get the contract with the provided name for this version of Rocket Pool +func (m *LegacyVersionWrapper_v1_2_0) GetContract(contractName string, opts *bind.CallOpts) (*Contract, error) { + return getLegacyContract(m.rp, contractName, m, opts) +} + +func (m *LegacyVersionWrapper_v1_2_0) GetContractWithAddress(contractName string, address common.Address) (*Contract, error) { + return getLegacyContractWithAddress(m.rp, contractName, address, m) +} diff --git a/bindings/rocketpool/version-interface.go b/bindings/rocketpool/version-interface.go new file mode 100644 index 000000000..c78f27d52 --- /dev/null +++ b/bindings/rocketpool/version-interface.go @@ -0,0 +1,77 @@ +package rocketpool + +import ( + "fmt" + "strings" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" +) + +const ( + rocketVersionInterfaceAbiString string = `[ + { + "inputs": [], + "name": "version", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + } + ]` +) + +var versionAbi *abi.ABI + +// Get the version of the given contract +func GetContractVersion(rp *RocketPool, contractAddress common.Address, opts *bind.CallOpts) (uint8, error) { + if versionAbi == nil { + // Parse ABI using the hardcoded string until the contract is deployed + abiParsed, err := abi.JSON(strings.NewReader(rocketVersionInterfaceAbiString)) + if err != nil { + return 0, fmt.Errorf("error parsing version interface JSON: %w", err) + } + versionAbi = &abiParsed + } + + // Create contract + contract := &Contract{ + Contract: bind.NewBoundContract(contractAddress, *versionAbi, rp.Client, rp.Client, rp.Client), + Address: &contractAddress, + ABI: versionAbi, + Client: rp.Client, + } + + // Get the contract version + version := new(uint8) + if err := contract.Call(opts, version, "version"); err != nil { + return 0, fmt.Errorf("error getting contract version: %w", err) + } + + return *version, nil +} + +// Get the rocketVersion contract binding at the given address +func GetRocketVersionContractForAddress(rp *RocketPool, address common.Address) (*Contract, error) { + if versionAbi == nil { + // Parse ABI using the hardcoded string until the contract is deployed + abiParsed, err := abi.JSON(strings.NewReader(rocketVersionInterfaceAbiString)) + if err != nil { + return nil, fmt.Errorf("error parsing version interface JSON: %w", err) + } + versionAbi = &abiParsed + } + + return &Contract{ + Contract: bind.NewBoundContract(address, *versionAbi, rp.Client, rp.Client, rp.Client), + Address: &address, + ABI: versionAbi, + Client: rp.Client, + }, nil +} diff --git a/bindings/rocketpool/version-manager.go b/bindings/rocketpool/version-manager.go new file mode 100644 index 000000000..d4f52e097 --- /dev/null +++ b/bindings/rocketpool/version-manager.go @@ -0,0 +1,107 @@ +package rocketpool + +import ( + "fmt" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/hashicorp/go-version" +) + +// Wrapper for legacy contract versions +type LegacyVersionWrapper interface { + GetVersion() *version.Version + GetVersionedContractName(contractName string) (string, bool) + GetEncodedABI(contractName string) string + GetContract(contractName string, opts *bind.CallOpts) (*Contract, error) + GetContractWithAddress(contractName string, address common.Address) (*Contract, error) +} + +type VersionManager struct { + V1_0_0 LegacyVersionWrapper + V1_1_0_RC1 LegacyVersionWrapper + V1_1_0 LegacyVersionWrapper + V1_2_0 LegacyVersionWrapper + + rp *RocketPool +} + +func NewVersionManager(rp *RocketPool) *VersionManager { + return &VersionManager{ + V1_0_0: newLegacyVersionWrapper_v1_0_0(rp), + V1_1_0_RC1: newLegacyVersionWrapper_v1_1_0_rc1(rp), + V1_1_0: newLegacyVersionWrapper_v1_1_0(rp), + V1_2_0: newLegacyVersionWrapper_v1_2_0(rp), + rp: rp, + } +} + +// Get the contract with the provided name and version wrapper +func getLegacyContract(rp *RocketPool, contractName string, m LegacyVersionWrapper, opts *bind.CallOpts) (*Contract, error) { + + legacyName, exists := m.GetVersionedContractName(contractName) + if !exists { + // This wasn't upgraded in previous versions + return rp.GetContract(contractName, opts) + } + + // Check for cached contract + if cached, ok := rp.getCachedContract(legacyName); ok { + if time.Now().Unix()-cached.time <= CacheTTL { + return cached.contract, nil + } else { + rp.deleteCachedContract(legacyName) + } + } + + // Try to get the legacy address from RocketStorage first + emptyAddress := common.Address{} + address, err := rp.RocketStorage.GetAddress(nil, crypto.Keccak256Hash([]byte("contract.address"), []byte(legacyName))) + if err != nil { + return nil, fmt.Errorf("error loading v%s contract %s address: %w", m.GetVersion().String(), contractName, err) + } + + if address == emptyAddress { + // Not found, so the legacy contract is still on the network - try loading the original contract name instead + return rp.GetContract(contractName, opts) + } + + // If we're here, we have a legacy contract + abiEncoded := m.GetEncodedABI(contractName) + abi, err := DecodeAbi(abiEncoded) + if err != nil { + return nil, fmt.Errorf("error decoding contract %s ABI: %w", contractName, err) + } + + contract := &Contract{ + Contract: bind.NewBoundContract(address, *abi, rp.Client, rp.Client, rp.Client), + Address: &address, + ABI: abi, + Client: rp.Client, + } + + return contract, nil + +} + +// Get the contract with the provided name, address, and version wrapper +func getLegacyContractWithAddress(rp *RocketPool, contractName string, address common.Address, m LegacyVersionWrapper) (*Contract, error) { + + abiEncoded := m.GetEncodedABI(contractName) + abi, err := DecodeAbi(abiEncoded) + if err != nil { + return nil, fmt.Errorf("error decoding contract %s ABI: %w", contractName, err) + } + + contract := &Contract{ + Contract: bind.NewBoundContract(address, *abi, rp.Client, rp.Client, rp.Client), + Address: &address, + ABI: abi, + Client: rp.Client, + } + + return contract, nil + +} diff --git a/bindings/settings/protocol/auction.go b/bindings/settings/protocol/auction.go new file mode 100644 index 000000000..f5d42567c --- /dev/null +++ b/bindings/settings/protocol/auction.go @@ -0,0 +1,196 @@ +package protocol + +import ( + "fmt" + "math/big" + "sync" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + + "github.com/rocket-pool/smartnode/bindings/dao/protocol" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/types" + "github.com/rocket-pool/smartnode/bindings/utils/eth" +) + +// Config +const ( + AuctionSettingsContractName string = "rocketDAOProtocolSettingsAuction" + CreateLotEnabledSettingPath string = "auction.lot.create.enabled" + BidOnLotEnabledSettingPath string = "auction.lot.bidding.enabled" + LotMinimumEthValueSettingPath string = "auction.lot.value.minimum" + LotMaximumEthValueSettingPath string = "auction.lot.value.maximum" + LotDurationSettingPath string = "auction.lot.duration" + LotStartingPriceRatioSettingPath string = "auction.price.start" + LotReservePriceRatioSettingPath string = "auction.price.reserve" +) + +// Lot creation currently enabled +func GetCreateLotEnabled(rp *rocketpool.RocketPool, opts *bind.CallOpts) (bool, error) { + auctionSettingsContract, err := getAuctionSettingsContract(rp, opts) + if err != nil { + return false, err + } + value := new(bool) + if err := auctionSettingsContract.Call(opts, value, "getCreateLotEnabled"); err != nil { + return false, fmt.Errorf("error getting lot creation enabled status: %w", err) + } + return *value, nil +} +func ProposeCreateLotEnabled(rp *rocketpool.RocketPool, value bool, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return protocol.ProposeSetBool(rp, fmt.Sprintf("set %s", CreateLotEnabledSettingPath), AuctionSettingsContractName, CreateLotEnabledSettingPath, value, blockNumber, treeNodes, opts) +} +func EstimateProposeCreateLotEnabledGas(rp *rocketpool.RocketPool, value bool, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return protocol.EstimateProposeSetBoolGas(rp, fmt.Sprintf("set %s", CreateLotEnabledSettingPath), AuctionSettingsContractName, CreateLotEnabledSettingPath, value, blockNumber, treeNodes, opts) +} + +// Lot bidding currently enabled +func GetBidOnLotEnabled(rp *rocketpool.RocketPool, opts *bind.CallOpts) (bool, error) { + auctionSettingsContract, err := getAuctionSettingsContract(rp, opts) + if err != nil { + return false, err + } + value := new(bool) + if err := auctionSettingsContract.Call(opts, value, "getBidOnLotEnabled"); err != nil { + return false, fmt.Errorf("error getting lot bidding enabled status: %w", err) + } + return *value, nil +} +func ProposeBidOnLotEnabled(rp *rocketpool.RocketPool, value bool, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return protocol.ProposeSetBool(rp, fmt.Sprintf("set %s", BidOnLotEnabledSettingPath), AuctionSettingsContractName, BidOnLotEnabledSettingPath, value, blockNumber, treeNodes, opts) +} +func EstimateProposeBidOnLotEnabledGas(rp *rocketpool.RocketPool, value bool, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return protocol.EstimateProposeSetBoolGas(rp, fmt.Sprintf("set %s", BidOnLotEnabledSettingPath), AuctionSettingsContractName, BidOnLotEnabledSettingPath, value, blockNumber, treeNodes, opts) +} + +// The minimum lot size in ETH value +func GetLotMinimumEthValue(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*big.Int, error) { + auctionSettingsContract, err := getAuctionSettingsContract(rp, opts) + if err != nil { + return nil, err + } + value := new(*big.Int) + if err := auctionSettingsContract.Call(opts, value, "getLotMinimumEthValue"); err != nil { + return nil, fmt.Errorf("error getting lot minimum ETH value: %w", err) + } + return *value, nil +} +func ProposeLotMinimumEthValue(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return protocol.ProposeSetUint(rp, fmt.Sprintf("set %s", LotMinimumEthValueSettingPath), AuctionSettingsContractName, LotMinimumEthValueSettingPath, value, blockNumber, treeNodes, opts) +} +func EstimateProposeLotMinimumEthValueGas(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return protocol.EstimateProposeSetUintGas(rp, fmt.Sprintf("set %s", LotMinimumEthValueSettingPath), AuctionSettingsContractName, LotMinimumEthValueSettingPath, value, blockNumber, treeNodes, opts) +} + +// The maximum lot size in ETH value +func GetLotMaximumEthValue(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*big.Int, error) { + auctionSettingsContract, err := getAuctionSettingsContract(rp, opts) + if err != nil { + return nil, err + } + value := new(*big.Int) + if err := auctionSettingsContract.Call(opts, value, "getLotMaximumEthValue"); err != nil { + return nil, fmt.Errorf("error getting lot maximum ETH value: %w", err) + } + return *value, nil +} +func ProposeLotMaximumEthValue(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return protocol.ProposeSetUint(rp, fmt.Sprintf("set %s", LotMaximumEthValueSettingPath), AuctionSettingsContractName, LotMaximumEthValueSettingPath, value, blockNumber, treeNodes, opts) +} +func EstimateProposeLotMaximumEthValueGas(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return protocol.EstimateProposeSetUintGas(rp, fmt.Sprintf("set %s", LotMaximumEthValueSettingPath), AuctionSettingsContractName, LotMaximumEthValueSettingPath, value, blockNumber, treeNodes, opts) +} + +// // The lot duration +func GetLotDuration(rp *rocketpool.RocketPool, opts *bind.CallOpts) (time.Duration, error) { + auctionSettingsContract, err := getAuctionSettingsContract(rp, opts) + if err != nil { + return 0, err + } + value := new(*big.Int) + if err := auctionSettingsContract.Call(opts, value, "getLotDuration"); err != nil { + return 0, fmt.Errorf("error getting lot duration: %w", err) + } + return time.Duration((*value).Uint64()) * time.Second, nil +} +func ProposeLotDuration(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return protocol.ProposeSetUint(rp, fmt.Sprintf("set %s", LotDurationSettingPath), AuctionSettingsContractName, LotDurationSettingPath, value, blockNumber, treeNodes, opts) +} +func EstimateProposeLotDurationGas(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return protocol.EstimateProposeSetUintGas(rp, fmt.Sprintf("set %s", LotDurationSettingPath), AuctionSettingsContractName, LotDurationSettingPath, value, blockNumber, treeNodes, opts) +} + +// The starting price relative to current ETH price, as a fraction +func GetLotStartingPriceRatio(rp *rocketpool.RocketPool, opts *bind.CallOpts) (float64, error) { + auctionSettingsContract, err := getAuctionSettingsContract(rp, opts) + if err != nil { + return 0, err + } + value := new(*big.Int) + if err := auctionSettingsContract.Call(opts, value, "getStartingPriceRatio"); err != nil { + return 0, fmt.Errorf("error getting lot starting price ratio: %w", err) + } + return eth.WeiToEth(*value), nil +} + +// The starting price relative to current ETH price, as a fraction +func GetLotStartingPriceRatioRaw(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*big.Int, error) { + auctionSettingsContract, err := getAuctionSettingsContract(rp, opts) + if err != nil { + return nil, err + } + value := new(*big.Int) + if err := auctionSettingsContract.Call(opts, value, "getStartingPriceRatio"); err != nil { + return nil, fmt.Errorf("error getting lot starting price ratio: %w", err) + } + return *value, nil +} +func ProposeLotStartingPriceRatio(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return protocol.ProposeSetUint(rp, fmt.Sprintf("set %s", LotStartingPriceRatioSettingPath), AuctionSettingsContractName, LotStartingPriceRatioSettingPath, value, blockNumber, treeNodes, opts) +} +func EstimateProposeLotStartingPriceRatioGas(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return protocol.EstimateProposeSetUintGas(rp, fmt.Sprintf("set %s", LotStartingPriceRatioSettingPath), AuctionSettingsContractName, LotStartingPriceRatioSettingPath, value, blockNumber, treeNodes, opts) +} + +// The reserve price relative to current ETH price, as a fraction +func GetLotReservePriceRatio(rp *rocketpool.RocketPool, opts *bind.CallOpts) (float64, error) { + auctionSettingsContract, err := getAuctionSettingsContract(rp, opts) + if err != nil { + return 0, err + } + value := new(*big.Int) + if err := auctionSettingsContract.Call(opts, value, "getReservePriceRatio"); err != nil { + return 0, fmt.Errorf("error getting lot reserve price ratio: %w", err) + } + return eth.WeiToEth(*value), nil +} + +// The reserve price relative to current ETH price, as a fraction +func GetLotReservePriceRatioRaw(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*big.Int, error) { + auctionSettingsContract, err := getAuctionSettingsContract(rp, opts) + if err != nil { + return nil, err + } + value := new(*big.Int) + if err := auctionSettingsContract.Call(opts, value, "getReservePriceRatio"); err != nil { + return nil, fmt.Errorf("error getting lot reserve price ratio: %w", err) + } + return *value, nil +} +func ProposeLotReservePriceRatio(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return protocol.ProposeSetUint(rp, fmt.Sprintf("set %s", LotReservePriceRatioSettingPath), AuctionSettingsContractName, LotReservePriceRatioSettingPath, value, blockNumber, treeNodes, opts) +} +func EstimateProposeLotReservePriceRatioGas(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return protocol.EstimateProposeSetUintGas(rp, fmt.Sprintf("set %s", LotReservePriceRatioSettingPath), AuctionSettingsContractName, LotReservePriceRatioSettingPath, value, blockNumber, treeNodes, opts) +} + +// Get contracts +var auctionSettingsContractLock sync.Mutex + +func getAuctionSettingsContract(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*rocketpool.Contract, error) { + auctionSettingsContractLock.Lock() + defer auctionSettingsContractLock.Unlock() + return rp.GetContract(AuctionSettingsContractName, opts) +} diff --git a/bindings/settings/protocol/deposit.go b/bindings/settings/protocol/deposit.go new file mode 100644 index 000000000..03381f9a6 --- /dev/null +++ b/bindings/settings/protocol/deposit.go @@ -0,0 +1,208 @@ +package protocol + +import ( + "fmt" + "math/big" + "sync" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + + "github.com/rocket-pool/smartnode/bindings/dao/protocol" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/types" +) + +// Config +const ( + DepositSettingsContractName string = "rocketDAOProtocolSettingsDeposit" + DepositEnabledSettingPath string = "deposit.enabled" + AssignDepositsEnabledSettingPath string = "deposit.assign.enabled" + MinimumDepositSettingPath string = "deposit.minimum" + MaximumDepositPoolSizeSettingPath string = "deposit.pool.maximum" + MaximumDepositAssignmentsSettingPath string = "deposit.assign.maximum" + MaximumSocializedDepositAssignmentsSettingPath string = "deposit.assign.socialised.maximum" + DepositFeeSettingPath string = "deposit.fee" + ExpressQueueRatePath string = "deposit.express.queue.rate" + ExpressQueueTicketsBaseProvisionPath string = "deposit.express.queue.tickets.base.provision" +) + +// Deposits currently enabled +func GetDepositEnabled(rp *rocketpool.RocketPool, opts *bind.CallOpts) (bool, error) { + depositSettingsContract, err := getDepositSettingsContract(rp, opts) + if err != nil { + return false, err + } + value := new(bool) + if err := depositSettingsContract.Call(opts, value, "getDepositEnabled"); err != nil { + return false, fmt.Errorf("error getting deposits enabled status: %w", err) + } + return *value, nil +} +func ProposeDepositEnabled(rp *rocketpool.RocketPool, value bool, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return protocol.ProposeSetBool(rp, fmt.Sprintf("set %s", DepositEnabledSettingPath), DepositSettingsContractName, DepositEnabledSettingPath, value, blockNumber, treeNodes, opts) +} +func EstimateProposeDepositEnabledGas(rp *rocketpool.RocketPool, value bool, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return protocol.EstimateProposeSetBoolGas(rp, fmt.Sprintf("set %s", DepositEnabledSettingPath), DepositSettingsContractName, DepositEnabledSettingPath, value, blockNumber, treeNodes, opts) +} + +// Deposit assignments currently enabled +func GetAssignDepositsEnabled(rp *rocketpool.RocketPool, opts *bind.CallOpts) (bool, error) { + depositSettingsContract, err := getDepositSettingsContract(rp, opts) + if err != nil { + return false, err + } + value := new(bool) + if err := depositSettingsContract.Call(opts, value, "getAssignDepositsEnabled"); err != nil { + return false, fmt.Errorf("error getting deposit assignments enabled status: %w", err) + } + return *value, nil +} +func ProposeAssignDepositsEnabled(rp *rocketpool.RocketPool, value bool, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return protocol.ProposeSetBool(rp, fmt.Sprintf("set %s", AssignDepositsEnabledSettingPath), DepositSettingsContractName, AssignDepositsEnabledSettingPath, value, blockNumber, treeNodes, opts) +} +func EstimateProposeAssignDepositsEnabledGas(rp *rocketpool.RocketPool, value bool, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return protocol.EstimateProposeSetBoolGas(rp, fmt.Sprintf("set %s", AssignDepositsEnabledSettingPath), DepositSettingsContractName, AssignDepositsEnabledSettingPath, value, blockNumber, treeNodes, opts) +} + +// Minimum deposit amount +func GetMinimumDeposit(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*big.Int, error) { + depositSettingsContract, err := getDepositSettingsContract(rp, opts) + if err != nil { + return nil, err + } + value := new(*big.Int) + if err := depositSettingsContract.Call(opts, value, "getMinimumDeposit"); err != nil { + return nil, fmt.Errorf("error getting minimum deposit amount: %w", err) + } + return *value, nil +} +func ProposeMinimumDeposit(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return protocol.ProposeSetUint(rp, fmt.Sprintf("set %s", MinimumDepositSettingPath), DepositSettingsContractName, MinimumDepositSettingPath, value, blockNumber, treeNodes, opts) +} +func EstimateProposeMinimumDepositGas(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return protocol.EstimateProposeSetUintGas(rp, fmt.Sprintf("set %s", MinimumDepositSettingPath), DepositSettingsContractName, MinimumDepositSettingPath, value, blockNumber, treeNodes, opts) +} + +// Maximum deposit pool size +func GetMaximumDepositPoolSize(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*big.Int, error) { + depositSettingsContract, err := getDepositSettingsContract(rp, opts) + if err != nil { + return nil, err + } + value := new(*big.Int) + if err := depositSettingsContract.Call(opts, value, "getMaximumDepositPoolSize"); err != nil { + return nil, fmt.Errorf("error getting maximum deposit pool size: %w", err) + } + return *value, nil +} +func ProposeMaximumDepositPoolSize(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return protocol.ProposeSetUint(rp, fmt.Sprintf("set %s", MaximumDepositPoolSizeSettingPath), DepositSettingsContractName, MaximumDepositPoolSizeSettingPath, value, blockNumber, treeNodes, opts) +} +func EstimateProposeMaximumDepositPoolSizeGas(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return protocol.EstimateProposeSetUintGas(rp, fmt.Sprintf("set %s", MaximumDepositPoolSizeSettingPath), DepositSettingsContractName, MaximumDepositPoolSizeSettingPath, value, blockNumber, treeNodes, opts) +} + +// Maximum deposit assignments per transaction +func GetMaximumDepositAssignments(rp *rocketpool.RocketPool, opts *bind.CallOpts) (uint64, error) { + depositSettingsContract, err := getDepositSettingsContract(rp, opts) + if err != nil { + return 0, err + } + value := new(*big.Int) + if err := depositSettingsContract.Call(opts, value, "getMaximumDepositAssignments"); err != nil { + return 0, fmt.Errorf("error getting maximum deposit assignments: %w", err) + } + return (*value).Uint64(), nil +} +func ProposeMaximumDepositAssignments(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return protocol.ProposeSetUint(rp, fmt.Sprintf("set %s", MaximumDepositAssignmentsSettingPath), DepositSettingsContractName, MaximumDepositAssignmentsSettingPath, value, blockNumber, treeNodes, opts) +} +func EstimateProposeMaximumDepositAssignmentsGas(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return protocol.EstimateProposeSetUintGas(rp, fmt.Sprintf("set %s", MaximumDepositAssignmentsSettingPath), DepositSettingsContractName, MaximumDepositAssignmentsSettingPath, value, blockNumber, treeNodes, opts) +} + +// Maximum socialized deposit assignments per transaction +func GetMaximumSocializedDepositAssignments(rp *rocketpool.RocketPool, opts *bind.CallOpts) (uint64, error) { + depositSettingsContract, err := getDepositSettingsContract(rp, opts) + if err != nil { + return 0, err + } + value := new(*big.Int) + if err := depositSettingsContract.Call(opts, value, "getMaximumDepositSocialisedAssignments"); err != nil { + return 0, fmt.Errorf("error getting maximum socialized deposit assignments: %w", err) + } + return (*value).Uint64(), nil +} +func ProposeMaximumSocializedDepositAssignments(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return protocol.ProposeSetUint(rp, fmt.Sprintf("set %s", MaximumSocializedDepositAssignmentsSettingPath), DepositSettingsContractName, MaximumSocializedDepositAssignmentsSettingPath, value, blockNumber, treeNodes, opts) +} +func EstimateProposeMaximumSocializedDepositAssignmentsGas(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return protocol.EstimateProposeSetUintGas(rp, fmt.Sprintf("set %s", MaximumSocializedDepositAssignmentsSettingPath), DepositSettingsContractName, MaximumSocializedDepositAssignmentsSettingPath, value, blockNumber, treeNodes, opts) +} + +// Current fee taken from user deposits +func GetDepositFee(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*big.Int, error) { + depositSettingsContract, err := getDepositSettingsContract(rp, opts) + if err != nil { + return nil, err + } + value := new(*big.Int) + if err := depositSettingsContract.Call(opts, value, "getDepositFee"); err != nil { + return nil, fmt.Errorf("error getting deposit fee: %w", err) + } + return *value, nil +} +func ProposeDepositFee(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return protocol.ProposeSetUint(rp, fmt.Sprintf("set %s", DepositFeeSettingPath), DepositSettingsContractName, DepositFeeSettingPath, value, blockNumber, treeNodes, opts) +} +func EstimateProposeDepositFeeGas(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return protocol.EstimateProposeSetUintGas(rp, fmt.Sprintf("set %s", DepositFeeSettingPath), DepositSettingsContractName, DepositFeeSettingPath, value, blockNumber, treeNodes, opts) +} + +func GetExpressQueueRate(rp *rocketpool.RocketPool, opts *bind.CallOpts) (uint64, error) { + depositSettingsContract, err := getDepositSettingsContract(rp, opts) + if err != nil { + return 0, err + } + value := new(*big.Int) + if err := depositSettingsContract.Call(opts, value, "getExpressQueueRate"); err != nil { + return 0, fmt.Errorf("error getting deposit queue rate: %w", err) + } + return (*value).Uint64(), nil +} + +func ProposeExpressQueueRate(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return protocol.ProposeSetUint(rp, fmt.Sprintf("set %s", ExpressQueueRatePath), DepositSettingsContractName, ExpressQueueRatePath, value, blockNumber, treeNodes, opts) +} +func EstimateProposeExpressQueueRateGas(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return protocol.EstimateProposeSetUintGas(rp, fmt.Sprintf("set %s", ExpressQueueRatePath), DepositSettingsContractName, ExpressQueueRatePath, value, blockNumber, treeNodes, opts) +} + +func GetExpressQueueTicketsBaseProvision(rp *rocketpool.RocketPool, opts *bind.CallOpts) (uint64, error) { + depositSettingsContract, err := getDepositSettingsContract(rp, opts) + if err != nil { + return 0, err + } + value := new(*big.Int) + if err := depositSettingsContract.Call(opts, value, "getExpressQueueTicketsBaseProvision"); err != nil { + return 0, fmt.Errorf("error getting express queue tickets base provision: %w", err) + } + return (*value).Uint64(), nil +} + +func ProposeExpressQueueTicketsBaseProvision(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return protocol.ProposeSetUint(rp, fmt.Sprintf("set %s", ExpressQueueTicketsBaseProvisionPath), DepositSettingsContractName, ExpressQueueTicketsBaseProvisionPath, value, blockNumber, treeNodes, opts) +} +func EstimateProposeExpressQueueTicketsBaseProvisionGas(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return protocol.EstimateProposeSetUintGas(rp, fmt.Sprintf("set %s", ExpressQueueTicketsBaseProvisionPath), DepositSettingsContractName, ExpressQueueTicketsBaseProvisionPath, value, blockNumber, treeNodes, opts) +} + +// Get contracts +var depositSettingsContractLock sync.Mutex + +func getDepositSettingsContract(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*rocketpool.Contract, error) { + depositSettingsContractLock.Lock() + defer depositSettingsContractLock.Unlock() + return rp.GetContract(DepositSettingsContractName, opts) +} diff --git a/bindings/settings/protocol/inflation.go b/bindings/settings/protocol/inflation.go new file mode 100644 index 000000000..6c279df6b --- /dev/null +++ b/bindings/settings/protocol/inflation.go @@ -0,0 +1,65 @@ +package protocol + +import ( + "fmt" + "math/big" + "sync" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/utils/eth" +) + +// Config +const ( + InflationSettingsContractName string = "rocketDAOProtocolSettingsInflation" +) + +// RPL inflation rate per interval +func GetInflationIntervalRate(rp *rocketpool.RocketPool, opts *bind.CallOpts) (float64, error) { + inflationSettingsContract, err := getInflationSettingsContract(rp, opts) + if err != nil { + return 0, err + } + value := new(*big.Int) + if err := inflationSettingsContract.Call(opts, value, "getInflationIntervalRate"); err != nil { + return 0, fmt.Errorf("error getting inflation rate: %w", err) + } + return eth.WeiToEth(*value), nil +} + +// RPL inflation rate per interval +func GetInflationIntervalRateRaw(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*big.Int, error) { + inflationSettingsContract, err := getInflationSettingsContract(rp, opts) + if err != nil { + return nil, err + } + value := new(*big.Int) + if err := inflationSettingsContract.Call(opts, value, "getInflationIntervalRate"); err != nil { + return nil, fmt.Errorf("error getting inflation rate: %w", err) + } + return *value, nil +} + +// RPL inflation start time +func GetInflationStartTime(rp *rocketpool.RocketPool, opts *bind.CallOpts) (uint64, error) { + inflationSettingsContract, err := getInflationSettingsContract(rp, opts) + if err != nil { + return 0, err + } + value := new(*big.Int) + if err := inflationSettingsContract.Call(opts, value, "getInflationIntervalStartTime"); err != nil { + return 0, fmt.Errorf("error getting inflation start time: %w", err) + } + return (*value).Uint64(), nil +} + +// Get contracts +var inflationSettingsContractLock sync.Mutex + +func getInflationSettingsContract(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*rocketpool.Contract, error) { + inflationSettingsContractLock.Lock() + defer inflationSettingsContractLock.Unlock() + return rp.GetContract(InflationSettingsContractName, opts) +} diff --git a/bindings/settings/protocol/megapool.go b/bindings/settings/protocol/megapool.go new file mode 100644 index 000000000..932ee169b --- /dev/null +++ b/bindings/settings/protocol/megapool.go @@ -0,0 +1,38 @@ +package protocol + +import ( + "fmt" + "math/big" + "sync" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + + "github.com/rocket-pool/smartnode/bindings/rocketpool" +) + +// Config +const ( + MegapoolSettingsContractName string = "rocketDAOProtocolSettingsMegapool" +) + +// Megapool time before dissolve +func GetMegapoolTimeBeforeDissolve(rp *rocketpool.RocketPool, opts *bind.CallOpts) (uint64, error) { + megapoolSettingsContract, err := getMegapoolSettingsContract(rp, opts) + if err != nil { + return 0, err + } + value := new(*big.Int) + if err := megapoolSettingsContract.Call(opts, value, "getTimeBeforeDissolve"); err != nil { + return 0, fmt.Errorf("error getting megapool time before dissolve value: %w", err) + } + return (*value).Uint64(), nil +} + +// Get contracts +var megapoolSettingsContractLock sync.Mutex + +func getMegapoolSettingsContract(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*rocketpool.Contract, error) { + megapoolSettingsContractLock.Lock() + defer megapoolSettingsContractLock.Unlock() + return rp.GetContract(MegapoolSettingsContractName, opts) +} diff --git a/bindings/settings/protocol/minipool.go b/bindings/settings/protocol/minipool.go new file mode 100644 index 000000000..f889e0e6c --- /dev/null +++ b/bindings/settings/protocol/minipool.go @@ -0,0 +1,163 @@ +package protocol + +import ( + "fmt" + "math/big" + "sync" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + + "github.com/rocket-pool/smartnode/bindings/dao/protocol" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/types" +) + +// Config +const ( + MinipoolSettingsContractName string = "rocketDAOProtocolSettingsMinipool" + MinipoolSubmitWithdrawableEnabledSettingPath string = "minipool.submit.withdrawable.enabled" + MinipoolLaunchTimeoutSettingPath string = "minipool.launch.timeout" + BondReductionEnabledSettingPath string = "minipool.bond.reduction.enabled" + MaximumMinipoolCountSettingPath string = "minipool.maximum.count" + MinipoolUserDistributeWindowStartSettingPath string = "minipool.user.distribute.window.start" + MinipoolUserDistributeWindowLengthSettingPath string = "minipool.user.distribute.window.length" +) + +// Minipool withdrawable event submissions currently enabled +func GetMinipoolSubmitWithdrawableEnabled(rp *rocketpool.RocketPool, opts *bind.CallOpts) (bool, error) { + minipoolSettingsContract, err := getMinipoolSettingsContract(rp, opts) + if err != nil { + return false, err + } + value := new(bool) + if err := minipoolSettingsContract.Call(opts, value, "getSubmitWithdrawableEnabled"); err != nil { + return false, fmt.Errorf("error getting minipool withdrawable submissions enabled status: %w", err) + } + return *value, nil +} +func ProposeMinipoolSubmitWithdrawableEnabled(rp *rocketpool.RocketPool, value bool, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return protocol.ProposeSetBool(rp, fmt.Sprintf("set %s", MinipoolSubmitWithdrawableEnabledSettingPath), MinipoolSettingsContractName, MinipoolSubmitWithdrawableEnabledSettingPath, value, blockNumber, treeNodes, opts) +} +func EstimateProposeMinipoolSubmitWithdrawableEnabledGas(rp *rocketpool.RocketPool, value bool, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return protocol.EstimateProposeSetBoolGas(rp, fmt.Sprintf("set %s", MinipoolSubmitWithdrawableEnabledSettingPath), MinipoolSettingsContractName, MinipoolSubmitWithdrawableEnabledSettingPath, value, blockNumber, treeNodes, opts) +} + +// Timeout period in seconds for prelaunch minipools to launch +func GetMinipoolLaunchTimeout(rp *rocketpool.RocketPool, opts *bind.CallOpts) (time.Duration, error) { + minipoolSettingsContract, err := getMinipoolSettingsContract(rp, opts) + if err != nil { + return 0, err + } + value := new(*big.Int) + if err := minipoolSettingsContract.Call(opts, value, "getLaunchTimeout"); err != nil { + return 0, fmt.Errorf("error getting minipool launch timeout: %w", err) + } + seconds := time.Duration((*value).Int64()) * time.Second + return seconds, nil +} +func ProposeMinipoolLaunchTimeout(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return protocol.ProposeSetUint(rp, fmt.Sprintf("set %s", MinipoolLaunchTimeoutSettingPath), MinipoolSettingsContractName, MinipoolLaunchTimeoutSettingPath, value, blockNumber, treeNodes, opts) +} +func EstimateProposeMinipoolLaunchTimeoutGas(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return protocol.EstimateProposeSetUintGas(rp, fmt.Sprintf("set %s", MinipoolLaunchTimeoutSettingPath), MinipoolSettingsContractName, MinipoolLaunchTimeoutSettingPath, value, blockNumber, treeNodes, opts) +} + +// Timeout period in seconds for prelaunch minipools to launch +func GetMinipoolLaunchTimeoutRaw(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*big.Int, error) { + minipoolSettingsContract, err := getMinipoolSettingsContract(rp, opts) + if err != nil { + return nil, err + } + value := new(*big.Int) + if err := minipoolSettingsContract.Call(opts, value, "getLaunchTimeout"); err != nil { + return nil, fmt.Errorf("error getting minipool launch timeout: %w", err) + } + return *value, nil +} + +// Minipool bond reductions currently enabled +func GetBondReductionEnabled(rp *rocketpool.RocketPool, opts *bind.CallOpts) (bool, error) { + minipoolSettingsContract, err := getMinipoolSettingsContract(rp, opts) + if err != nil { + return false, err + } + value := new(bool) + if err := minipoolSettingsContract.Call(opts, value, "getBondReductionEnabled"); err != nil { + return false, fmt.Errorf("error getting bond reduction enabled status: %w", err) + } + return *value, nil +} +func ProposeBondReductionEnabled(rp *rocketpool.RocketPool, value bool, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return protocol.ProposeSetBool(rp, fmt.Sprintf("set %s", BondReductionEnabledSettingPath), MinipoolSettingsContractName, BondReductionEnabledSettingPath, value, blockNumber, treeNodes, opts) +} +func EstimateProposeBondReductionEnabledGas(rp *rocketpool.RocketPool, value bool, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return protocol.EstimateProposeSetBoolGas(rp, fmt.Sprintf("set %s", BondReductionEnabledSettingPath), MinipoolSettingsContractName, BondReductionEnabledSettingPath, value, blockNumber, treeNodes, opts) +} + +// The maximum number of minipools allowed +func GetMaximumMinipoolCount(rp *rocketpool.RocketPool, opts *bind.CallOpts) (uint64, error) { + minipoolSettingsContract, err := getMinipoolSettingsContract(rp, opts) + if err != nil { + return 0, err + } + value := new(*big.Int) + if err := minipoolSettingsContract.Call(opts, value, "getMaximumCount"); err != nil { + return 0, fmt.Errorf("error getting maximum minipool count: %w", err) + } + return (*value).Uint64(), nil +} +func ProposeMaximumMinipoolCount(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return protocol.ProposeSetUint(rp, fmt.Sprintf("set %s", MaximumMinipoolCountSettingPath), MinipoolSettingsContractName, MaximumMinipoolCountSettingPath, value, blockNumber, treeNodes, opts) +} +func EstimateProposeMaximumMinipoolCountGas(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return protocol.EstimateProposeSetUintGas(rp, fmt.Sprintf("set %s", MaximumMinipoolCountSettingPath), MinipoolSettingsContractName, MaximumMinipoolCountSettingPath, value, blockNumber, treeNodes, opts) +} + +// The time a user must wait before being able to distribute a minipool +func GetMinipoolUserDistributeWindowStart(rp *rocketpool.RocketPool, opts *bind.CallOpts) (time.Duration, error) { + minipoolSettingsContract, err := getMinipoolSettingsContract(rp, opts) + if err != nil { + return 0, err + } + value := new(*big.Int) + if err := minipoolSettingsContract.Call(opts, value, "getUserDistributeWindowStart"); err != nil { + return 0, fmt.Errorf("error getting user distribute window start: %w", err) + } + return time.Duration((*value).Uint64()) * time.Second, nil +} +func ProposeMinipoolUserDistributeWindowStart(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return protocol.ProposeSetUint(rp, fmt.Sprintf("set %s", MinipoolUserDistributeWindowStartSettingPath), MinipoolSettingsContractName, MinipoolUserDistributeWindowStartSettingPath, value, blockNumber, treeNodes, opts) +} +func EstimateProposeMinipoolUserDistributeWindowStartGas(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return protocol.EstimateProposeSetUintGas(rp, fmt.Sprintf("set %s", MinipoolUserDistributeWindowStartSettingPath), MinipoolSettingsContractName, MinipoolUserDistributeWindowStartSettingPath, value, blockNumber, treeNodes, opts) +} + +// The time a user has to distribute a minipool after waiting the start length +func GetMinipoolUserDistributeWindowLength(rp *rocketpool.RocketPool, opts *bind.CallOpts) (time.Duration, error) { + minipoolSettingsContract, err := getMinipoolSettingsContract(rp, opts) + if err != nil { + return 0, err + } + value := new(*big.Int) + if err := minipoolSettingsContract.Call(opts, value, "getUserDistributeWindowLength"); err != nil { + return 0, fmt.Errorf("error getting user distribute window length: %w", err) + } + return time.Duration((*value).Uint64()) * time.Second, nil +} +func ProposeMinipoolUserDistributeWindowLength(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return protocol.ProposeSetUint(rp, fmt.Sprintf("set %s", MinipoolUserDistributeWindowLengthSettingPath), MinipoolSettingsContractName, MinipoolUserDistributeWindowLengthSettingPath, value, blockNumber, treeNodes, opts) +} +func EstimateProposeMinipoolUserDistributeWindowLengthGas(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return protocol.EstimateProposeSetUintGas(rp, fmt.Sprintf("set %s", MinipoolUserDistributeWindowLengthSettingPath), MinipoolSettingsContractName, MinipoolUserDistributeWindowLengthSettingPath, value, blockNumber, treeNodes, opts) +} + +// Get contracts +var minipoolSettingsContractLock sync.Mutex + +func getMinipoolSettingsContract(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*rocketpool.Contract, error) { + minipoolSettingsContractLock.Lock() + defer minipoolSettingsContractLock.Unlock() + return rp.GetContract(MinipoolSettingsContractName, opts) +} diff --git a/bindings/settings/protocol/network.go b/bindings/settings/protocol/network.go new file mode 100644 index 000000000..de82077e7 --- /dev/null +++ b/bindings/settings/protocol/network.go @@ -0,0 +1,382 @@ +package protocol + +import ( + "fmt" + "math/big" + "sync" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + + "github.com/rocket-pool/smartnode/bindings/dao/protocol" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/types" + "github.com/rocket-pool/smartnode/bindings/utils/eth" +) + +// Config +const ( + NetworkSettingsContractName string = "rocketDAOProtocolSettingsNetwork" + NodeConsensusThresholdSettingPath string = "network.consensus.threshold" + SubmitBalancesEnabledSettingPath string = "network.submit.balances.enabled" + SubmitBalancesFrequencySettingPath string = "network.submit.balances.frequency" + SubmitPricesEnabledSettingPath string = "network.submit.prices.enabled" + SubmitPricesFrequencySettingPath string = "network.submit.prices.frequency" + MinimumNodeFeeSettingPath string = "network.node.fee.minimum" + TargetNodeFeeSettingPath string = "network.node.fee.target" + MaximumNodeFeeSettingPath string = "network.node.fee.maximum" + NodeFeeDemandRangeSettingPath string = "network.node.fee.demand.range" + NodeComissionShareSecurityCouncilAdder string = "network.node.commission.share.security.council.adder" + TargetRethCollateralRateSettingPath string = "network.reth.collateral.target" + NetworkPenaltyThresholdSettingPath string = "network.penalty.threshold" + NetworkPenaltyPerRateSettingPath string = "network.penalty.per.rate" + SubmitRewardsEnabledSettingPath string = "network.submit.rewards.enabled" +) + +// The threshold of trusted nodes that must reach consensus on oracle data to commit it +func GetNodeConsensusThreshold(rp *rocketpool.RocketPool, opts *bind.CallOpts) (float64, error) { + networkSettingsContract, err := getNetworkSettingsContract(rp, opts) + if err != nil { + return 0, err + } + value := new(*big.Int) + if err := networkSettingsContract.Call(opts, value, "getNodeConsensusThreshold"); err != nil { + return 0, fmt.Errorf("error getting trusted node consensus threshold: %w", err) + } + return eth.WeiToEth(*value), nil +} + +// The threshold of trusted nodes that must reach consensus on oracle data to commit it +func GetNodeConsensusThresholdRaw(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*big.Int, error) { + networkSettingsContract, err := getNetworkSettingsContract(rp, opts) + if err != nil { + return nil, err + } + value := new(*big.Int) + if err := networkSettingsContract.Call(opts, value, "getNodeConsensusThreshold"); err != nil { + return nil, fmt.Errorf("error getting trusted node consensus threshold: %w", err) + } + return *value, nil +} +func ProposeNodeConsensusThreshold(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return protocol.ProposeSetUint(rp, fmt.Sprintf("set %s", NodeConsensusThresholdSettingPath), NetworkSettingsContractName, NodeConsensusThresholdSettingPath, value, blockNumber, treeNodes, opts) +} +func EstimateProposeNodeConsensusThresholdGas(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return protocol.EstimateProposeSetUintGas(rp, fmt.Sprintf("set %s", NodeConsensusThresholdSettingPath), NetworkSettingsContractName, NodeConsensusThresholdSettingPath, value, blockNumber, treeNodes, opts) +} + +// Network balance submissions currently enabled +func GetSubmitBalancesEnabled(rp *rocketpool.RocketPool, opts *bind.CallOpts) (bool, error) { + networkSettingsContract, err := getNetworkSettingsContract(rp, opts) + if err != nil { + return false, err + } + value := new(bool) + if err := networkSettingsContract.Call(opts, value, "getSubmitBalancesEnabled"); err != nil { + return false, fmt.Errorf("error getting network balance submissions enabled status: %w", err) + } + return *value, nil +} +func ProposeSubmitBalancesEnabled(rp *rocketpool.RocketPool, value bool, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return protocol.ProposeSetBool(rp, fmt.Sprintf("set %s", SubmitBalancesEnabledSettingPath), NetworkSettingsContractName, SubmitBalancesEnabledSettingPath, value, blockNumber, treeNodes, opts) +} +func EstimateProposeSubmitBalancesEnabledGas(rp *rocketpool.RocketPool, value bool, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return protocol.EstimateProposeSetBoolGas(rp, fmt.Sprintf("set %s", SubmitBalancesEnabledSettingPath), NetworkSettingsContractName, SubmitBalancesEnabledSettingPath, value, blockNumber, treeNodes, opts) +} + +// The frequency in seconds at which network balances should be submitted by trusted nodes +func GetSubmitBalancesFrequency(rp *rocketpool.RocketPool, opts *bind.CallOpts) (time.Duration, error) { + networkSettingsContract, err := getNetworkSettingsContract(rp, opts) + if err != nil { + return 0, err + } + value := new(*big.Int) + if err := networkSettingsContract.Call(opts, value, "getSubmitBalancesFrequency"); err != nil { + return 0, fmt.Errorf("error getting network balance submission frequency: %w", err) + } + return time.Duration((*value).Uint64()) * time.Second, nil +} +func ProposeSubmitBalancesFrequency(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return protocol.ProposeSetUint(rp, fmt.Sprintf("set %s", SubmitBalancesFrequencySettingPath), NetworkSettingsContractName, SubmitBalancesFrequencySettingPath, value, blockNumber, treeNodes, opts) +} +func EstimateProposeSubmitBalancesFrequencyGas(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return protocol.EstimateProposeSetUintGas(rp, fmt.Sprintf("set %s", SubmitBalancesFrequencySettingPath), NetworkSettingsContractName, SubmitBalancesFrequencySettingPath, value, blockNumber, treeNodes, opts) +} + +// Network price submissions currently enabled +func GetSubmitPricesEnabled(rp *rocketpool.RocketPool, opts *bind.CallOpts) (bool, error) { + networkSettingsContract, err := getNetworkSettingsContract(rp, opts) + if err != nil { + return false, err + } + value := new(bool) + if err := networkSettingsContract.Call(opts, value, "getSubmitPricesEnabled"); err != nil { + return false, fmt.Errorf("error getting network price submissions enabled status: %w", err) + } + return *value, nil +} +func ProposeSubmitPricesEnabled(rp *rocketpool.RocketPool, value bool, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return protocol.ProposeSetBool(rp, fmt.Sprintf("set %s", SubmitPricesEnabledSettingPath), NetworkSettingsContractName, SubmitPricesEnabledSettingPath, value, blockNumber, treeNodes, opts) +} +func EstimateProposeSubmitPricesEnabledGas(rp *rocketpool.RocketPool, value bool, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return protocol.EstimateProposeSetBoolGas(rp, fmt.Sprintf("set %s", SubmitPricesEnabledSettingPath), NetworkSettingsContractName, SubmitPricesEnabledSettingPath, value, blockNumber, treeNodes, opts) +} + +// The frequency in seconds at which network prices should be submitted by trusted nodes +func GetSubmitPricesFrequency(rp *rocketpool.RocketPool, opts *bind.CallOpts) (time.Duration, error) { + networkSettingsContract, err := getNetworkSettingsContract(rp, opts) + if err != nil { + return 0, err + } + value := new(*big.Int) + if err := networkSettingsContract.Call(opts, value, "getSubmitPricesFrequency"); err != nil { + return 0, fmt.Errorf("error getting network price submission frequency: %w", err) + } + return time.Duration((*value).Uint64()) * time.Second, nil +} +func ProposeSubmitPricesFrequency(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return protocol.ProposeSetUint(rp, fmt.Sprintf("set %s", SubmitPricesFrequencySettingPath), NetworkSettingsContractName, SubmitPricesFrequencySettingPath, value, blockNumber, treeNodes, opts) +} +func EstimateProposeSubmitPricesFrequencyGas(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return protocol.EstimateProposeSetUintGas(rp, fmt.Sprintf("set %s", SubmitPricesFrequencySettingPath), NetworkSettingsContractName, SubmitPricesFrequencySettingPath, value, blockNumber, treeNodes, opts) +} + +// Minimum node commission rate +func GetMinimumNodeFee(rp *rocketpool.RocketPool, opts *bind.CallOpts) (float64, error) { + networkSettingsContract, err := getNetworkSettingsContract(rp, opts) + if err != nil { + return 0, err + } + value := new(*big.Int) + if err := networkSettingsContract.Call(opts, value, "getMinimumNodeFee"); err != nil { + return 0, fmt.Errorf("error getting minimum node fee: %w", err) + } + return eth.WeiToEth(*value), nil +} + +// Minimum node commission rate +func GetMinimumNodeFeeRaw(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*big.Int, error) { + networkSettingsContract, err := getNetworkSettingsContract(rp, opts) + if err != nil { + return nil, err + } + value := new(*big.Int) + if err := networkSettingsContract.Call(opts, value, "getMinimumNodeFee"); err != nil { + return nil, fmt.Errorf("error getting minimum node fee: %w", err) + } + return *value, nil +} +func ProposeMinimumNodeFee(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return protocol.ProposeSetUint(rp, fmt.Sprintf("set %s", MinimumNodeFeeSettingPath), NetworkSettingsContractName, MinimumNodeFeeSettingPath, value, blockNumber, treeNodes, opts) +} +func EstimateProposeMinimumNodeFeeGas(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return protocol.EstimateProposeSetUintGas(rp, fmt.Sprintf("set %s", MinimumNodeFeeSettingPath), NetworkSettingsContractName, MinimumNodeFeeSettingPath, value, blockNumber, treeNodes, opts) +} + +// Target node commission rate +func GetTargetNodeFee(rp *rocketpool.RocketPool, opts *bind.CallOpts) (float64, error) { + networkSettingsContract, err := getNetworkSettingsContract(rp, opts) + if err != nil { + return 0, err + } + value := new(*big.Int) + if err := networkSettingsContract.Call(opts, value, "getTargetNodeFee"); err != nil { + return 0, fmt.Errorf("error getting target node fee: %w", err) + } + return eth.WeiToEth(*value), nil +} + +// Target node commission rate +func GetTargetNodeFeeRaw(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*big.Int, error) { + networkSettingsContract, err := getNetworkSettingsContract(rp, opts) + if err != nil { + return nil, err + } + value := new(*big.Int) + if err := networkSettingsContract.Call(opts, value, "getTargetNodeFee"); err != nil { + return nil, fmt.Errorf("error getting target node fee: %w", err) + } + return *value, nil +} +func ProposeTargetNodeFee(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return protocol.ProposeSetUint(rp, fmt.Sprintf("set %s", TargetNodeFeeSettingPath), NetworkSettingsContractName, TargetNodeFeeSettingPath, value, blockNumber, treeNodes, opts) +} +func EstimateProposeTargetNodeFeeGas(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return protocol.EstimateProposeSetUintGas(rp, fmt.Sprintf("set %s", TargetNodeFeeSettingPath), NetworkSettingsContractName, TargetNodeFeeSettingPath, value, blockNumber, treeNodes, opts) +} + +// Maximum node commission rate +func GetMaximumNodeFee(rp *rocketpool.RocketPool, opts *bind.CallOpts) (float64, error) { + networkSettingsContract, err := getNetworkSettingsContract(rp, opts) + if err != nil { + return 0, err + } + value := new(*big.Int) + if err := networkSettingsContract.Call(opts, value, "getMaximumNodeFee"); err != nil { + return 0, fmt.Errorf("error getting maximum node fee: %w", err) + } + return eth.WeiToEth(*value), nil +} + +// Maximum node commission rate +func GetMaximumNodeFeeRaw(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*big.Int, error) { + networkSettingsContract, err := getNetworkSettingsContract(rp, opts) + if err != nil { + return nil, err + } + value := new(*big.Int) + if err := networkSettingsContract.Call(opts, value, "getMaximumNodeFee"); err != nil { + return nil, fmt.Errorf("error getting maximum node fee: %w", err) + } + return *value, nil +} +func ProposeMaximumNodeFee(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return protocol.ProposeSetUint(rp, fmt.Sprintf("set %s", MaximumNodeFeeSettingPath), NetworkSettingsContractName, MaximumNodeFeeSettingPath, value, blockNumber, treeNodes, opts) +} +func EstimateProposeMaximumNodeFeeGas(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return protocol.EstimateProposeSetUintGas(rp, fmt.Sprintf("set %s", MaximumNodeFeeSettingPath), NetworkSettingsContractName, MaximumNodeFeeSettingPath, value, blockNumber, treeNodes, opts) +} + +// The range of node demand values to base fee calculations on +func GetNodeFeeDemandRange(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*big.Int, error) { + networkSettingsContract, err := getNetworkSettingsContract(rp, opts) + if err != nil { + return nil, err + } + value := new(*big.Int) + if err := networkSettingsContract.Call(opts, value, "getNodeFeeDemandRange"); err != nil { + return nil, fmt.Errorf("error getting node fee demand range: %w", err) + } + return *value, nil +} +func ProposeNodeFeeDemandRange(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return protocol.ProposeSetUint(rp, fmt.Sprintf("set %s", NodeFeeDemandRangeSettingPath), NetworkSettingsContractName, NodeFeeDemandRangeSettingPath, value, blockNumber, treeNodes, opts) +} +func EstimateProposeNodeFeeDemandRangeGas(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return protocol.EstimateProposeSetUintGas(rp, fmt.Sprintf("set %s", NodeFeeDemandRangeSettingPath), NetworkSettingsContractName, NodeFeeDemandRangeSettingPath, value, blockNumber, treeNodes, opts) +} + +// The target collateralization rate for the rETH contract as a fraction +func GetTargetRethCollateralRate(rp *rocketpool.RocketPool, opts *bind.CallOpts) (float64, error) { + networkSettingsContract, err := getNetworkSettingsContract(rp, opts) + if err != nil { + return 0, err + } + value := new(*big.Int) + if err := networkSettingsContract.Call(opts, value, "getTargetRethCollateralRate"); err != nil { + return 0, fmt.Errorf("error getting target rETH contract collateralization rate: %w", err) + } + return eth.WeiToEth(*value), nil +} + +// The target collateralization rate for the rETH contract as a fraction +func GetTargetRethCollateralRateRaw(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*big.Int, error) { + networkSettingsContract, err := getNetworkSettingsContract(rp, opts) + if err != nil { + return nil, err + } + value := new(*big.Int) + if err := networkSettingsContract.Call(opts, value, "getTargetRethCollateralRate"); err != nil { + return nil, fmt.Errorf("error getting target rETH contract collateralization rate: %w", err) + } + return *value, nil +} +func ProposeTargetRethCollateralRate(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return protocol.ProposeSetUint(rp, fmt.Sprintf("set %s", TargetRethCollateralRateSettingPath), NetworkSettingsContractName, TargetRethCollateralRateSettingPath, value, blockNumber, treeNodes, opts) +} +func EstimateProposeTargetRethCollateralRateGas(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return protocol.EstimateProposeSetUintGas(rp, fmt.Sprintf("set %s", TargetRethCollateralRateSettingPath), NetworkSettingsContractName, TargetRethCollateralRateSettingPath, value, blockNumber, treeNodes, opts) +} + +// The number of oDAO members that have to vote for a penalty expressed as a percentage +func GetNetworkPenaltyThreshold(rp *rocketpool.RocketPool, opts *bind.CallOpts) (float64, error) { + networkSettingsContract, err := getNetworkSettingsContract(rp, opts) + if err != nil { + return 0, err + } + value := new(*big.Int) + if err := networkSettingsContract.Call(opts, value, "getNodePenaltyThreshold"); err != nil { + return 0, fmt.Errorf("error getting network penalty threshold: %w", err) + } + return eth.WeiToEth(*value), nil +} + +// The number of oDAO members that have to vote for a penalty expressed as a percentage +func GetNetworkPenaltyThresholdRaw(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*big.Int, error) { + networkSettingsContract, err := getNetworkSettingsContract(rp, opts) + if err != nil { + return nil, err + } + value := new(*big.Int) + if err := networkSettingsContract.Call(opts, value, "getNodePenaltyThreshold"); err != nil { + return nil, fmt.Errorf("error getting network penalty threshold: %w", err) + } + return *value, nil +} +func ProposeNetworkPenaltyThreshold(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return protocol.ProposeSetUint(rp, fmt.Sprintf("set %s", NetworkPenaltyThresholdSettingPath), NetworkSettingsContractName, NetworkPenaltyThresholdSettingPath, value, blockNumber, treeNodes, opts) +} +func EstimateProposeNetworkPenaltyThresholdGas(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return protocol.EstimateProposeSetUintGas(rp, fmt.Sprintf("set %s", NetworkPenaltyThresholdSettingPath), NetworkSettingsContractName, NetworkPenaltyThresholdSettingPath, value, blockNumber, treeNodes, opts) +} + +// The amount a node operator is penalised for each penalty as a percentage +func GetNetworkPenaltyPerRate(rp *rocketpool.RocketPool, opts *bind.CallOpts) (float64, error) { + networkSettingsContract, err := getNetworkSettingsContract(rp, opts) + if err != nil { + return 0, err + } + value := new(*big.Int) + if err := networkSettingsContract.Call(opts, value, "getPerPenaltyRate"); err != nil { + return 0, fmt.Errorf("error getting network penalty per rate: %w", err) + } + return eth.WeiToEth(*value), nil +} + +// The amount a node operator is penalised for each penalty as a percentage +func GetNetworkPenaltyPerRateRaw(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*big.Int, error) { + networkSettingsContract, err := getNetworkSettingsContract(rp, opts) + if err != nil { + return nil, err + } + value := new(*big.Int) + if err := networkSettingsContract.Call(opts, value, "getPerPenaltyRate"); err != nil { + return nil, fmt.Errorf("error getting network penalty per rate: %w", err) + } + return *value, nil +} +func ProposeNetworkPenaltyPerRate(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return protocol.ProposeSetUint(rp, fmt.Sprintf("set %s", NetworkPenaltyPerRateSettingPath), NetworkSettingsContractName, NetworkPenaltyPerRateSettingPath, value, blockNumber, treeNodes, opts) +} +func EstimateProposeNetworkPenaltyPerRateGas(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return protocol.EstimateProposeSetUintGas(rp, fmt.Sprintf("set %s", NetworkPenaltyPerRateSettingPath), NetworkSettingsContractName, NetworkPenaltyPerRateSettingPath, value, blockNumber, treeNodes, opts) +} + +// Rewards submissions currently enabled +func GetSubmitRewardsEnabled(rp *rocketpool.RocketPool, opts *bind.CallOpts) (bool, error) { + networkSettingsContract, err := getNetworkSettingsContract(rp, opts) + if err != nil { + return false, err + } + value := new(bool) + if err := networkSettingsContract.Call(opts, value, "getSubmitRewardsEnabled"); err != nil { + return false, fmt.Errorf("error getting rewards submissions enabled status: %w", err) + } + return *value, nil +} +func ProposeSubmitRewardsEnabled(rp *rocketpool.RocketPool, value bool, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return protocol.ProposeSetBool(rp, fmt.Sprintf("set %s", SubmitRewardsEnabledSettingPath), NetworkSettingsContractName, SubmitRewardsEnabledSettingPath, value, blockNumber, treeNodes, opts) +} +func EstimateProposeSubmitRewardsEnabledGas(rp *rocketpool.RocketPool, value bool, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return protocol.EstimateProposeSetBoolGas(rp, fmt.Sprintf("set %s", SubmitRewardsEnabledSettingPath), NetworkSettingsContractName, SubmitRewardsEnabledSettingPath, value, blockNumber, treeNodes, opts) +} + +// Get contracts +var networkSettingsContractLock sync.Mutex + +func getNetworkSettingsContract(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*rocketpool.Contract, error) { + networkSettingsContractLock.Lock() + defer networkSettingsContractLock.Unlock() + return rp.GetContract(NetworkSettingsContractName, opts) +} diff --git a/bindings/settings/protocol/node.go b/bindings/settings/protocol/node.go new file mode 100644 index 000000000..fc964aedc --- /dev/null +++ b/bindings/settings/protocol/node.go @@ -0,0 +1,175 @@ +package protocol + +import ( + "fmt" + "math/big" + "sync" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + + "github.com/rocket-pool/smartnode/bindings/dao/protocol" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/types" + "github.com/rocket-pool/smartnode/bindings/utils/eth" +) + +// Config +const ( + NodeSettingsContractName string = "rocketDAOProtocolSettingsNode" + NodeRegistrationEnabledSettingPath string = "node.registration.enabled" + SmoothingPoolRegistrationEnabledSettingPath string = "node.smoothing.pool.registration.enabled" + NodeDepositEnabledSettingPath string = "node.deposit.enabled" + VacantMinipoolsEnabledSettingPath string = "node.vacant.minipools.enabled" + MinimumPerMinipoolStakeSettingPath string = "node.per.minipool.stake.minimum" + MaximumPerMinipoolStakeSettingPath string = "node.per.minipool.stake.maximum" +) + +// Node registrations currently enabled +func GetNodeRegistrationEnabled(rp *rocketpool.RocketPool, opts *bind.CallOpts) (bool, error) { + nodeSettingsContract, err := getNodeSettingsContract(rp, opts) + if err != nil { + return false, err + } + value := new(bool) + if err := nodeSettingsContract.Call(opts, value, "getRegistrationEnabled"); err != nil { + return false, fmt.Errorf("error getting node registrations enabled status: %w", err) + } + return *value, nil +} +func ProposeNodeRegistrationEnabled(rp *rocketpool.RocketPool, value bool, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return protocol.ProposeSetBool(rp, fmt.Sprintf("set %s", NodeRegistrationEnabledSettingPath), NodeSettingsContractName, NodeRegistrationEnabledSettingPath, value, blockNumber, treeNodes, opts) +} +func EstimateProposeNodeRegistrationEnabledGas(rp *rocketpool.RocketPool, value bool, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return protocol.EstimateProposeSetBoolGas(rp, fmt.Sprintf("set %s", NodeRegistrationEnabledSettingPath), NodeSettingsContractName, NodeRegistrationEnabledSettingPath, value, blockNumber, treeNodes, opts) +} + +// Smoothing pool joining currently enabled +func GetSmoothingPoolRegistrationEnabled(rp *rocketpool.RocketPool, opts *bind.CallOpts) (bool, error) { + nodeSettingsContract, err := getNodeSettingsContract(rp, opts) + if err != nil { + return false, err + } + value := new(bool) + if err := nodeSettingsContract.Call(opts, value, "getSmoothingPoolRegistrationEnabled"); err != nil { + return false, fmt.Errorf("error getting smoothing pool registrations enabled status: %w", err) + } + return *value, nil +} +func ProposeSmoothingPoolRegistrationEnabled(rp *rocketpool.RocketPool, value bool, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return protocol.ProposeSetBool(rp, fmt.Sprintf("set %s", SmoothingPoolRegistrationEnabledSettingPath), NodeSettingsContractName, SmoothingPoolRegistrationEnabledSettingPath, value, blockNumber, treeNodes, opts) +} +func EstimateProposeSmoothingPoolRegistrationEnabledGas(rp *rocketpool.RocketPool, value bool, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return protocol.EstimateProposeSetBoolGas(rp, fmt.Sprintf("set %s", SmoothingPoolRegistrationEnabledSettingPath), NodeSettingsContractName, SmoothingPoolRegistrationEnabledSettingPath, value, blockNumber, treeNodes, opts) +} + +// Node deposits currently enabled +func GetNodeDepositEnabled(rp *rocketpool.RocketPool, opts *bind.CallOpts) (bool, error) { + nodeSettingsContract, err := getNodeSettingsContract(rp, opts) + if err != nil { + return false, err + } + value := new(bool) + if err := nodeSettingsContract.Call(opts, value, "getDepositEnabled"); err != nil { + return false, fmt.Errorf("error getting node deposits enabled status: %w", err) + } + return *value, nil +} +func ProposeNodeDepositEnabled(rp *rocketpool.RocketPool, value bool, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return protocol.ProposeSetBool(rp, fmt.Sprintf("set %s", NodeDepositEnabledSettingPath), NodeSettingsContractName, NodeDepositEnabledSettingPath, value, blockNumber, treeNodes, opts) +} +func EstimateProposeNodeDepositEnabledGas(rp *rocketpool.RocketPool, value bool, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return protocol.EstimateProposeSetBoolGas(rp, fmt.Sprintf("set %s", NodeDepositEnabledSettingPath), NodeSettingsContractName, NodeDepositEnabledSettingPath, value, blockNumber, treeNodes, opts) +} + +// Vacant minipools currently enabled +func GetVacantMinipoolsEnabled(rp *rocketpool.RocketPool, opts *bind.CallOpts) (bool, error) { + nodeSettingsContract, err := getNodeSettingsContract(rp, opts) + if err != nil { + return false, err + } + value := new(bool) + if err := nodeSettingsContract.Call(opts, value, "getVacantMinipoolsEnabled"); err != nil { + return false, fmt.Errorf("error getting vacant minipools enabled status: %w", err) + } + return *value, nil +} +func ProposeVacantMinipoolsEnabled(rp *rocketpool.RocketPool, value bool, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return protocol.ProposeSetBool(rp, fmt.Sprintf("set %s", VacantMinipoolsEnabledSettingPath), NodeSettingsContractName, VacantMinipoolsEnabledSettingPath, value, blockNumber, treeNodes, opts) +} +func EstimateProposeVacantMinipoolsEnabledGas(rp *rocketpool.RocketPool, value bool, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return protocol.EstimateProposeSetBoolGas(rp, fmt.Sprintf("set %s", VacantMinipoolsEnabledSettingPath), NodeSettingsContractName, VacantMinipoolsEnabledSettingPath, value, blockNumber, treeNodes, opts) +} + +// The minimum RPL stake per minipool as a fraction of assigned user ETH +func GetMinimumPerMinipoolStake(rp *rocketpool.RocketPool, opts *bind.CallOpts) (float64, error) { + nodeSettingsContract, err := getNodeSettingsContract(rp, opts) + if err != nil { + return 0, err + } + value := new(*big.Int) + if err := nodeSettingsContract.Call(opts, value, "getMinimumPerMinipoolStake"); err != nil { + return 0, fmt.Errorf("error getting minimum RPL stake per minipool: %w", err) + } + return eth.WeiToEth(*value), nil +} +func ProposeMinimumPerMinipoolStake(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return protocol.ProposeSetUint(rp, fmt.Sprintf("set %s", MinimumPerMinipoolStakeSettingPath), NodeSettingsContractName, MinimumPerMinipoolStakeSettingPath, value, blockNumber, treeNodes, opts) +} +func EstimateProposeMinimumPerMinipoolStakeGas(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return protocol.EstimateProposeSetUintGas(rp, fmt.Sprintf("set %s", MinimumPerMinipoolStakeSettingPath), NodeSettingsContractName, MinimumPerMinipoolStakeSettingPath, value, blockNumber, treeNodes, opts) +} + +// The minimum RPL stake per minipool as a fraction of assigned user ETH +func GetMinimumPerMinipoolStakeRaw(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*big.Int, error) { + nodeSettingsContract, err := getNodeSettingsContract(rp, opts) + if err != nil { + return nil, err + } + value := new(*big.Int) + if err := nodeSettingsContract.Call(opts, value, "getMinimumPerMinipoolStake"); err != nil { + return nil, fmt.Errorf("error getting minimum RPL stake per minipool: %w", err) + } + return *value, nil +} + +// The maximum RPL stake per minipool as a fraction of assigned user ETH +func GetMaximumPerMinipoolStake(rp *rocketpool.RocketPool, opts *bind.CallOpts) (float64, error) { + nodeSettingsContract, err := getNodeSettingsContract(rp, opts) + if err != nil { + return 0, err + } + value := new(*big.Int) + if err := nodeSettingsContract.Call(opts, value, "getMaximumPerMinipoolStake"); err != nil { + return 0, fmt.Errorf("error getting maximum RPL stake per minipool: %w", err) + } + return eth.WeiToEth(*value), nil +} +func ProposeMaximumPerMinipoolStake(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return protocol.ProposeSetUint(rp, fmt.Sprintf("set %s", MaximumPerMinipoolStakeSettingPath), NodeSettingsContractName, MaximumPerMinipoolStakeSettingPath, value, blockNumber, treeNodes, opts) +} +func EstimateProposeMaximumPerMinipoolStakeGas(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return protocol.EstimateProposeSetUintGas(rp, fmt.Sprintf("set %s", MaximumPerMinipoolStakeSettingPath), NodeSettingsContractName, MaximumPerMinipoolStakeSettingPath, value, blockNumber, treeNodes, opts) +} + +// The maximum RPL stake per minipool as a fraction of assigned user ETH +func GetMaximumPerMinipoolStakeRaw(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*big.Int, error) { + nodeSettingsContract, err := getNodeSettingsContract(rp, opts) + if err != nil { + return nil, err + } + value := new(*big.Int) + if err := nodeSettingsContract.Call(opts, value, "getMaximumPerMinipoolStake"); err != nil { + return nil, fmt.Errorf("error getting maximum RPL stake per minipool: %w", err) + } + return *value, nil +} + +// Get contracts +var nodeSettingsContractLock sync.Mutex + +func getNodeSettingsContract(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*rocketpool.Contract, error) { + nodeSettingsContractLock.Lock() + defer nodeSettingsContractLock.Unlock() + return rp.GetContract(NodeSettingsContractName, opts) +} diff --git a/bindings/settings/protocol/proposals.go b/bindings/settings/protocol/proposals.go new file mode 100644 index 000000000..5d925ba42 --- /dev/null +++ b/bindings/settings/protocol/proposals.go @@ -0,0 +1,255 @@ +package protocol + +import ( + "fmt" + "math/big" + "sync" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/rocket-pool/smartnode/bindings/dao/protocol" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/types" + "github.com/rocket-pool/smartnode/bindings/utils/eth" +) + +// Config +const ( + ProposalsSettingsContractName string = "rocketDAOProtocolSettingsProposals" + VotePhase1TimeSettingPath string = "proposal.vote.phase1.time" + VotePhase2TimeSettingPath string = "proposal.vote.phase2.time" + VoteDelayTimeSettingPath string = "proposal.vote.delay.time" + ExecuteTimeSettingPath string = "proposal.execute.time" + ProposalBondSettingPath string = "proposal.bond" + ChallengeBondSettingPath string = "proposal.challenge.bond" + ChallengePeriodSettingPath string = "proposal.challenge.period" + ProposalQuorumSettingPath string = "proposal.quorum" + ProposalVetoQuorumSettingPath string = "proposal.veto.quorum" + ProposalMaxBlockAgeSettingPath string = "proposal.max.block.age" +) + +// How long a proposal can be voted on phase 1 +func GetVotePhase1Time(rp *rocketpool.RocketPool, opts *bind.CallOpts) (time.Duration, error) { + proposalsSettingsContract, err := getProposalsSettingsContract(rp, opts) + if err != nil { + return 0, err + } + value := new(*big.Int) + if err := proposalsSettingsContract.Call(opts, value, "getVotePhase1Time"); err != nil { + return 0, fmt.Errorf("error getting vote time: %w", err) + } + return time.Duration((*value).Uint64()) * time.Second, nil +} +func ProposeVotePhase1Time(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return protocol.ProposeSetUint(rp, fmt.Sprintf("set %s", VotePhase1TimeSettingPath), ProposalsSettingsContractName, VotePhase1TimeSettingPath, value, blockNumber, treeNodes, opts) +} +func EstimateProposeVotePhase1TimeGas(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return protocol.EstimateProposeSetUintGas(rp, fmt.Sprintf("set %s", VotePhase1TimeSettingPath), ProposalsSettingsContractName, VotePhase1TimeSettingPath, value, blockNumber, treeNodes, opts) +} + +// How long a proposal can be voted on phase 2 +func GetVotePhase2Time(rp *rocketpool.RocketPool, opts *bind.CallOpts) (time.Duration, error) { + proposalsSettingsContract, err := getProposalsSettingsContract(rp, opts) + if err != nil { + return 0, err + } + value := new(*big.Int) + if err := proposalsSettingsContract.Call(opts, value, "getVotePhase2Time"); err != nil { + return 0, fmt.Errorf("error getting vote time: %w", err) + } + return time.Duration((*value).Uint64()) * time.Second, nil +} +func ProposeVotePhase2Time(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return protocol.ProposeSetUint(rp, fmt.Sprintf("set %s", VotePhase2TimeSettingPath), ProposalsSettingsContractName, VotePhase2TimeSettingPath, value, blockNumber, treeNodes, opts) +} +func EstimateProposeVotePhase2TimeGas(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return protocol.EstimateProposeSetUintGas(rp, fmt.Sprintf("set %s", VotePhase2TimeSettingPath), ProposalsSettingsContractName, VotePhase2TimeSettingPath, value, blockNumber, treeNodes, opts) +} + +// How long before a proposal can be voted on after its created +func GetVoteDelayTime(rp *rocketpool.RocketPool, opts *bind.CallOpts) (time.Duration, error) { + proposalsSettingsContract, err := getProposalsSettingsContract(rp, opts) + if err != nil { + return 0, err + } + value := new(*big.Int) + if err := proposalsSettingsContract.Call(opts, value, "getVoteDelayTime"); err != nil { + return 0, fmt.Errorf("error getting vote delay time: %w", err) + } + return time.Duration((*value).Uint64()) * time.Second, nil +} +func ProposeVoteDelayTime(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return protocol.ProposeSetUint(rp, fmt.Sprintf("set %s", VoteDelayTimeSettingPath), ProposalsSettingsContractName, VoteDelayTimeSettingPath, value, blockNumber, treeNodes, opts) +} +func EstimateProposeVoteDelayTimeGas(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return protocol.EstimateProposeSetUintGas(rp, fmt.Sprintf("set %s", VoteDelayTimeSettingPath), ProposalsSettingsContractName, VoteDelayTimeSettingPath, value, blockNumber, treeNodes, opts) +} + +// How long after a succesful proposal can it be executed before it expires +func GetExecuteTime(rp *rocketpool.RocketPool, opts *bind.CallOpts) (time.Duration, error) { + proposalsSettingsContract, err := getProposalsSettingsContract(rp, opts) + if err != nil { + return 0, err + } + value := new(*big.Int) + if err := proposalsSettingsContract.Call(opts, value, "getExecuteTime"); err != nil { + return 0, fmt.Errorf("error getting execute time: %w", err) + } + return time.Duration((*value).Uint64()) * time.Second, nil +} +func ProposeExecuteTime(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return protocol.ProposeSetUint(rp, fmt.Sprintf("set %s", ExecuteTimeSettingPath), ProposalsSettingsContractName, ExecuteTimeSettingPath, value, blockNumber, treeNodes, opts) +} +func EstimateProposeExecuteTimeGas(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return protocol.EstimateProposeSetUintGas(rp, fmt.Sprintf("set %s", ExecuteTimeSettingPath), ProposalsSettingsContractName, ExecuteTimeSettingPath, value, blockNumber, treeNodes, opts) +} + +// How much RPL is locked when creating a proposal +func GetProposalBond(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*big.Int, error) { + proposalsSettingsContract, err := getProposalsSettingsContract(rp, opts) + if err != nil { + return nil, err + } + value := new(*big.Int) + if err := proposalsSettingsContract.Call(opts, value, "getProposalBond"); err != nil { + return nil, fmt.Errorf("error getting proposal bond: %w", err) + } + return *value, nil +} +func ProposeProposalBond(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return protocol.ProposeSetUint(rp, fmt.Sprintf("set %s", ProposalBondSettingPath), ProposalsSettingsContractName, ProposalBondSettingPath, value, blockNumber, treeNodes, opts) +} +func EstimateProposeProposalBondGas(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return protocol.EstimateProposeSetUintGas(rp, fmt.Sprintf("set %s", ProposalBondSettingPath), ProposalsSettingsContractName, ProposalBondSettingPath, value, blockNumber, treeNodes, opts) +} + +// How much RPL is locked when challenging a proposal +func GetChallengeBond(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*big.Int, error) { + proposalsSettingsContract, err := getProposalsSettingsContract(rp, opts) + if err != nil { + return nil, err + } + value := new(*big.Int) + if err := proposalsSettingsContract.Call(opts, value, "getChallengeBond"); err != nil { + return nil, fmt.Errorf("error getting challenge bond: %w", err) + } + return *value, nil +} +func ProposeChallengeBond(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return protocol.ProposeSetUint(rp, fmt.Sprintf("set %s", ChallengeBondSettingPath), ProposalsSettingsContractName, ChallengeBondSettingPath, value, blockNumber, treeNodes, opts) +} +func EstimateProposeChallengeBondGas(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return protocol.EstimateProposeSetUintGas(rp, fmt.Sprintf("set %s", ChallengeBondSettingPath), ProposalsSettingsContractName, ChallengeBondSettingPath, value, blockNumber, treeNodes, opts) +} + +// How long a proposer has to respond to a challenge before the proposal is defeated +func GetChallengePeriod(rp *rocketpool.RocketPool, opts *bind.CallOpts) (time.Duration, error) { + proposalsSettingsContract, err := getProposalsSettingsContract(rp, opts) + if err != nil { + return 0, err + } + value := new(*big.Int) + if err := proposalsSettingsContract.Call(opts, value, "getChallengePeriod"); err != nil { + return 0, fmt.Errorf("error getting challenge period: %w", err) + } + return time.Duration((*value).Uint64()) * time.Second, nil +} +func ProposeChallengePeriod(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return protocol.ProposeSetUint(rp, fmt.Sprintf("set %s", ChallengePeriodSettingPath), ProposalsSettingsContractName, ChallengePeriodSettingPath, value, blockNumber, treeNodes, opts) +} +func EstimateProposeChallengePeriodGas(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return protocol.EstimateProposeSetUintGas(rp, fmt.Sprintf("set %s", ChallengePeriodSettingPath), ProposalsSettingsContractName, ChallengePeriodSettingPath, value, blockNumber, treeNodes, opts) +} + +// The minimum amount of voting power a proposal needs to succeed +func GetProposalQuorum(rp *rocketpool.RocketPool, opts *bind.CallOpts) (float64, error) { + proposalsSettingsContract, err := getProposalsSettingsContract(rp, opts) + if err != nil { + return 0, err + } + value := new(*big.Int) + if err := proposalsSettingsContract.Call(opts, value, "getProposalQuorum"); err != nil { + return 0, fmt.Errorf("error getting proposal quorum: %w", err) + } + return eth.WeiToEth(*value), nil +} + +// The minimum amount of voting power a proposal needs to succeed +func GetProposalQuorumRaw(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*big.Int, error) { + proposalsSettingsContract, err := getProposalsSettingsContract(rp, opts) + if err != nil { + return nil, err + } + value := new(*big.Int) + if err := proposalsSettingsContract.Call(opts, value, "getProposalQuorum"); err != nil { + return nil, fmt.Errorf("error getting proposal quorum: %w", err) + } + return *value, nil +} +func ProposeProposalQuorum(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return protocol.ProposeSetUint(rp, fmt.Sprintf("set %s", ProposalQuorumSettingPath), ProposalsSettingsContractName, ProposalQuorumSettingPath, value, blockNumber, treeNodes, opts) +} +func EstimateProposeProposalQuorumGas(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return protocol.EstimateProposeSetUintGas(rp, fmt.Sprintf("set %s", ProposalQuorumSettingPath), ProposalsSettingsContractName, ProposalQuorumSettingPath, value, blockNumber, treeNodes, opts) +} + +// The amount of voting power vetoing a proposal require to veto it +func GetProposalVetoQuorum(rp *rocketpool.RocketPool, opts *bind.CallOpts) (float64, error) { + proposalsSettingsContract, err := getProposalsSettingsContract(rp, opts) + if err != nil { + return 0, err + } + value := new(*big.Int) + if err := proposalsSettingsContract.Call(opts, value, "getProposalVetoQuorum"); err != nil { + return 0, fmt.Errorf("error getting proposal veto quorum: %w", err) + } + return eth.WeiToEth(*value), nil +} + +// The amount of voting power vetoing a proposal require to veto it +func GetProposalVetoQuorumRaw(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*big.Int, error) { + proposalsSettingsContract, err := getProposalsSettingsContract(rp, opts) + if err != nil { + return nil, err + } + value := new(*big.Int) + if err := proposalsSettingsContract.Call(opts, value, "getProposalVetoQuorum"); err != nil { + return nil, fmt.Errorf("error getting proposal veto quorum: %w", err) + } + return *value, nil +} +func ProposeProposalVetoQuorum(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return protocol.ProposeSetUint(rp, fmt.Sprintf("set %s", ProposalVetoQuorumSettingPath), ProposalsSettingsContractName, ProposalVetoQuorumSettingPath, value, blockNumber, treeNodes, opts) +} +func EstimateProposeProposalVetoQuorumGas(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return protocol.EstimateProposeSetUintGas(rp, fmt.Sprintf("set %s", ProposalVetoQuorumSettingPath), ProposalsSettingsContractName, ProposalVetoQuorumSettingPath, value, blockNumber, treeNodes, opts) +} + +// The maximum number of blocks old a proposal can be submitted for +func GetProposalMaxBlockAge(rp *rocketpool.RocketPool, opts *bind.CallOpts) (uint64, error) { + proposalsSettingsContract, err := getProposalsSettingsContract(rp, opts) + if err != nil { + return 0, err + } + value := new(*big.Int) + if err := proposalsSettingsContract.Call(opts, value, "getProposalMaxBlockAge"); err != nil { + return 0, fmt.Errorf("error getting proposal max block age: %w", err) + } + return (*value).Uint64(), nil +} +func ProposeProposalMaxBlockAge(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return protocol.ProposeSetUint(rp, fmt.Sprintf("set %s", ProposalMaxBlockAgeSettingPath), ProposalsSettingsContractName, ProposalMaxBlockAgeSettingPath, value, blockNumber, treeNodes, opts) +} +func EstimateProposeProposalMaxBlockAgeGas(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return protocol.EstimateProposeSetUintGas(rp, fmt.Sprintf("set %s", ProposalMaxBlockAgeSettingPath), ProposalsSettingsContractName, ProposalMaxBlockAgeSettingPath, value, blockNumber, treeNodes, opts) +} + +// Get contracts +var proposalsSettingsContractLock sync.Mutex + +func getProposalsSettingsContract(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*rocketpool.Contract, error) { + proposalsSettingsContractLock.Lock() + defer proposalsSettingsContractLock.Unlock() + return rp.GetContract(ProposalsSettingsContractName, opts) +} diff --git a/bindings/settings/protocol/rewards.go b/bindings/settings/protocol/rewards.go new file mode 100644 index 000000000..b9adddbb5 --- /dev/null +++ b/bindings/settings/protocol/rewards.go @@ -0,0 +1,135 @@ +package protocol + +import ( + "fmt" + "math/big" + "sync" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + + "github.com/rocket-pool/smartnode/bindings/dao/protocol" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/types" + "github.com/rocket-pool/smartnode/bindings/utils/eth" +) + +// Config +const ( + RewardsSettingsContractName string = "rocketDAOProtocolSettingsRewards" + RewardsClaimIntervalPeriodsSettingPath string = "rewards.claimsperiods" +) + +// Rewards claimer percents +type RplRewardsPercentages struct { + OdaoPercentage *big.Int `abi:"trustedNodePerc"` + PdaoPercentage *big.Int `abi:"protocolPerc"` + NodePercentage *big.Int `abi:"nodePerc"` +} + +// The RPL rewards percentages for the Oracle DAO, Protocol DAO, and node operators +func GetRewardsPercentages(rp *rocketpool.RocketPool, opts *bind.CallOpts) (RplRewardsPercentages, error) { + rewardsSettingsContract, err := getRewardsSettingsContract(rp, opts) + if err != nil { + return RplRewardsPercentages{}, err + } + value := new(RplRewardsPercentages) + if err := rewardsSettingsContract.Call(opts, value, "getRewardsClaimersPerc"); err != nil { + return RplRewardsPercentages{}, fmt.Errorf("error getting rewards percentages: %w", err) + } + return *value, nil +} + +// The total RPL rewards percentage for node operator collateral +func GetNodeOperatorRewardsPercent(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*big.Int, error) { + rewardsSettingsContract, err := getRewardsSettingsContract(rp, opts) + if err != nil { + return nil, err + } + value := new(*big.Int) + if err := rewardsSettingsContract.Call(opts, value, "getRewardsClaimersNodePerc"); err != nil { + return nil, fmt.Errorf("error getting node operator rewards percent: %w", err) + } + return *value, nil +} + +// The total RPL rewards percentage for Oracle DAO members +func GetOracleDAORewardsPercent(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*big.Int, error) { + rewardsSettingsContract, err := getRewardsSettingsContract(rp, opts) + if err != nil { + return nil, err + } + value := new(*big.Int) + if err := rewardsSettingsContract.Call(opts, value, "getRewardsClaimersTrustedNodePerc"); err != nil { + return nil, fmt.Errorf("error getting oracle DAO rewards percent: %w", err) + } + return *value, nil +} + +// The total RPL rewards percentage for the Protocol DAO treasury +func GetProtocolDAORewardsPercent(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*big.Int, error) { + rewardsSettingsContract, err := getRewardsSettingsContract(rp, opts) + if err != nil { + return nil, err + } + value := new(*big.Int) + if err := rewardsSettingsContract.Call(opts, value, "getRewardsClaimersProtocolPerc"); err != nil { + return nil, fmt.Errorf("error getting protocol DAO rewards percent: %w", err) + } + return *value, nil +} + +// The time that the RPL rewards percentages were last updated +func GetRewardsClaimerPercTimeUpdated(rp *rocketpool.RocketPool, opts *bind.CallOpts) (uint64, error) { + rewardsSettingsContract, err := getRewardsSettingsContract(rp, opts) + if err != nil { + return 0, err + } + value := new(*big.Int) + if err := rewardsSettingsContract.Call(opts, value, "getRewardsClaimersTimeUpdated"); err != nil { + return 0, fmt.Errorf("error getting rewards claimer updated time: %w", err) + } + return (*value).Uint64(), nil +} + +// The total claim amount for all claimers as a fraction +func GetRewardsClaimersPercTotal(rp *rocketpool.RocketPool, opts *bind.CallOpts) (float64, error) { + rewardsSettingsContract, err := getRewardsSettingsContract(rp, opts) + if err != nil { + return 0, err + } + value := new(*big.Int) + if err := rewardsSettingsContract.Call(opts, value, "getRewardsClaimersPercTotal"); err != nil { + return 0, fmt.Errorf("error getting rewards claimers total percent: %w", err) + } + return eth.WeiToEth(*value), nil +} + +// Rewards claim interval time +func GetRewardsClaimIntervalTime(rp *rocketpool.RocketPool, opts *bind.CallOpts) (time.Duration, error) { + rewardsSettingsContract, err := getRewardsSettingsContract(rp, opts) + if err != nil { + return 0, err + } + value := new(*big.Int) + if err := rewardsSettingsContract.Call(opts, value, "getRewardsClaimIntervalTime"); err != nil { + return 0, fmt.Errorf("error getting rewards claim interval: %w", err) + } + return time.Duration((*value).Uint64()) * time.Second, nil +} +func ProposeRewardsClaimIntervalTime(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return protocol.ProposeSetUint(rp, fmt.Sprintf("set %s", RewardsClaimIntervalPeriodsSettingPath), RewardsSettingsContractName, RewardsClaimIntervalPeriodsSettingPath, value, blockNumber, treeNodes, opts) +} +func EstimateProposeRewardsClaimIntervalTimeGas(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return protocol.EstimateProposeSetUintGas(rp, fmt.Sprintf("set %s", RewardsClaimIntervalPeriodsSettingPath), RewardsSettingsContractName, RewardsClaimIntervalPeriodsSettingPath, value, blockNumber, treeNodes, opts) +} + +// Get contracts +var rewardsSettingsContractLock sync.Mutex + +func getRewardsSettingsContract(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*rocketpool.Contract, error) { + rewardsSettingsContractLock.Lock() + defer rewardsSettingsContractLock.Unlock() + return rp.GetContract(RewardsSettingsContractName, opts) +} diff --git a/bindings/settings/protocol/security.go b/bindings/settings/protocol/security.go new file mode 100644 index 000000000..8939bb63e --- /dev/null +++ b/bindings/settings/protocol/security.go @@ -0,0 +1,128 @@ +package protocol + +import ( + "fmt" + "math/big" + "sync" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/rocket-pool/smartnode/bindings/dao/protocol" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/types" +) + +// Config +const ( + SecuritySettingsContractName string = "rocketDAOProtocolSettingsSecurity" + SecurityMembersQuorumSettingPath string = "members.quorum" + SecurityMembersLeaveTimeSettingPath string = "members.leave.time" + SecurityProposalVoteTimeSettingPath string = "proposal.vote.time" + SecurityProposalExecuteTimeSettingPath string = "proposal.execute.time" + SecurityProposalActionTimeSettingPath string = "proposal.action.time" +) + +// Security council member quorum threshold that must be met for proposals to pass +func GetSecurityMembersQuorum(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*big.Int, error) { + securitySettingsContract, err := getSecuritySettingsContract(rp, opts) + if err != nil { + return nil, err + } + value := new(*big.Int) + if err := securitySettingsContract.Call(opts, value, "getQuorum"); err != nil { + return nil, fmt.Errorf("error getting security members quorum: %w", err) + } + return *value, nil +} +func ProposeSecurityMembersQuorum(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return protocol.ProposeSetUint(rp, fmt.Sprintf("set %s", SecurityMembersQuorumSettingPath), SecuritySettingsContractName, SecurityMembersQuorumSettingPath, value, blockNumber, treeNodes, opts) +} +func EstimateProposeSecurityMembersQuorumGas(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return protocol.EstimateProposeSetUintGas(rp, fmt.Sprintf("set %s", SecurityMembersQuorumSettingPath), SecuritySettingsContractName, SecurityMembersQuorumSettingPath, value, blockNumber, treeNodes, opts) +} + +// How long a member must give notice for before manually leaving the security council +func GetSecurityMembersLeaveTime(rp *rocketpool.RocketPool, opts *bind.CallOpts) (time.Duration, error) { + securitySettingsContract, err := getSecuritySettingsContract(rp, opts) + if err != nil { + return 0, err + } + value := new(*big.Int) + if err := securitySettingsContract.Call(opts, value, "getLeaveTime"); err != nil { + return 0, fmt.Errorf("error getting security members leave time: %w", err) + } + return time.Second * time.Duration((*value).Uint64()), nil +} +func ProposeSecurityMembersLeaveTime(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return protocol.ProposeSetUint(rp, fmt.Sprintf("set %s", SecurityMembersLeaveTimeSettingPath), SecuritySettingsContractName, SecurityMembersLeaveTimeSettingPath, value, blockNumber, treeNodes, opts) +} +func EstimateProposeSecurityMembersLeaveTimeGas(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return protocol.EstimateProposeSetUintGas(rp, fmt.Sprintf("set %s", SecurityMembersLeaveTimeSettingPath), SecuritySettingsContractName, SecurityMembersLeaveTimeSettingPath, value, blockNumber, treeNodes, opts) +} + +// How long a security council proposal can be voted on (phase2) +func GetSecurityProposalVoteTime(rp *rocketpool.RocketPool, opts *bind.CallOpts) (time.Duration, error) { + securitySettingsContract, err := getSecuritySettingsContract(rp, opts) + if err != nil { + return 0, err + } + value := new(*big.Int) + if err := securitySettingsContract.Call(opts, value, "getVoteTime"); err != nil { + return 0, fmt.Errorf("error getting security proposal vote time: %w", err) + } + return time.Second * time.Duration((*value).Uint64()), nil +} +func ProposeSecurityProposalVoteTime(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return protocol.ProposeSetUint(rp, fmt.Sprintf("set %s", SecurityProposalVoteTimeSettingPath), SecuritySettingsContractName, SecurityProposalVoteTimeSettingPath, value, blockNumber, treeNodes, opts) +} +func EstimateProposeSecurityProposalVoteTimeGas(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return protocol.EstimateProposeSetUintGas(rp, fmt.Sprintf("set %s", SecurityProposalVoteTimeSettingPath), SecuritySettingsContractName, SecurityProposalVoteTimeSettingPath, value, blockNumber, treeNodes, opts) +} + +// How long a security council proposal can be executed after its voting period is finished +func GetSecurityProposalExecuteTime(rp *rocketpool.RocketPool, opts *bind.CallOpts) (time.Duration, error) { + securitySettingsContract, err := getSecuritySettingsContract(rp, opts) + if err != nil { + return 0, err + } + value := new(*big.Int) + if err := securitySettingsContract.Call(opts, value, "getExecuteTime"); err != nil { + return 0, fmt.Errorf("error getting security proposal execute time: %w", err) + } + return time.Second * time.Duration((*value).Uint64()), nil +} +func ProposeSecurityProposalExecuteTime(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return protocol.ProposeSetUint(rp, fmt.Sprintf("set %s", SecurityProposalExecuteTimeSettingPath), SecuritySettingsContractName, SecurityProposalExecuteTimeSettingPath, value, blockNumber, treeNodes, opts) +} +func EstimateProposeSecurityProposalExecuteTimeGas(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return protocol.EstimateProposeSetUintGas(rp, fmt.Sprintf("set %s", SecurityProposalExecuteTimeSettingPath), SecuritySettingsContractName, SecurityProposalExecuteTimeSettingPath, value, blockNumber, treeNodes, opts) +} + +// Certain security council proposals require a secondary action to be run after the proposal is successful (joining, leaving etc). This is how long until that action expires. +func GetSecurityProposalActionTime(rp *rocketpool.RocketPool, opts *bind.CallOpts) (time.Duration, error) { + securitySettingsContract, err := getSecuritySettingsContract(rp, opts) + if err != nil { + return 0, err + } + value := new(*big.Int) + if err := securitySettingsContract.Call(opts, value, "getActionTime"); err != nil { + return 0, fmt.Errorf("error getting security proposal action time: %w", err) + } + return time.Second * time.Duration((*value).Uint64()), nil +} +func ProposeSecurityProposalActionTime(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return protocol.ProposeSetUint(rp, fmt.Sprintf("set %s", SecurityProposalActionTimeSettingPath), SecuritySettingsContractName, SecurityProposalActionTimeSettingPath, value, blockNumber, treeNodes, opts) +} +func EstimateProposeSecurityProposalActionTimeGas(rp *rocketpool.RocketPool, value *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return protocol.EstimateProposeSetUintGas(rp, fmt.Sprintf("set %s", SecurityProposalActionTimeSettingPath), SecuritySettingsContractName, SecurityProposalActionTimeSettingPath, value, blockNumber, treeNodes, opts) +} + +// Get contracts +var securitySettingsContractLock sync.Mutex + +func getSecuritySettingsContract(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*rocketpool.Contract, error) { + securitySettingsContractLock.Lock() + defer securitySettingsContractLock.Unlock() + return rp.GetContract(SecuritySettingsContractName, opts) +} diff --git a/bindings/settings/security/auction.go b/bindings/settings/security/auction.go new file mode 100644 index 000000000..d69ea6e87 --- /dev/null +++ b/bindings/settings/security/auction.go @@ -0,0 +1,32 @@ +package security + +import ( + "fmt" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + + "github.com/rocket-pool/smartnode/bindings/dao/security" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + psettings "github.com/rocket-pool/smartnode/bindings/settings/protocol" +) + +const ( + auctionNamespace string = "auction" +) + +// Lot creation currently enabled +func ProposeCreateLotEnabled(rp *rocketpool.RocketPool, value bool, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return security.ProposeSetBool(rp, fmt.Sprintf("set %s", psettings.CreateLotEnabledSettingPath), auctionNamespace, psettings.CreateLotEnabledSettingPath, value, opts) +} +func EstimateProposeCreateLotEnabledGas(rp *rocketpool.RocketPool, value bool, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return security.EstimateProposeSetBoolGas(rp, fmt.Sprintf("set %s", psettings.CreateLotEnabledSettingPath), auctionNamespace, psettings.CreateLotEnabledSettingPath, value, opts) +} + +// Lot bidding currently enabled +func ProposeBidOnLotEnabled(rp *rocketpool.RocketPool, value bool, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return security.ProposeSetBool(rp, fmt.Sprintf("set %s", psettings.BidOnLotEnabledSettingPath), auctionNamespace, psettings.BidOnLotEnabledSettingPath, value, opts) +} +func EstimateProposeBidOnLotEnabledGas(rp *rocketpool.RocketPool, value bool, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return security.EstimateProposeSetBoolGas(rp, fmt.Sprintf("set %s", psettings.BidOnLotEnabledSettingPath), auctionNamespace, psettings.BidOnLotEnabledSettingPath, value, opts) +} diff --git a/bindings/settings/security/deposit.go b/bindings/settings/security/deposit.go new file mode 100644 index 000000000..e5aabfb3d --- /dev/null +++ b/bindings/settings/security/deposit.go @@ -0,0 +1,32 @@ +package security + +import ( + "fmt" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + + "github.com/rocket-pool/smartnode/bindings/dao/security" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + psettings "github.com/rocket-pool/smartnode/bindings/settings/protocol" +) + +const ( + depositNamespace string = "deposit" +) + +// Deposits currently enabled +func ProposeDepositEnabled(rp *rocketpool.RocketPool, value bool, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return security.ProposeSetBool(rp, fmt.Sprintf("set %s", psettings.DepositEnabledSettingPath), depositNamespace, psettings.DepositEnabledSettingPath, value, opts) +} +func EstimateProposeDepositEnabledGas(rp *rocketpool.RocketPool, value bool, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return security.EstimateProposeSetBoolGas(rp, fmt.Sprintf("set %s", psettings.DepositEnabledSettingPath), depositNamespace, psettings.DepositEnabledSettingPath, value, opts) +} + +// Deposit assignments currently enabled +func ProposeAssignDepositsEnabled(rp *rocketpool.RocketPool, value bool, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return security.ProposeSetBool(rp, fmt.Sprintf("set %s", psettings.AssignDepositsEnabledSettingPath), depositNamespace, psettings.AssignDepositsEnabledSettingPath, value, opts) +} +func EstimateProposeAssignDepositsEnabledGas(rp *rocketpool.RocketPool, value bool, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return security.EstimateProposeSetBoolGas(rp, fmt.Sprintf("set %s", psettings.AssignDepositsEnabledSettingPath), depositNamespace, psettings.AssignDepositsEnabledSettingPath, value, opts) +} diff --git a/bindings/settings/security/minipool.go b/bindings/settings/security/minipool.go new file mode 100644 index 000000000..b2e857da3 --- /dev/null +++ b/bindings/settings/security/minipool.go @@ -0,0 +1,32 @@ +package security + +import ( + "fmt" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + + "github.com/rocket-pool/smartnode/bindings/dao/security" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + psettings "github.com/rocket-pool/smartnode/bindings/settings/protocol" +) + +const ( + minipoolNamespace string = "minipool" +) + +// Minipool withdrawable event submissions currently enabled +func ProposeMinipoolSubmitWithdrawableEnabled(rp *rocketpool.RocketPool, value bool, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return security.ProposeSetBool(rp, fmt.Sprintf("set %s", psettings.MinipoolSubmitWithdrawableEnabledSettingPath), minipoolNamespace, psettings.MinipoolSubmitWithdrawableEnabledSettingPath, value, opts) +} +func EstimateProposeMinipoolSubmitWithdrawableEnabledGas(rp *rocketpool.RocketPool, value bool, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return security.EstimateProposeSetBoolGas(rp, fmt.Sprintf("set %s", psettings.MinipoolSubmitWithdrawableEnabledSettingPath), minipoolNamespace, psettings.MinipoolSubmitWithdrawableEnabledSettingPath, value, opts) +} + +// Minipool bond reductions currently enabled +func ProposeBondReductionEnabled(rp *rocketpool.RocketPool, value bool, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return security.ProposeSetBool(rp, fmt.Sprintf("set %s", psettings.BondReductionEnabledSettingPath), minipoolNamespace, psettings.BondReductionEnabledSettingPath, value, opts) +} +func EstimateProposeBondReductionEnabledGas(rp *rocketpool.RocketPool, value bool, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return security.EstimateProposeSetBoolGas(rp, fmt.Sprintf("set %s", psettings.BondReductionEnabledSettingPath), minipoolNamespace, psettings.BondReductionEnabledSettingPath, value, opts) +} diff --git a/bindings/settings/security/network.go b/bindings/settings/security/network.go new file mode 100644 index 000000000..45b133a00 --- /dev/null +++ b/bindings/settings/security/network.go @@ -0,0 +1,40 @@ +package security + +import ( + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + + "github.com/rocket-pool/smartnode/bindings/dao/security" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + psettings "github.com/rocket-pool/smartnode/bindings/settings/protocol" +) + +const ( + networkNamespace string = "network" +) + +// Network balance submissions currently enabled +func ProposeSubmitBalancesEnabled(rp *rocketpool.RocketPool, value bool, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return security.ProposeSetBool(rp, fmt.Sprintf("set %s", psettings.SubmitBalancesEnabledSettingPath), networkNamespace, psettings.SubmitBalancesEnabledSettingPath, value, opts) +} +func EstimateProposeSubmitBalancesEnabledGas(rp *rocketpool.RocketPool, value bool, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return security.EstimateProposeSetBoolGas(rp, fmt.Sprintf("set %s", psettings.SubmitBalancesEnabledSettingPath), networkNamespace, psettings.SubmitBalancesEnabledSettingPath, value, opts) +} + +// Rewards submissions currently enabled +func ProposeSubmitRewardsEnabled(rp *rocketpool.RocketPool, value bool, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return security.ProposeSetBool(rp, fmt.Sprintf("set %s", psettings.SubmitRewardsEnabledSettingPath), networkNamespace, psettings.SubmitRewardsEnabledSettingPath, value, opts) +} +func EstimateProposeSubmitRewardsEnabledGas(rp *rocketpool.RocketPool, value bool, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return security.EstimateProposeSetBoolGas(rp, fmt.Sprintf("set %s", psettings.SubmitRewardsEnabledSettingPath), networkNamespace, psettings.SubmitRewardsEnabledSettingPath, value, opts) +} + +func ProposeNodeComissionShareSecurityCouncilAdder(rp *rocketpool.RocketPool, value *big.Int, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return security.ProposeSetUint(rp, fmt.Sprintf("set %s", psettings.NodeComissionShareSecurityCouncilAdder), networkNamespace, psettings.NodeComissionShareSecurityCouncilAdder, value, opts) +} +func EstimateProposeNodeComissionShareSecurityCouncilAdder(rp *rocketpool.RocketPool, value *big.Int, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return security.EstimateProposeSetUintGas(rp, fmt.Sprintf("set %s", psettings.NodeComissionShareSecurityCouncilAdder), networkNamespace, psettings.NodeComissionShareSecurityCouncilAdder, value, opts) +} diff --git a/bindings/settings/security/node.go b/bindings/settings/security/node.go new file mode 100644 index 000000000..c9e57afaf --- /dev/null +++ b/bindings/settings/security/node.go @@ -0,0 +1,48 @@ +package security + +import ( + "fmt" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + + "github.com/rocket-pool/smartnode/bindings/dao/security" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + psettings "github.com/rocket-pool/smartnode/bindings/settings/protocol" +) + +const ( + nodeNamespace string = "node" +) + +// Node registrations currently enabled +func ProposeNodeRegistrationEnabled(rp *rocketpool.RocketPool, value bool, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return security.ProposeSetBool(rp, fmt.Sprintf("set %s", psettings.NodeRegistrationEnabledSettingPath), nodeNamespace, psettings.NodeRegistrationEnabledSettingPath, value, opts) +} +func EstimateProposeNodeRegistrationEnabledGas(rp *rocketpool.RocketPool, value bool, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return security.EstimateProposeSetBoolGas(rp, fmt.Sprintf("set %s", psettings.NodeRegistrationEnabledSettingPath), nodeNamespace, psettings.NodeRegistrationEnabledSettingPath, value, opts) +} + +// Smoothing pool joining currently enabled +func ProposeSmoothingPoolRegistrationEnabled(rp *rocketpool.RocketPool, value bool, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return security.ProposeSetBool(rp, fmt.Sprintf("set %s", psettings.SmoothingPoolRegistrationEnabledSettingPath), nodeNamespace, psettings.SmoothingPoolRegistrationEnabledSettingPath, value, opts) +} +func EstimateProposeSmoothingPoolRegistrationEnabledGas(rp *rocketpool.RocketPool, value bool, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return security.EstimateProposeSetBoolGas(rp, fmt.Sprintf("set %s", psettings.SmoothingPoolRegistrationEnabledSettingPath), nodeNamespace, psettings.SmoothingPoolRegistrationEnabledSettingPath, value, opts) +} + +// Node deposits currently enabled +func ProposeNodeDepositEnabled(rp *rocketpool.RocketPool, value bool, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return security.ProposeSetBool(rp, fmt.Sprintf("set %s", psettings.NodeDepositEnabledSettingPath), nodeNamespace, psettings.NodeDepositEnabledSettingPath, value, opts) +} +func EstimateProposeNodeDepositEnabledGas(rp *rocketpool.RocketPool, value bool, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return security.EstimateProposeSetBoolGas(rp, fmt.Sprintf("set %s", psettings.NodeDepositEnabledSettingPath), nodeNamespace, psettings.NodeDepositEnabledSettingPath, value, opts) +} + +// Vacant minipools currently enabled +func ProposeVacantMinipoolsEnabled(rp *rocketpool.RocketPool, value bool, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return security.ProposeSetBool(rp, fmt.Sprintf("set %s", psettings.VacantMinipoolsEnabledSettingPath), nodeNamespace, psettings.VacantMinipoolsEnabledSettingPath, value, opts) +} +func EstimateProposeVacantMinipoolsEnabledGas(rp *rocketpool.RocketPool, value bool, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return security.EstimateProposeSetBoolGas(rp, fmt.Sprintf("set %s", psettings.VacantMinipoolsEnabledSettingPath), nodeNamespace, psettings.VacantMinipoolsEnabledSettingPath, value, opts) +} diff --git a/bindings/settings/trustednode/members.go b/bindings/settings/trustednode/members.go new file mode 100644 index 000000000..5d703b65d --- /dev/null +++ b/bindings/settings/trustednode/members.go @@ -0,0 +1,168 @@ +package trustednode + +import ( + "fmt" + "math/big" + "sync" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + + trustednodedao "github.com/rocket-pool/smartnode/bindings/dao/trustednode" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/utils/eth" +) + +// Config +const ( + MembersSettingsContractName = "rocketDAONodeTrustedSettingsMembers" + QuorumSettingPath = "members.quorum" + RPLBondSettingPath = "members.rplbond" + MinipoolUnbondedMaxSettingPath = "members.minipool.unbonded.max" + MinipoolUnbondedMinFeeSettingPath = "members.minipool.unbonded.min.fee" + ChallengeCooldownSettingPath = "members.challenge.cooldown" + ChallengeWindowSettingPath = "members.challenge.window" + ChallengeCostSettingPath = "members.challenge.cost" +) + +// Member proposal quorum threshold +func GetQuorum(rp *rocketpool.RocketPool, opts *bind.CallOpts) (float64, error) { + membersSettingsContract, err := getMembersSettingsContract(rp, opts) + if err != nil { + return 0, err + } + value := new(*big.Int) + if err := membersSettingsContract.Call(opts, value, "getQuorum"); err != nil { + return 0, fmt.Errorf("error getting member quorum threshold: %w", err) + } + return eth.WeiToEth(*value), nil +} +func ProposeQuorum(rp *rocketpool.RocketPool, value float64, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return trustednodedao.ProposeSetUint(rp, fmt.Sprintf("set %s", QuorumSettingPath), MembersSettingsContractName, QuorumSettingPath, eth.EthToWei(value), opts) +} +func EstimateProposeQuorumGas(rp *rocketpool.RocketPool, value float64, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return trustednodedao.EstimateProposeSetUintGas(rp, fmt.Sprintf("set %s", QuorumSettingPath), MembersSettingsContractName, QuorumSettingPath, eth.EthToWei(value), opts) +} + +// RPL bond required for a member +func GetRPLBond(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*big.Int, error) { + membersSettingsContract, err := getMembersSettingsContract(rp, opts) + if err != nil { + return nil, err + } + value := new(*big.Int) + if err := membersSettingsContract.Call(opts, value, "getRPLBond"); err != nil { + return nil, fmt.Errorf("error getting member RPL bond amount: %w", err) + } + return *value, nil +} +func ProposeRPLBond(rp *rocketpool.RocketPool, value *big.Int, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return trustednodedao.ProposeSetUint(rp, fmt.Sprintf("set %s", RPLBondSettingPath), MembersSettingsContractName, RPLBondSettingPath, value, opts) +} +func EstimateProposeRPLBondGas(rp *rocketpool.RocketPool, value *big.Int, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return trustednodedao.EstimateProposeSetUintGas(rp, fmt.Sprintf("set %s", RPLBondSettingPath), MembersSettingsContractName, RPLBondSettingPath, value, opts) +} + +// The maximum number of unbonded minipools a member can run +func GetMinipoolUnbondedMax(rp *rocketpool.RocketPool, opts *bind.CallOpts) (uint64, error) { + membersSettingsContract, err := getMembersSettingsContract(rp, opts) + if err != nil { + return 0, err + } + value := new(*big.Int) + if err := membersSettingsContract.Call(opts, value, "getMinipoolUnbondedMax"); err != nil { + return 0, fmt.Errorf("error getting member unbonded minipool limit: %w", err) + } + return (*value).Uint64(), nil +} +func ProposeMinipoolUnbondedMax(rp *rocketpool.RocketPool, value uint64, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return trustednodedao.ProposeSetUint(rp, fmt.Sprintf("set %s", MinipoolUnbondedMaxSettingPath), MembersSettingsContractName, MinipoolUnbondedMaxSettingPath, big.NewInt(int64(value)), opts) +} +func EstimateProposeMinipoolUnbondedMaxGas(rp *rocketpool.RocketPool, value uint64, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return trustednodedao.EstimateProposeSetUintGas(rp, fmt.Sprintf("set %s", MinipoolUnbondedMaxSettingPath), MembersSettingsContractName, MinipoolUnbondedMaxSettingPath, big.NewInt(int64(value)), opts) +} + +// The minimum commission rate before unbonded minipools are allowed +func GetMinipoolUnbondedMinFee(rp *rocketpool.RocketPool, opts *bind.CallOpts) (uint64, error) { + membersSettingsContract, err := getMembersSettingsContract(rp, opts) + if err != nil { + return 0, err + } + value := new(*big.Int) + if err := membersSettingsContract.Call(opts, value, "getMinipoolUnbondedMinFee"); err != nil { + return 0, fmt.Errorf("error getting member unbonded minipool minimum fee: %w", err) + } + return (*value).Uint64(), nil +} +func ProposeMinipoolUnbondedMinFee(rp *rocketpool.RocketPool, value uint64, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return trustednodedao.ProposeSetUint(rp, fmt.Sprintf("set %s", MinipoolUnbondedMinFeeSettingPath), MembersSettingsContractName, MinipoolUnbondedMinFeeSettingPath, big.NewInt(int64(value)), opts) +} +func EstimateProposeMinipoolUnbondedMinFeeGas(rp *rocketpool.RocketPool, value uint64, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return trustednodedao.EstimateProposeSetUintGas(rp, fmt.Sprintf("set %s", MinipoolUnbondedMinFeeSettingPath), MembersSettingsContractName, MinipoolUnbondedMinFeeSettingPath, big.NewInt(int64(value)), opts) +} + +// The period a member must wait for before submitting another challenge, in blocks +func GetChallengeCooldown(rp *rocketpool.RocketPool, opts *bind.CallOpts) (uint64, error) { + membersSettingsContract, err := getMembersSettingsContract(rp, opts) + if err != nil { + return 0, err + } + value := new(*big.Int) + if err := membersSettingsContract.Call(opts, value, "getChallengeCooldown"); err != nil { + return 0, fmt.Errorf("error getting member challenge cooldown period: %w", err) + } + return (*value).Uint64(), nil +} +func ProposeChallengeCooldown(rp *rocketpool.RocketPool, value uint64, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return trustednodedao.ProposeSetUint(rp, fmt.Sprintf("set %s", ChallengeCooldownSettingPath), MembersSettingsContractName, ChallengeCooldownSettingPath, big.NewInt(int64(value)), opts) +} +func EstimateProposeChallengeCooldownGas(rp *rocketpool.RocketPool, value uint64, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return trustednodedao.EstimateProposeSetUintGas(rp, fmt.Sprintf("set %s", ChallengeCooldownSettingPath), MembersSettingsContractName, ChallengeCooldownSettingPath, big.NewInt(int64(value)), opts) +} + +// The period during which a member can respond to a challenge, in blocks +func GetChallengeWindow(rp *rocketpool.RocketPool, opts *bind.CallOpts) (uint64, error) { + membersSettingsContract, err := getMembersSettingsContract(rp, opts) + if err != nil { + return 0, err + } + value := new(*big.Int) + if err := membersSettingsContract.Call(opts, value, "getChallengeWindow"); err != nil { + return 0, fmt.Errorf("error getting member challenge window period: %w", err) + } + return (*value).Uint64(), nil +} +func ProposeChallengeWindow(rp *rocketpool.RocketPool, value uint64, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return trustednodedao.ProposeSetUint(rp, fmt.Sprintf("set %s", ChallengeWindowSettingPath), MembersSettingsContractName, ChallengeWindowSettingPath, big.NewInt(int64(value)), opts) +} +func EstimateProposeChallengeWindowGas(rp *rocketpool.RocketPool, value uint64, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return trustednodedao.EstimateProposeSetUintGas(rp, fmt.Sprintf("set %s", ChallengeWindowSettingPath), MembersSettingsContractName, ChallengeWindowSettingPath, big.NewInt(int64(value)), opts) +} + +// The fee for a non-member to challenge a member, in wei +func GetChallengeCost(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*big.Int, error) { + membersSettingsContract, err := getMembersSettingsContract(rp, opts) + if err != nil { + return nil, err + } + value := new(*big.Int) + if err := membersSettingsContract.Call(opts, value, "getChallengeCost"); err != nil { + return nil, fmt.Errorf("error getting member challenge cost: %w", err) + } + return *value, nil +} +func ProposeChallengeCost(rp *rocketpool.RocketPool, value *big.Int, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return trustednodedao.ProposeSetUint(rp, fmt.Sprintf("set %s", ChallengeCostSettingPath), MembersSettingsContractName, ChallengeCostSettingPath, value, opts) +} +func EstimateProposeChallengeCostGas(rp *rocketpool.RocketPool, value *big.Int, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return trustednodedao.EstimateProposeSetUintGas(rp, fmt.Sprintf("set %s", ChallengeCostSettingPath), MembersSettingsContractName, ChallengeCostSettingPath, value, opts) +} + +// Get contracts +var membersSettingsContractLock sync.Mutex + +func getMembersSettingsContract(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*rocketpool.Contract, error) { + membersSettingsContractLock.Lock() + defer membersSettingsContractLock.Unlock() + return rp.GetContract(MembersSettingsContractName, opts) +} diff --git a/bindings/settings/trustednode/minipool.go b/bindings/settings/trustednode/minipool.go new file mode 100644 index 000000000..385f90a36 --- /dev/null +++ b/bindings/settings/trustednode/minipool.go @@ -0,0 +1,147 @@ +package trustednode + +import ( + "fmt" + "math/big" + "sync" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + + trustednodedao "github.com/rocket-pool/smartnode/bindings/dao/trustednode" + "github.com/rocket-pool/smartnode/bindings/rocketpool" +) + +// Config +const ( + MinipoolSettingsContractName = "rocketDAONodeTrustedSettingsMinipool" + DissolvePeriodPath = "megapool.dissolve.period" + ScrubPeriodPath = "minipool.scrub.period" + PromotionScrubPeriodPath = "minipool.promotion.scrub.period" + ScrubPenaltyEnabledPath = "minipool.scrub.penalty.enabled" + BondReductionWindowStartPath = "minipool.bond.reduction.window.start" + BondReductionWindowLengthPath = "minipool.bond.reduction.window.length" +) + +// The amount of time, in seconds, the scrub check lasts before a minipool can move from prelaunch to staking +func GetScrubPeriod(rp *rocketpool.RocketPool, opts *bind.CallOpts) (uint64, error) { + minipoolSettingsContract, err := getMinipoolSettingsContract(rp, opts) + if err != nil { + return 0, err + } + value := new(*big.Int) + if err := minipoolSettingsContract.Call(opts, value, "getScrubPeriod"); err != nil { + return 0, fmt.Errorf("error getting scrub period: %w", err) + } + return (*value).Uint64(), nil +} +func ProposeScrubPeriod(rp *rocketpool.RocketPool, value uint64, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return trustednodedao.ProposeSetUint(rp, fmt.Sprintf("set %s", ScrubPeriodPath), MinipoolSettingsContractName, ScrubPeriodPath, big.NewInt(int64(value)), opts) +} +func EstimateProposeScrubPeriodGas(rp *rocketpool.RocketPool, value uint64, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return trustednodedao.EstimateProposeSetUintGas(rp, fmt.Sprintf("set %s", ScrubPeriodPath), MinipoolSettingsContractName, ScrubPeriodPath, big.NewInt(int64(value)), opts) +} + +// The amount of time, in seconds, the promotion scrub check lasts before a vacant minipool can be promoted +func GetPromotionScrubPeriod(rp *rocketpool.RocketPool, opts *bind.CallOpts) (uint64, error) { + minipoolSettingsContract, err := getMinipoolSettingsContract(rp, opts) + if err != nil { + return 0, err + } + value := new(*big.Int) + if err := minipoolSettingsContract.Call(opts, value, "getPromotionScrubPeriod"); err != nil { + return 0, fmt.Errorf("error getting promotion scrub period: %w", err) + } + return (*value).Uint64(), nil +} +func ProposePromotionScrubPeriod(rp *rocketpool.RocketPool, value uint64, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return trustednodedao.ProposeSetUint(rp, fmt.Sprintf("set %s", PromotionScrubPeriodPath), MinipoolSettingsContractName, PromotionScrubPeriodPath, big.NewInt(int64(value)), opts) +} +func EstimateProposePromotionScrubPeriodGas(rp *rocketpool.RocketPool, value uint64, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return trustednodedao.EstimateProposeSetUintGas(rp, fmt.Sprintf("set %s", PromotionScrubPeriodPath), MinipoolSettingsContractName, PromotionScrubPeriodPath, big.NewInt(int64(value)), opts) +} + +// Whether or not the RPL slashing penalty is applied to scrubbed minipools +func GetScrubPenaltyEnabled(rp *rocketpool.RocketPool, opts *bind.CallOpts) (bool, error) { + minipoolSettingsContract, err := getMinipoolSettingsContract(rp, opts) + if err != nil { + return false, err + } + value := new(bool) + if err := minipoolSettingsContract.Call(opts, value, "getScrubPenaltyEnabled"); err != nil { + return false, fmt.Errorf("error getting scrub penalty setting: %w", err) + } + return (*value), nil +} +func ProposeScrubPenaltyEnabled(rp *rocketpool.RocketPool, value bool, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return trustednodedao.ProposeSetBool(rp, fmt.Sprintf("set %s", ScrubPenaltyEnabledPath), MinipoolSettingsContractName, ScrubPenaltyEnabledPath, value, opts) +} +func EstimateProposeScrubPenaltyEnabledGas(rp *rocketpool.RocketPool, value bool, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return trustednodedao.EstimateProposeSetBoolGas(rp, fmt.Sprintf("set %s", ScrubPenaltyEnabledPath), MinipoolSettingsContractName, ScrubPenaltyEnabledPath, value, opts) +} + +// The amount of time, in seconds, a minipool must wait after beginning a bond reduction before it can apply the bond reduction (how long the Oracle DAO has to cancel the reduction if required) +func GetBondReductionWindowStart(rp *rocketpool.RocketPool, opts *bind.CallOpts) (uint64, error) { + minipoolSettingsContract, err := getMinipoolSettingsContract(rp, opts) + if err != nil { + return 0, err + } + value := new(*big.Int) + if err := minipoolSettingsContract.Call(opts, value, "getBondReductionWindowStart"); err != nil { + return 0, fmt.Errorf("error getting bond reduction window start: %w", err) + } + return (*value).Uint64(), nil +} +func ProposeBondReductionWindowStart(rp *rocketpool.RocketPool, value uint64, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return trustednodedao.ProposeSetUint(rp, fmt.Sprintf("set %s", BondReductionWindowStartPath), MinipoolSettingsContractName, BondReductionWindowStartPath, big.NewInt(int64(value)), opts) +} +func EstimateProposeBondReductionWindowStartGas(rp *rocketpool.RocketPool, value uint64, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return trustednodedao.EstimateProposeSetUintGas(rp, fmt.Sprintf("set %s", BondReductionWindowStartPath), MinipoolSettingsContractName, BondReductionWindowStartPath, big.NewInt(int64(value)), opts) +} + +// The amount of time, in seconds, a minipool has to reduce its bond once it has passed the check window +func GetBondReductionWindowLength(rp *rocketpool.RocketPool, opts *bind.CallOpts) (uint64, error) { + minipoolSettingsContract, err := getMinipoolSettingsContract(rp, opts) + if err != nil { + return 0, err + } + value := new(*big.Int) + if err := minipoolSettingsContract.Call(opts, value, "getBondReductionWindowLength"); err != nil { + return 0, fmt.Errorf("error getting bond reduction window length: %w", err) + } + return (*value).Uint64(), nil +} +func ProposeBondReductionWindowLength(rp *rocketpool.RocketPool, value uint64, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return trustednodedao.ProposeSetUint(rp, fmt.Sprintf("set %s", BondReductionWindowLengthPath), MinipoolSettingsContractName, BondReductionWindowLengthPath, big.NewInt(int64(value)), opts) +} +func EstimateProposeBondReductionWindowLengthGas(rp *rocketpool.RocketPool, value uint64, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return trustednodedao.EstimateProposeSetUintGas(rp, fmt.Sprintf("set %s", BondReductionWindowLengthPath), MinipoolSettingsContractName, BondReductionWindowLengthPath, big.NewInt(int64(value)), opts) +} + +// The amount of time, in seconds, how long after assignment before a validator can be dissolved +func GetDissolvePeriod(rp *rocketpool.RocketPool, opts *bind.CallOpts) (uint64, error) { + minipoolSettingsContract, err := getMinipoolSettingsContract(rp, opts) + if err != nil { + return 0, err + } + value := new(*big.Int) + if err := minipoolSettingsContract.Call(opts, value, "getDissolvePeriod"); err != nil { + return 0, fmt.Errorf("error getting dissolve period: %w", err) + } + return (*value).Uint64(), nil +} +func ProposeDissolvePeriod(rp *rocketpool.RocketPool, value uint64, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return trustednodedao.ProposeSetUint(rp, fmt.Sprintf("set %s", DissolvePeriodPath), MinipoolSettingsContractName, DissolvePeriodPath, big.NewInt(int64(value)), opts) +} +func EstimateProposeDissolvePeriodGas(rp *rocketpool.RocketPool, value uint64, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return trustednodedao.EstimateProposeSetUintGas(rp, fmt.Sprintf("set %s", DissolvePeriodPath), MinipoolSettingsContractName, DissolvePeriodPath, big.NewInt(int64(value)), opts) +} + +// Get contracts +var minipoolSettingsContractLock sync.Mutex + +func getMinipoolSettingsContract(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*rocketpool.Contract, error) { + minipoolSettingsContractLock.Lock() + defer minipoolSettingsContractLock.Unlock() + return rp.GetContract(MinipoolSettingsContractName, opts) +} diff --git a/bindings/settings/trustednode/proposals.go b/bindings/settings/trustednode/proposals.go new file mode 100644 index 000000000..3a23f416d --- /dev/null +++ b/bindings/settings/trustednode/proposals.go @@ -0,0 +1,127 @@ +package trustednode + +import ( + "fmt" + "math/big" + "sync" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + + trustednodedao "github.com/rocket-pool/smartnode/bindings/dao/trustednode" + "github.com/rocket-pool/smartnode/bindings/rocketpool" +) + +// Config +const ( + ProposalsSettingsContractName = "rocketDAONodeTrustedSettingsProposals" + CooldownTimeSettingPath = "proposal.cooldown.time" + VoteTimeSettingPath = "proposal.vote.time" + VoteDelayTimeSettingPath = "proposal.vote.delay.time" + ExecuteTimeSettingPath = "proposal.execute.time" + ActionTimeSettingPath = "proposal.action.time" +) + +// The cooldown period a member must wait after making a proposal before making another in seconds +func GetProposalCooldownTime(rp *rocketpool.RocketPool, opts *bind.CallOpts) (uint64, error) { + proposalsSettingsContract, err := getProposalsSettingsContract(rp, opts) + if err != nil { + return 0, err + } + value := new(*big.Int) + if err := proposalsSettingsContract.Call(opts, value, "getCooldownTime"); err != nil { + return 0, fmt.Errorf("error getting proposal cooldown period: %w", err) + } + return (*value).Uint64(), nil +} +func ProposeProposalCooldownTime(rp *rocketpool.RocketPool, value uint64, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return trustednodedao.ProposeSetUint(rp, fmt.Sprintf("set %s", CooldownTimeSettingPath), ProposalsSettingsContractName, CooldownTimeSettingPath, big.NewInt(int64(value)), opts) +} +func EstimateProposeProposalCooldownTimeGas(rp *rocketpool.RocketPool, value uint64, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return trustednodedao.EstimateProposeSetUintGas(rp, fmt.Sprintf("set %s", CooldownTimeSettingPath), ProposalsSettingsContractName, CooldownTimeSettingPath, big.NewInt(int64(value)), opts) +} + +// The period a proposal can be voted on for in seconds +func GetProposalVoteTime(rp *rocketpool.RocketPool, opts *bind.CallOpts) (uint64, error) { + proposalsSettingsContract, err := getProposalsSettingsContract(rp, opts) + if err != nil { + return 0, err + } + value := new(*big.Int) + if err := proposalsSettingsContract.Call(opts, value, "getVoteTime"); err != nil { + return 0, fmt.Errorf("error getting proposal voting period: %w", err) + } + return (*value).Uint64(), nil +} +func ProposeProposalVoteTime(rp *rocketpool.RocketPool, value uint64, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return trustednodedao.ProposeSetUint(rp, fmt.Sprintf("set %s", VoteTimeSettingPath), ProposalsSettingsContractName, VoteTimeSettingPath, big.NewInt(int64(value)), opts) +} +func EstimateProposeProposalVoteTimeGas(rp *rocketpool.RocketPool, value uint64, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return trustednodedao.EstimateProposeSetUintGas(rp, fmt.Sprintf("set %s", VoteTimeSettingPath), ProposalsSettingsContractName, VoteTimeSettingPath, big.NewInt(int64(value)), opts) +} + +// The delay after creation before a proposal can be voted on in seconds +func GetProposalVoteDelayTime(rp *rocketpool.RocketPool, opts *bind.CallOpts) (uint64, error) { + proposalsSettingsContract, err := getProposalsSettingsContract(rp, opts) + if err != nil { + return 0, err + } + value := new(*big.Int) + if err := proposalsSettingsContract.Call(opts, value, "getVoteDelayTime"); err != nil { + return 0, fmt.Errorf("error getting proposal voting delay: %w", err) + } + return (*value).Uint64(), nil +} +func ProposeProposalVoteDelayTime(rp *rocketpool.RocketPool, value uint64, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return trustednodedao.ProposeSetUint(rp, fmt.Sprintf("set %s", VoteDelayTimeSettingPath), ProposalsSettingsContractName, VoteDelayTimeSettingPath, big.NewInt(int64(value)), opts) +} +func EstimateProposeProposalVoteDelayTimeGas(rp *rocketpool.RocketPool, value uint64, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return trustednodedao.EstimateProposeSetUintGas(rp, fmt.Sprintf("set %s", VoteDelayTimeSettingPath), ProposalsSettingsContractName, VoteDelayTimeSettingPath, big.NewInt(int64(value)), opts) +} + +// The period during which a passed proposal can be executed in time +func GetProposalExecuteTime(rp *rocketpool.RocketPool, opts *bind.CallOpts) (uint64, error) { + proposalsSettingsContract, err := getProposalsSettingsContract(rp, opts) + if err != nil { + return 0, err + } + value := new(*big.Int) + if err := proposalsSettingsContract.Call(opts, value, "getExecuteTime"); err != nil { + return 0, fmt.Errorf("error getting proposal execution period: %w", err) + } + return (*value).Uint64(), nil +} +func ProposeProposalExecuteTime(rp *rocketpool.RocketPool, value uint64, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return trustednodedao.ProposeSetUint(rp, fmt.Sprintf("set %s", ExecuteTimeSettingPath), ProposalsSettingsContractName, ExecuteTimeSettingPath, big.NewInt(int64(value)), opts) +} +func EstimateProposeProposalExecuteTimeGas(rp *rocketpool.RocketPool, value uint64, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return trustednodedao.EstimateProposeSetUintGas(rp, fmt.Sprintf("set %s", ExecuteTimeSettingPath), ProposalsSettingsContractName, ExecuteTimeSettingPath, big.NewInt(int64(value)), opts) +} + +// The period during which an action can be performed on an executed proposal in seconds +func GetProposalActionTime(rp *rocketpool.RocketPool, opts *bind.CallOpts) (uint64, error) { + proposalsSettingsContract, err := getProposalsSettingsContract(rp, opts) + if err != nil { + return 0, err + } + value := new(*big.Int) + if err := proposalsSettingsContract.Call(opts, value, "getActionTime"); err != nil { + return 0, fmt.Errorf("error getting proposal action period: %w", err) + } + return (*value).Uint64(), nil +} +func ProposeProposalActionTime(rp *rocketpool.RocketPool, value uint64, opts *bind.TransactOpts) (uint64, common.Hash, error) { + return trustednodedao.ProposeSetUint(rp, fmt.Sprintf("set %s", ActionTimeSettingPath), ProposalsSettingsContractName, ActionTimeSettingPath, big.NewInt(int64(value)), opts) +} +func EstimateProposeProposalActionTimeGas(rp *rocketpool.RocketPool, value uint64, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return trustednodedao.EstimateProposeSetUintGas(rp, fmt.Sprintf("set %s", ActionTimeSettingPath), ProposalsSettingsContractName, ActionTimeSettingPath, big.NewInt(int64(value)), opts) +} + +// Get contracts +var proposalsSettingsContractLock sync.Mutex + +func getProposalsSettingsContract(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*rocketpool.Contract, error) { + proposalsSettingsContractLock.Lock() + defer proposalsSettingsContractLock.Unlock() + return rp.GetContract(ProposalsSettingsContractName, opts) +} diff --git a/bindings/settings/trustednode/rewards.go b/bindings/settings/trustednode/rewards.go new file mode 100644 index 000000000..f67867dcd --- /dev/null +++ b/bindings/settings/trustednode/rewards.go @@ -0,0 +1,39 @@ +package trustednode + +import ( + "fmt" + "math/big" + "sync" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + + "github.com/rocket-pool/smartnode/bindings/rocketpool" +) + +// Config +const ( + RewardsSettingsContractName string = "rocketDAONodeTrustedSettingsRewards" + NetworkEnabledPath string = "rewards.network.enabled" +) + +// Get whether or not the provided rewards network is enabled +func GetNetworkEnabled(rp *rocketpool.RocketPool, network *big.Int, opts *bind.CallOpts) (bool, error) { + rewardsSettingsContract, err := getRewardsSettingsContract(rp, opts) + if err != nil { + return false, err + } + value := new(bool) + if err := rewardsSettingsContract.Call(opts, value, "getNetworkEnabled", network); err != nil { + return false, fmt.Errorf("error checking if network %s is enabled: %w", network.String(), err) + } + return (*value), nil +} + +// Get contracts +var rewardsSettingsContractLock sync.Mutex + +func getRewardsSettingsContract(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*rocketpool.Contract, error) { + rewardsSettingsContractLock.Lock() + defer rewardsSettingsContractLock.Unlock() + return rp.GetContract(RewardsSettingsContractName, opts) +} diff --git a/bindings/storage/address-queue-storage.go b/bindings/storage/address-queue-storage.go new file mode 100644 index 000000000..46f77813d --- /dev/null +++ b/bindings/storage/address-queue-storage.go @@ -0,0 +1,61 @@ +package storage + +import ( + "fmt" + "math/big" + "sync" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/rocket-pool/smartnode/bindings/rocketpool" +) + +// low-level address queue storage interface. Currently only used for the minipool queue. + +// Return the length of all addresses matching the given key in the queue +func GetAddressQueueLength(rp *rocketpool.RocketPool, opts *bind.CallOpts, key [32]byte) (uint64, error) { + addressQueueStorage, err := getAddressQueueStorage(rp, opts) + if err != nil { + return 0, err + } + length := new(*big.Int) + if err := addressQueueStorage.Call(opts, length, "getIndexOf", key); err != nil { + return 0, fmt.Errorf("error getting address queue length for key: %w", key, err) + } + return (*length).Uint64(), nil +} + +// Return address item at index for the given key +func GetAddressQueueItem(rp *rocketpool.RocketPool, opts *bind.CallOpts, key [32]byte, index *big.Int) (common.Address, error) { + addressQueueStorage, err := getAddressQueueStorage(rp, opts) + if err != nil { + return common.Address{}, err + } + address := new(common.Address) + if err := addressQueueStorage.Call(opts, address, "getItem", key, index); err != nil { + return common.Address{}, fmt.Errorf("error getting address item at index %d: %w", index, key, err) + } + return *address, nil +} + +// Return index of the input address for the given key. -1 if not present. +func GetAddressQueueIndexOf(rp *rocketpool.RocketPool, opts *bind.CallOpts, key [32]byte, address common.Address) (int64, error) { + addressQueueStorage, err := getAddressQueueStorage(rp, opts) + if err != nil { + return 0, err + } + index := new(*big.Int) + if err := addressQueueStorage.Call(opts, index, "getIndexOf", key, address); err != nil { + return 0, fmt.Errorf("error getting index for address %s: %w", address.String(), err) + } + return (*index).Int64(), nil +} + +// Get contracts +var AddressQueueStorageLock sync.Mutex + +func getAddressQueueStorage(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*rocketpool.Contract, error) { + AddressQueueStorageLock.Lock() + defer AddressQueueStorageLock.Unlock() + return rp.GetContract("addressQueueStorage", opts) +} diff --git a/bindings/storage/linked-list-storage.go b/bindings/storage/linked-list-storage.go new file mode 100644 index 000000000..de884f729 --- /dev/null +++ b/bindings/storage/linked-list-storage.go @@ -0,0 +1,98 @@ +package storage + +import ( + "fmt" + "math/big" + "sync" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/rocket-pool/smartnode/bindings/rocketpool" +) + +type DepositQueueValue struct { + Receiver common.Address `abi:"receiver"` + ValidatorID uint32 `abi:"validatorId"` + SuppliedValue uint32 `abi:"suppliedValue"` + RequestedValue uint32 `abi:"requestedValue"` +} + +type Slice struct { + Entries []DepositQueueValue `abi:"entries"` + NextIndex *big.Int `abi:"nextIndex"` +} + +// Returns a slice of the specified queue along with the next index, starting at the supplied index +func Scan(rp *rocketpool.RocketPool, namespace [32]byte, startIndex *big.Int, count *big.Int, opts *bind.CallOpts) (Slice, error) { + linkedListStorage, err := getLinkedListStorage(rp, opts) + if err != nil { + return Slice{}, err + } + + slice := Slice{} + if err := linkedListStorage.Call(opts, &slice, "scan", namespace, startIndex, count); err != nil { + return Slice{}, fmt.Errorf("error getting slice of size %s for namespace %s starting at %s: %w", count, namespace, startIndex, err) + } + return slice, nil +} + +// Return the number of items in queue +func GetListLength(rp *rocketpool.RocketPool, namespace [32]byte, opts *bind.CallOpts) (*big.Int, error) { + linkedListStorage, err := getLinkedListStorage(rp, opts) + if err != nil { + return nil, err + } + length := new(*big.Int) + if err := linkedListStorage.Call(opts, length, "getLength", namespace); err != nil { + return nil, fmt.Errorf("error getting address queue length for namespace %s: %w", namespace, err) + } + return *length, nil +} + +// Return the item in queue by index +func GetListItem(rp *rocketpool.RocketPool, namespace [32]byte, index *big.Int, opts *bind.CallOpts) (DepositQueueValue, error) { + linkedListStorage, err := getLinkedListStorage(rp, opts) + if err != nil { + return DepositQueueValue{}, err + } + item := DepositQueueValue{} + if err := linkedListStorage.Call(opts, item, "getItem", namespace, index); err != nil { + return DepositQueueValue{}, fmt.Errorf("error getting item at index %s for namespace %s: %w", index, namespace, err) + } + return item, nil +} + +// Returns the item from the start of the queue without removing it +func PeekListItem(rp *rocketpool.RocketPool, namespace [32]byte, opts *bind.CallOpts) (DepositQueueValue, error) { + linkedListStorage, err := getLinkedListStorage(rp, opts) + if err != nil { + return DepositQueueValue{}, err + } + item := DepositQueueValue{} + if err := linkedListStorage.Call(opts, item, "peekItem", namespace); err != nil { + return DepositQueueValue{}, fmt.Errorf("error getting peeking the item for namespace %s: %w", namespace, err) + } + return item, nil +} + +// Returns the index of an item in queue. Returns 0 if the value is not found +func GetListQueueIndexOf(rp *rocketpool.RocketPool, namespace [32]byte, value DepositQueueValue, opts *bind.CallOpts) (*big.Int, error) { + linkedListStorage, err := getLinkedListStorage(rp, opts) + if err != nil { + return nil, err + } + queueIndex := new(*big.Int) + if err := linkedListStorage.Call(opts, queueIndex, "getIndexOf", namespace, value); err != nil { + return nil, fmt.Errorf("error getting linked list queue for namespace %s: %w", namespace, err) + } + return *queueIndex, nil +} + +// Get contracts +var LinkedListStorageLock sync.Mutex + +func getLinkedListStorage(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*rocketpool.Contract, error) { + LinkedListStorageLock.Lock() + defer LinkedListStorageLock.Unlock() + return rp.GetContract("linkedListStorage", opts) +} diff --git a/bindings/storage/rocket-storage.go b/bindings/storage/rocket-storage.go new file mode 100644 index 000000000..e5e161bbb --- /dev/null +++ b/bindings/storage/rocket-storage.go @@ -0,0 +1,68 @@ +package storage + +import ( + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/rocket-pool/smartnode/bindings/rocketpool" +) + +// Get a node's withdrawal address +func GetNodeWithdrawalAddress(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (common.Address, error) { + withdrawalAddress := new(common.Address) + if err := rp.RocketStorageContract.Call(opts, withdrawalAddress, "getNodeWithdrawalAddress", nodeAddress); err != nil { + return common.Address{}, fmt.Errorf("error getting node %s withdrawal address: %w", nodeAddress.Hex(), err) + } + return *withdrawalAddress, nil +} + +// Get a node's pending withdrawal address +func GetNodePendingWithdrawalAddress(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (common.Address, error) { + withdrawalAddress := new(common.Address) + if err := rp.RocketStorageContract.Call(opts, withdrawalAddress, "getNodePendingWithdrawalAddress", nodeAddress); err != nil { + return common.Address{}, fmt.Errorf("error getting node %s pending withdrawal address: %w", nodeAddress.Hex(), err) + } + return *withdrawalAddress, nil +} + +// Estimate the gas of SetWithdrawalAddress +func EstimateSetWithdrawalAddressGas(rp *rocketpool.RocketPool, nodeAddress common.Address, withdrawalAddress common.Address, confirm bool, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return rp.RocketStorageContract.GetTransactionGasInfo(opts, "setWithdrawalAddress", nodeAddress, withdrawalAddress, confirm) +} + +// Set a node's withdrawal address +func SetWithdrawalAddress(rp *rocketpool.RocketPool, nodeAddress common.Address, withdrawalAddress common.Address, confirm bool, opts *bind.TransactOpts) (common.Hash, error) { + tx, err := rp.RocketStorageContract.Transact(opts, "setWithdrawalAddress", nodeAddress, withdrawalAddress, confirm) + if err != nil { + return common.Hash{}, fmt.Errorf("error setting node withdrawal address: %w", err) + } + return tx.Hash(), nil +} + +// Estimate the gas of ConfirmWithdrawalAddress +func EstimateConfirmWithdrawalAddressGas(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return rp.RocketStorageContract.GetTransactionGasInfo(opts, "confirmWithdrawalAddress", nodeAddress) +} + +// Set a node's withdrawal address +func ConfirmWithdrawalAddress(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.TransactOpts) (common.Hash, error) { + tx, err := rp.RocketStorageContract.Transact(opts, "confirmWithdrawalAddress", nodeAddress) + if err != nil { + return common.Hash{}, fmt.Errorf("error confirming node withdrawal address: %w", err) + } + return tx.Hash(), nil +} + +// Get the number of the block that Rocket Pool was deployed on +func GetDeployBlock(rp *rocketpool.RocketPool) (*big.Int, error) { + deployBlockHash := crypto.Keccak256Hash([]byte("deploy.block")) + deployBlock, err := rp.RocketStorage.GetUint(nil, deployBlockHash) + if err != nil { + return nil, fmt.Errorf("error getting Rocket Pool deployment block: %w", err) + } + + return deployBlock, nil +} diff --git a/bindings/test.sh b/bindings/test.sh new file mode 100755 index 000000000..8dbf2e3de --- /dev/null +++ b/bindings/test.sh @@ -0,0 +1,141 @@ +#!/usr/bin/env bash + +# Exit if a command fails +set -o errexit + +# Check commands +if ! command -v git &> /dev/null; then + echo "git command required"; exit +fi +if ! command -v npm &> /dev/null; then + echo "npm command required"; exit +fi +if ! command -v go &> /dev/null; then + echo "go command required"; exit +fi + + +## +# Config +## + + +# Rocket Pool settings +rp_repo_url="git@ssh.dev.azure.com:v3/rocket-pool/RocketPool/rocketpool" +rp_repo_branch="v1.1" + +# Dependencies +rp_dependencies=( + "@openzeppelin/contracts@3.3.0" + "babel-polyfill@6.26.0" + "babel-register@6.26.0" + "ganache-cli@6.12.2" + "pako@1.0.11" + "truffle@5.1.66" + "truffle-contract@4.0.31" + "@truffle/hdwallet-provider@^1.2.3" + "web3@1.2.8" +) + +# Ganache settings +ganache_eth_balance="1000000" +ganache_gas_limit="12450000" +ganache_mnemonic="jungle neck govern chief unaware rubber frequent tissue service license alcohol velvet" +ganache_port="8545" + + +## +# Helpers +## + + +# Clean up +cleanup() { + + # Remove RP repo + if [ -d "$rp_tmp_path" ]; then + rm -rf "$rp_tmp_path" + fi + + # Kill ganache instance + if [ -n "$ganache_pid" ] && ps -p "$ganache_pid" > /dev/null; then + kill -9 "$ganache_pid" + fi + +} + +# Clone Rocket Pool repo +clone_rp() { + rp_tmp_path="$(mktemp -d)" + rp_path="$rp_tmp_path/rocketpool" + git clone "$rp_repo_url" -b "$rp_repo_branch" "$rp_path" +} + +# Install Rocket Pool dependencies +install_rp_deps() { + cd "$rp_path" + rm package.json package-lock.json + npm install "${rp_dependencies[@]}" + cd - > /dev/null +} + +# Start ganache-cli instance +start_ganache() { + cd "$rp_path" + node_modules/.bin/ganache-cli -e "$ganache_eth_balance" -l "$ganache_gas_limit" -m "$ganache_mnemonic" -p "$ganache_port" > /dev/null & + ganache_pid=$! + cd - > /dev/null +} + +# Migrate Rocket Pool contracts +migrate_rp() { + cd "$rp_path" + node_modules/.bin/truffle migrate --network localhost + cd - > /dev/null +} + +# Run tests +run_tests() { + go clean -testcache + go test -p 1 ./... +} + + +## +# Run +## + + +# Clean up before exiting +trap cleanup EXIT + +# Clone RP repo +echo "" +echo "Cloning main Rocket Pool repository..." +echo "" +clone_rp + +# Install RP deps +echo "" +echo "Installing Rocket Pool dependencies..." +echo "" +install_rp_deps + +# Start ganache +echo "" +echo "Starting ganache-cli process..." +echo "" +start_ganache + +# Migrate RP contracts +echo "" +echo "Migrating Rocket Pool contracts..." +echo "" +migrate_rp + +# Run tests +echo "" +echo "Running tests..." +echo "" +run_tests + diff --git a/bindings/tests/auction/auction_test.go b/bindings/tests/auction/auction_test.go new file mode 100644 index 000000000..c99d1fddd --- /dev/null +++ b/bindings/tests/auction/auction_test.go @@ -0,0 +1,333 @@ +package auction + +import ( + "math/big" + "testing" + + "github.com/rocket-pool/smartnode/bindings/settings/trustednode" + + "github.com/rocket-pool/smartnode/bindings/auction" + "github.com/rocket-pool/smartnode/bindings/network" + "github.com/rocket-pool/smartnode/bindings/settings/protocol" + "github.com/rocket-pool/smartnode/bindings/tokens" + "github.com/rocket-pool/smartnode/bindings/utils/eth" + + auctionutils "github.com/rocket-pool/smartnode/bindings/tests/testutils/auction" + "github.com/rocket-pool/smartnode/bindings/tests/testutils/evm" + nodeutils "github.com/rocket-pool/smartnode/bindings/tests/testutils/node" +) + +func TestAuctionDetails(t *testing.T) { + + // State snapshotting + if err := evm.TakeSnapshot(); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := evm.RevertSnapshot(); err != nil { + t.Fatal(err) + } + }) + + // Register node + if err := nodeutils.RegisterTrustedNode(rp, ownerAccount, trustedNodeAccount1); err != nil { + t.Fatal(err) + } + if err := nodeutils.RegisterTrustedNode(rp, ownerAccount, trustedNodeAccount2); err != nil { + t.Fatal(err) + } + if err := nodeutils.RegisterTrustedNode(rp, ownerAccount, trustedNodeAccount3); err != nil { + t.Fatal(err) + } + + // Disable min commission rate for unbonded pools + if _, err := trustednode.BootstrapMinipoolUnbondedMinFee(rp, uint64(0), ownerAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Get & check initial RPL balances + totalBalance1, err := auction.GetTotalRPLBalance(rp, nil) + if err != nil { + t.Fatal(err) + } + allottedBalance1, err := auction.GetAllottedRPLBalance(rp, nil) + if err != nil { + t.Fatal(err) + } + remainingBalance1, err := auction.GetRemainingRPLBalance(rp, nil) + if err != nil { + t.Fatal(err) + } + if totalBalance1.Cmp(big.NewInt(0)) != 0 { + t.Errorf("Incorrect initial auction contract total RPL balance %s", totalBalance1.String()) + } + if allottedBalance1.Cmp(big.NewInt(0)) != 0 { + t.Errorf("Incorrect initial auction contract allotted RPL balance %s", allottedBalance1.String()) + } + if remainingBalance1.Cmp(totalBalance1) != 0 { + t.Errorf("Incorrect initial auction contract remaining RPL balance %s", remainingBalance1.String()) + } + + // Mint slashed RPL to auction contract + if err := auctionutils.CreateSlashedRPL(t, rp, ownerAccount, trustedNodeAccount1, trustedNodeAccount2, userAccount1); err != nil { + t.Fatal(err) + } + + // Get & check updated RPL balances + totalBalance2, err := auction.GetTotalRPLBalance(rp, nil) + if err != nil { + t.Fatal(err) + } + allottedBalance2, err := auction.GetAllottedRPLBalance(rp, nil) + if err != nil { + t.Fatal(err) + } + remainingBalance2, err := auction.GetRemainingRPLBalance(rp, nil) + if err != nil { + t.Fatal(err) + } + if totalBalance2.Cmp(big.NewInt(0)) != 1 { + t.Errorf("Incorrect updated auction contract total RPL balance 1 %s", totalBalance2.String()) + } + if allottedBalance2.Cmp(big.NewInt(0)) != 0 { + t.Errorf("Incorrect updated auction contract allotted RPL balance 1 %s", allottedBalance2.String()) + } + if remainingBalance2.Cmp(totalBalance2) != 0 { + t.Errorf("Incorrect updated auction contract remaining RPL balance 1 %s", remainingBalance2.String()) + } + + // Create a new lot + if _, _, err := auction.CreateLot(rp, userAccount1.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Get & check updated RPL balances + totalBalance3, err := auction.GetTotalRPLBalance(rp, nil) + if err != nil { + t.Fatal(err) + } + allottedBalance3, err := auction.GetAllottedRPLBalance(rp, nil) + if err != nil { + t.Fatal(err) + } + remainingBalance3, err := auction.GetRemainingRPLBalance(rp, nil) + if err != nil { + t.Fatal(err) + } + var expectedRemainingBalance big.Int + expectedRemainingBalance.Sub(totalBalance3, allottedBalance3) + if allottedBalance3.Cmp(big.NewInt(0)) != 1 { + t.Errorf("Incorrect updated auction contract allotted RPL balance 2 %s", allottedBalance3.String()) + } + if remainingBalance3.Cmp(&expectedRemainingBalance) != 0 { + t.Errorf("Incorrect updated auction contract remaining RPL balance 2 %s", remainingBalance3.String()) + } + +} + +func TestLotDetails(t *testing.T) { + + // State snapshotting + if err := evm.TakeSnapshot(); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := evm.RevertSnapshot(); err != nil { + t.Fatal(err) + } + }) + + // Register node + if err := nodeutils.RegisterTrustedNode(rp, ownerAccount, trustedNodeAccount1); err != nil { + t.Fatal(err) + } + if err := nodeutils.RegisterTrustedNode(rp, ownerAccount, trustedNodeAccount2); err != nil { + t.Fatal(err) + } + if err := nodeutils.RegisterTrustedNode(rp, ownerAccount, trustedNodeAccount3); err != nil { + t.Fatal(err) + } + + // Disable min commission rate for unbonded pools + if _, err := trustednode.BootstrapMinipoolUnbondedMinFee(rp, uint64(0), ownerAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Set network parameters + if _, err := network.SubmitPrices(rp, 1, eth.EthToWei(1), eth.EthToWei(24), trustedNodeAccount1.GetTransactor()); err != nil { + t.Fatal(err) + } + if _, err := network.SubmitPrices(rp, 1, eth.EthToWei(1), eth.EthToWei(24), trustedNodeAccount2.GetTransactor()); err != nil { + t.Fatal(err) + } + if _, err := protocol.BootstrapLotStartingPriceRatio(rp, 1.0, ownerAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + if _, err := protocol.BootstrapLotReservePriceRatio(rp, 0.5, ownerAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + if _, err := protocol.BootstrapLotMaximumEthValue(rp, eth.EthToWei(10), ownerAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + if _, err := protocol.BootstrapLotDuration(rp, 5, ownerAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Mint slashed RPL to auction contract + if err := auctionutils.CreateSlashedRPL(t, rp, ownerAccount, trustedNodeAccount1, trustedNodeAccount2, userAccount1); err != nil { + t.Fatal(err) + } + + // Get & check initial lot details + if lots, err := auction.GetLots(rp, nil); err != nil { + t.Error(err) + } else if len(lots) != 0 { + t.Error("Incorrect initial lot count") + } + if lots, err := auction.GetLotsWithBids(rp, userAccount1.Address, nil); err != nil { + t.Error(err) + } else if len(lots) != 0 { + t.Error("Incorrect initial lot count") + } + + // Create lots + lot1Index, _, err := auction.CreateLot(rp, userAccount1.GetTransactor()) + if err != nil { + t.Fatal(err) + } + lot2Index, _, err := auction.CreateLot(rp, userAccount1.GetTransactor()) + if err != nil { + t.Fatal(err) + } + + // Place bid on lot 1 + bidAmount := eth.EthToWei(1) + bid1Opts := userAccount1.GetTransactor() + bid1Opts.Value = bidAmount + if _, err := auction.PlaceBid(rp, lot1Index, bid1Opts); err != nil { + t.Fatal(err) + } + + // Place another bid on lot 1 to clear it + bid2Opts := userAccount2.GetTransactor() + bid2Opts.Value = eth.EthToWei(1000) + if _, err := auction.PlaceBid(rp, lot1Index, bid2Opts); err != nil { + t.Fatal(err) + } + + // Mine blocks until lot 2 hits reserve price & recover unclaimed RPL from it + if err := evm.MineBlocks(5); err != nil { + t.Fatal(err) + } + if _, err := auction.RecoverUnclaimedRPL(rp, lot2Index, userAccount1.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Get & check updated lot details + if lots, err := auction.GetLots(rp, nil); err != nil { + t.Error(err) + } else if len(lots) != 2 { + t.Error("Incorrect updated lot count") + } else if lots[0].Index != lot1Index || lots[1].Index != lot2Index { + t.Error("Incorrect lot indexes") + } + if lots, err := auction.GetLotsWithBids(rp, userAccount1.Address, nil); err != nil { + t.Error(err) + } else if len(lots) != 2 { + t.Error("Incorrect updated lot count") + } else { + lot1 := lots[0] + lot2 := lots[1] + + // Lot 1 + if lot1.Index != lot1Index { + t.Errorf("Incorrect lot index %d", lot1.Index) + } + if !lot1.Exists { + t.Error("Incorrect lot exists status") + } + if lot1.StartBlock == 0 { + t.Errorf("Incorrect lot start block %d", lot1.StartBlock) + } + if lot1.EndBlock <= lot1.StartBlock { + t.Errorf("Incorrect lot end block %d", lot1.EndBlock) + } + if lot1.StartPrice.Cmp(eth.EthToWei(1)) != 0 { + t.Errorf("Incorrect lot start price %s", lot1.StartPrice.String()) + } + if lot1.ReservePrice.Cmp(eth.EthToWei(0.5)) != 0 { + t.Errorf("Incorrect lot reserve price %s", lot1.ReservePrice.String()) + } + if lot1.PriceAtCurrentBlock.Cmp(lot1.StartPrice) == 1 || lot1.PriceAtCurrentBlock.Cmp(lot1.ReservePrice) == -1 { + t.Errorf("Incorrect lot price at current block %s", lot1.PriceAtCurrentBlock.String()) + } + if lot1.PriceByTotalBids.Cmp(lot1.StartPrice) == 1 || lot1.PriceByTotalBids.Cmp(lot1.ReservePrice) == -1 { + t.Errorf("Incorrect lot price at current block %s", lot1.PriceByTotalBids.String()) + } + if lot1.CurrentPrice.Cmp(lot1.StartPrice) == 1 || lot1.CurrentPrice.Cmp(lot1.ReservePrice) == -1 { + t.Errorf("Incorrect lot price at current block %s", lot1.CurrentPrice.String()) + } + if lot1.TotalRPLAmount.Cmp(eth.EthToWei(10)) != 0 { + t.Errorf("Incorrect lot total RPL amount %s", lot1.TotalRPLAmount.String()) + } + if lot1.ClaimedRPLAmount.Cmp(eth.EthToWei(10)) != 0 { + t.Errorf("Incorrect lot claimed RPL amount %s", lot1.ClaimedRPLAmount.String()) + } + if lot1.RemainingRPLAmount.Cmp(big.NewInt(0)) != 0 { + t.Errorf("Incorrect lot remaining RPL amount %s", lot1.RemainingRPLAmount.String()) + } + if lot1.TotalBidAmount.Cmp(bidAmount) != 1 { + t.Errorf("Incorrect lot total bid amount %s", lot1.TotalBidAmount.String()) + } + if lot1.AddressBidAmount.Cmp(bidAmount) != 0 { + t.Errorf("Incorrect lot address bid amount %s", lot1.AddressBidAmount.String()) + } + if !lot1.Cleared { + t.Error("Incorrect lot cleared status") + } + if lot1.RPLRecovered { + t.Error("Incorrect lot RPL recovered status") + } + + // Lot 1 prices at blocks + if priceAtBlock, err := auction.GetLotPriceAtBlock(rp, lot1Index, 0, nil); err != nil { + t.Error(err) + } else if priceAtBlock.Cmp(lot1.StartPrice) != 0 { + t.Errorf("Incorrect lot price at block 1 %s", priceAtBlock.String()) + } + if priceAtBlock, err := auction.GetLotPriceAtBlock(rp, lot1Index, 1000000, nil); err != nil { + t.Error(err) + } else if priceAtBlock.Cmp(lot1.ReservePrice) != 0 { + t.Errorf("Incorrect lot price at block 2 %s", priceAtBlock.String()) + } + + // Lot 2 + if lot2.Index != lot2Index { + t.Errorf("Incorrect lot index %d", lot2.Index) + } + if !lot2.RPLRecovered { + t.Error("Incorrect lot RPL recovered status") + } + + } + + // Get & check initial bidder RPL balance + if rplBalance, err := tokens.GetRPLBalance(rp, userAccount1.Address, nil); err != nil { + t.Error(err) + } else if rplBalance.Cmp(big.NewInt(0)) != 0 { + t.Errorf("Incorrect initial bidder RPL balance %s", rplBalance.String()) + } + + // Claim bid on lot 1 + if _, err := auction.ClaimBid(rp, lot1Index, userAccount1.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Get & check updated bidder RPL balance + if rplBalance, err := tokens.GetRPLBalance(rp, userAccount1.Address, nil); err != nil { + t.Error(err) + } else if rplBalance.Cmp(big.NewInt(0)) != 1 { + t.Errorf("Incorrect updated bidder RPL balance %s", rplBalance.String()) + } + +} diff --git a/bindings/tests/auction/main_test.go b/bindings/tests/auction/main_test.go new file mode 100644 index 000000000..f9ad4a27b --- /dev/null +++ b/bindings/tests/auction/main_test.go @@ -0,0 +1,77 @@ +package auction + +import ( + "log" + "os" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethclient" + + "github.com/rocket-pool/smartnode/bindings/rocketpool" + + "github.com/rocket-pool/smartnode/bindings/tests" + "github.com/rocket-pool/smartnode/bindings/tests/testutils/accounts" + "github.com/rocket-pool/smartnode/bindings/tests/utils" +) + +var ( + client *ethclient.Client + rp *rocketpool.RocketPool + + ownerAccount *accounts.Account + trustedNodeAccount1 *accounts.Account + trustedNodeAccount2 *accounts.Account + trustedNodeAccount3 *accounts.Account + userAccount1 *accounts.Account + userAccount2 *accounts.Account +) + +func TestMain(m *testing.M) { + var err error + + // Initialize eth client + client, err = ethclient.Dial(tests.Eth1ProviderAddress) + if err != nil { + log.Fatal(err) + } + + // Initialize contract manager + rp, err = rocketpool.NewRocketPool(client, common.HexToAddress(tests.RocketStorageAddress)) + if err != nil { + log.Fatal(err) + } + + // Initialize accounts + ownerAccount, err = accounts.GetAccount(0) + if err != nil { + log.Fatal(err) + } + trustedNodeAccount1, err = accounts.GetAccount(1) + if err != nil { + log.Fatal(err) + } + trustedNodeAccount2, err = accounts.GetAccount(2) + if err != nil { + log.Fatal(err) + } + trustedNodeAccount3, err = accounts.GetAccount(3) + if err != nil { + log.Fatal(err) + } + userAccount1, err = accounts.GetAccount(8) + if err != nil { + log.Fatal(err) + } + userAccount2, err = accounts.GetAccount(9) + if err != nil { + log.Fatal(err) + } + + // Do the bootstrap settings + utils.Stage4Bootstrap(rp, ownerAccount) + + // Run tests + os.Exit(m.Run()) + +} diff --git a/bindings/tests/config.go b/bindings/tests/config.go new file mode 100644 index 000000000..38b17b950 --- /dev/null +++ b/bindings/tests/config.go @@ -0,0 +1,31 @@ +package tests + +// Contract addresses and account private keys are based on the following mnemonic: +// jungle neck govern chief unaware rubber frequent tissue service license alcohol velvet + +const ( + Eth1ProviderAddress = "http://127.0.0.1:8545" + RocketStorageAddress = "0x70a5F2eB9e4C003B105399b471DAeDbC8d00B1c5" +) + +const ( + ValidatorPubkey = "968bcf4081af4a10d054c1cde1dadfd6e85a120a397174173ca869f66bdc72835f9918ea251930778e5ba67a7907e30e" + ValidatorPubkey2 = "968bcf4081af4a10d054c1cde1dadfd6e85a120a397174173ca869f66bdc72835f9918ea251930778e5ba67a7907e30d" + ValidatorPubkey3 = "968bcf4081af4a10d054c1cde1dadfd6e85a120a397174173ca869f66bdc72835f9918ea251930778e5ba67a7907e30c" + ValidatorSignature = "83757098b3b118c67d993218afb69e80a13eb3b174cd3da9958971f05e6b30b9ff5a55677d644f972b31c24e0544604703e8cf18b109fde1e0d3cde0446147bf2f38f02fefce604e4119a605348dfc8a99935dbd65a64eb773c77508f9150e33" + ValidatorSignature2 = "83757098b3b118c67d993218afb69e80a13eb3b174cd3da9958971f05e6b30b9ff5a55677d644f972b31c24e0544604703e8cf18b109fde1e0d3cde0446147bf2f38f02fefce604e4119a605348dfc8a99935dbd65a64eb773c77508f9150e34" + ValidatorSignature3 = "83757098b3b118c67d993218afb69e80a13eb3b174cd3da9958971f05e6b30b9ff5a55677d644f972b31c24e0544604703e8cf18b109fde1e0d3cde0446147bf2f38f02fefce604e4119a605348dfc8a99935dbd65a64eb773c77508f9150e35" +) + +var AccountPrivateKeys = []string{ + "c6d2ac9b00bd599c4ce9d3a69c91e496eb9e79781d9dc84c79bafa7618f45f37", + "025515b79bbe5edf008112d19a14457e6bea72dc4660667eeb2c3225c8285618", + "02984e048155b5a3b80162a2041e096c3f99b9b4324bc7ff3e56e96d37f1500b", + "5894075a2b08d7585fd4b354914326da5c9b05f92a737b8789f127ba7a21f939", + "5a18d98ff88545ab82044b31ace49ad252056b89445913dc6a5653eca58c438a", + "ea8a7f5637ca1ae8ee6783850af1c0c57cdc5e66d1dcb92fd636908ad9b4cc04", + "836915de8841cd4e3a24b80c9c33e59be8db8ab3daf32d5edce56597b905bbf0", + "759b3437ff0fd1af70a5a367ac281c73f6dca2e17a4650a7f939fb50ad15f6cd", + "dde1c7fcfe3fa4c5e824e2e0cf5d8cef98692cde611b070d054045c2826aecb4", + "418bb76e4af529837d39f4812201c6e4b9b3d5d521f66047b6f34a6d7bc0c811", +} diff --git a/bindings/tests/dao/main_test.go b/bindings/tests/dao/main_test.go new file mode 100644 index 000000000..6f0813258 --- /dev/null +++ b/bindings/tests/dao/main_test.go @@ -0,0 +1,72 @@ +package dao + +import ( + "log" + "os" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethclient" + + "github.com/rocket-pool/smartnode/bindings/rocketpool" + + "github.com/rocket-pool/smartnode/bindings/tests" + "github.com/rocket-pool/smartnode/bindings/tests/testutils/accounts" + "github.com/rocket-pool/smartnode/bindings/tests/utils" +) + +var ( + client *ethclient.Client + rp *rocketpool.RocketPool + + ownerAccount *accounts.Account + trustedNodeAccount1 *accounts.Account + trustedNodeAccount2 *accounts.Account + trustedNodeAccount3 *accounts.Account + nodeAccount *accounts.Account +) + +func TestMain(m *testing.M) { + var err error + + // Initialize eth client + client, err = ethclient.Dial(tests.Eth1ProviderAddress) + if err != nil { + log.Fatal(err) + } + + // Initialize contract manager + rp, err = rocketpool.NewRocketPool(client, common.HexToAddress(tests.RocketStorageAddress)) + if err != nil { + log.Fatal(err) + } + + // Initialize accounts + ownerAccount, err = accounts.GetAccount(0) + if err != nil { + log.Fatal(err) + } + trustedNodeAccount1, err = accounts.GetAccount(1) + if err != nil { + log.Fatal(err) + } + trustedNodeAccount2, err = accounts.GetAccount(2) + if err != nil { + log.Fatal(err) + } + trustedNodeAccount3, err = accounts.GetAccount(3) + if err != nil { + log.Fatal(err) + } + nodeAccount, err = accounts.GetAccount(4) + if err != nil { + log.Fatal(err) + } + + // Do the bootstrap settings + utils.Stage4Bootstrap(rp, ownerAccount) + + // Run tests + os.Exit(m.Run()) + +} diff --git a/bindings/tests/dao/proposals_test.go b/bindings/tests/dao/proposals_test.go new file mode 100644 index 000000000..b197832aa --- /dev/null +++ b/bindings/tests/dao/proposals_test.go @@ -0,0 +1,209 @@ +package dao + +import ( + "bytes" + "fmt" + "testing" + + "github.com/rocket-pool/smartnode/bindings/dao" + trustednodedao "github.com/rocket-pool/smartnode/bindings/dao/trustednode" + "github.com/rocket-pool/smartnode/bindings/node" + trustednodesettings "github.com/rocket-pool/smartnode/bindings/settings/trustednode" + rptypes "github.com/rocket-pool/smartnode/bindings/types" + + "github.com/rocket-pool/smartnode/bindings/tests/testutils/evm" + nodeutils "github.com/rocket-pool/smartnode/bindings/tests/testutils/node" +) + +func TestProposalDetails(t *testing.T) { + + // State snapshotting + if err := evm.TakeSnapshot(); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := evm.RevertSnapshot(); err != nil { + t.Fatal(err) + } + }) + + // The DAO to check for proposals under + proposalDaoName := "rocketDAONodeTrustedProposals" + + // Set proposal cooldown + if _, err := trustednodesettings.BootstrapProposalCooldownTime(rp, 0, ownerAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + if _, err := trustednodesettings.BootstrapProposalVoteDelayTime(rp, 5, ownerAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Register nodes + if _, err := node.RegisterNode(rp, "Australia/Brisbane", nodeAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + if err := nodeutils.RegisterTrustedNode(rp, ownerAccount, trustedNodeAccount1); err != nil { + t.Fatal(err) + } + if err := nodeutils.RegisterTrustedNode(rp, ownerAccount, trustedNodeAccount2); err != nil { + t.Fatal(err) + } + if err := nodeutils.RegisterTrustedNode(rp, ownerAccount, trustedNodeAccount3); err != nil { + t.Fatal(err) + } + + // Get & check initial proposal details + if proposals, err := dao.GetProposals(rp, nil); err != nil { + t.Error(err) + } else if len(proposals) != 0 { + t.Error("Incorrect initial proposal count") + } + if proposals, err := dao.GetProposalsWithMember(rp, trustedNodeAccount1.Address, nil); err != nil { + t.Error(err) + } else if len(proposals) != 0 { + t.Error("Incorrect initial proposal count") + } + if daoProposals, err := dao.GetDAOProposals(rp, proposalDaoName, nil); err != nil { + t.Error(err) + } else if len(daoProposals) != 0 { + t.Error("Incorrect initial DAO proposal count") + } + if daoProposals, err := dao.GetDAOProposalsWithMember(rp, proposalDaoName, trustedNodeAccount1.Address, nil); err != nil { + t.Error(err) + } else if len(daoProposals) != 0 { + t.Error("Incorrect initial DAO proposal count") + } + + // Submit invite member proposal + proposalMessage := "invite coolguy" + proposalMemberAddress := nodeAccount.Address + proposalMemberId := "coolguy" + proposalMemberEmail := "coolguy@rocketpool.net" + proposalId, _, err := trustednodedao.ProposeInviteMember(rp, proposalMessage, proposalMemberAddress, proposalMemberId, proposalMemberEmail, trustedNodeAccount1.GetTransactor()) + if err != nil { + t.Fatal(err) + } + + // Increase time until proposal voting delay has passed + voteDelayTime, err := trustednodesettings.GetProposalVoteDelayTime(rp, nil) + if err != nil { + t.Fatal(err) + } + if err := evm.IncreaseTime(int(voteDelayTime)); err != nil { + t.Fatal(err) + } + + // Vote on & execute proposal + if _, err := trustednodedao.VoteOnProposal(rp, proposalId, true, trustedNodeAccount1.GetTransactor()); err != nil { + t.Fatal(err) + } + if _, err := trustednodedao.VoteOnProposal(rp, proposalId, true, trustedNodeAccount2.GetTransactor()); err != nil { + t.Fatal(err) + } + if _, err := trustednodedao.ExecuteProposal(rp, proposalId, trustedNodeAccount1.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Submit invite member proposal & cancel it + cancelledProposalId, _, err := trustednodedao.ProposeInviteMember(rp, "cancel this", nodeAccount.Address, "cancel", "cancel@rocketpool.net", trustedNodeAccount1.GetTransactor()) + if err != nil { + t.Fatal(err) + } + if _, err := trustednodedao.CancelProposal(rp, cancelledProposalId, trustedNodeAccount1.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Get & check updated proposal details + if proposals, err := dao.GetProposals(rp, nil); err != nil { + t.Error(err) + } else if len(proposals) != 2 { + t.Error("Incorrect updated proposal count") + } else if proposals[0].ID != proposalId || proposals[1].ID != cancelledProposalId { + t.Error("Incorrect proposal indexes") + } + if proposals, err := dao.GetProposalsWithMember(rp, trustedNodeAccount1.Address, nil); err != nil { + t.Error(err) + } else if len(proposals) != 2 { + t.Error("Incorrect updated proposal count") + } else { + + // Passed proposal + proposal := proposals[0] + if proposal.ID != proposalId { + t.Errorf("Incorrect proposal ID %d", proposal.ID) + } + if proposal.DAO != proposalDaoName { + t.Errorf("Incorrect proposal DAO %s", proposal.DAO) + } + if !bytes.Equal(proposal.ProposerAddress.Bytes(), trustedNodeAccount1.Address.Bytes()) { + t.Errorf("Incorrect proposal proposer address %s", proposal.ProposerAddress.Hex()) + } + if proposal.Message != proposalMessage { + t.Errorf("Incorrect proposal message %s", proposal.Message) + } + if proposal.CreatedTime == 0 { + t.Errorf("Incorrect proposal created time %d", proposal.CreatedTime) + } + if proposal.StartTime <= proposal.CreatedTime { + t.Errorf("Incorrect proposal start time %d", proposal.StartTime) + } + if proposal.EndTime <= proposal.StartTime { + t.Errorf("Incorrect proposal end time %d", proposal.EndTime) + } + if proposal.ExpiryTime <= proposal.EndTime { + t.Errorf("Incorrect proposal expiry time %d", proposal.ExpiryTime) + } + if proposal.VotesRequired == 0.0 { + t.Errorf("Incorrect proposal required votes %f", proposal.VotesRequired) + } + if proposal.VotesFor != 2.0 { + t.Errorf("Incorrect proposal votes for %f", proposal.VotesFor) + } + if proposal.VotesAgainst != 0.0 { + t.Errorf("Incorrect proposal votes against %f", proposal.VotesAgainst) + } + if !proposal.MemberVoted { + t.Error("Incorrect proposal member voted status") + } + if !proposal.MemberSupported { + t.Error("Incorrect proposal member supported status") + } + if proposal.IsCancelled { + t.Error("Incorrect proposal cancelled status") + } + if !proposal.IsExecuted { + t.Error("Incorrect proposal executed status") + } + if proposal.PayloadStr != fmt.Sprintf("proposalInvite(%s,%s,%s)", proposalMemberId, proposalMemberEmail, proposalMemberAddress.Hex()) { + t.Errorf("Incorrect proposal payload string %s", proposal.PayloadStr) + } + if proposal.State != rptypes.Executed { + t.Errorf("Incorrect proposal state %s", proposal.State.String()) + } + + // Cancelled proposal + cancelledProposal := proposals[1] + if cancelledProposal.ID != cancelledProposalId { + t.Errorf("Incorrect cancelled proposal ID %d", cancelledProposal.ID) + } + if !cancelledProposal.IsCancelled { + t.Error("Incorrect cancelled proposal cancelled status") + } + + } + if daoProposals, err := dao.GetDAOProposals(rp, proposalDaoName, nil); err != nil { + t.Error(err) + } else if len(daoProposals) != 2 { + t.Error("Incorrect updated DAO proposal count") + } else if daoProposals[0].ID != proposalId || daoProposals[1].ID != cancelledProposalId { + t.Error("Incorrect DAO proposal indexes") + } + if daoProposals, err := dao.GetDAOProposalsWithMember(rp, proposalDaoName, trustedNodeAccount1.Address, nil); err != nil { + t.Error(err) + } else if len(daoProposals) != 2 { + t.Error("Incorrect updated DAO proposal count") + } else if daoProposals[0].ID != proposalId || daoProposals[1].ID != cancelledProposalId { + t.Error("Incorrect DAO proposal indexes") + } + +} diff --git a/bindings/tests/dao/trustednode/dao_test.go b/bindings/tests/dao/trustednode/dao_test.go new file mode 100644 index 000000000..effee400c --- /dev/null +++ b/bindings/tests/dao/trustednode/dao_test.go @@ -0,0 +1,187 @@ +package trustednode + +import ( + "bytes" + "testing" + + "github.com/ethereum/go-ethereum/common" + + trustednodedao "github.com/rocket-pool/smartnode/bindings/dao/trustednode" + "github.com/rocket-pool/smartnode/bindings/node" + trustednodesettings "github.com/rocket-pool/smartnode/bindings/settings/trustednode" + "github.com/rocket-pool/smartnode/bindings/utils/eth" + + "github.com/rocket-pool/smartnode/bindings/tests/testutils/evm" + minipoolutils "github.com/rocket-pool/smartnode/bindings/tests/testutils/minipool" + nodeutils "github.com/rocket-pool/smartnode/bindings/tests/testutils/node" +) + +func TestMemberDetails(t *testing.T) { + + // State snapshotting + if err := evm.TakeSnapshot(); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := evm.RevertSnapshot(); err != nil { + t.Fatal(err) + } + }) + + // Disable min commission rate for unbonded pools + if _, err := trustednodesettings.BootstrapMinipoolUnbondedMinFee(rp, uint64(0), ownerAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Get & check minimum member count + if minMemberCount, err := trustednodedao.GetMinimumMemberCount(rp, nil); err != nil { + t.Error(err) + } else if minMemberCount == 0 { + t.Error("Incorrect trusted node DAO minimum member count") + } + + // Get & check initial member details + if members, err := trustednodedao.GetMembers(rp, nil); err != nil { + t.Error(err) + } else if len(members) != 0 { + t.Error("Incorrect initial trusted node DAO member count") + } + + // Set proposal cooldown + if _, err := trustednodesettings.BootstrapProposalCooldownTime(rp, 0, ownerAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Register nodes + if _, err := node.RegisterNode(rp, "Australia/Brisbane", trustedNodeAccount1.GetTransactor()); err != nil { + t.Fatal(err) + } + if _, err := node.RegisterNode(rp, "Australia/Brisbane", trustedNodeAccount2.GetTransactor()); err != nil { + t.Fatal(err) + } + if _, err := node.RegisterNode(rp, "Australia/Brisbane", trustedNodeAccount3.GetTransactor()); err != nil { + t.Fatal(err) + } + if _, err := node.RegisterNode(rp, "Australia/Brisbane", nodeAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Bootstrap trusted node DAO member + memberId := "coolguy" + memberEmail := "coolguy@rocketpool.net" + if _, err := trustednodedao.BootstrapMember(rp, memberId, memberEmail, trustedNodeAccount1.Address, ownerAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + if _, err := trustednodedao.BootstrapMember(rp, memberId, memberEmail, trustedNodeAccount2.Address, ownerAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + if _, err := trustednodedao.BootstrapMember(rp, memberId, memberEmail, trustedNodeAccount3.Address, ownerAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Get RPL bond amount + rplBondAmount, err := trustednodesettings.GetRPLBond(rp, nil) + if err != nil { + t.Fatal(err) + } + + // Mint trusted node RPL bond & join trusted node DAO + if err := nodeutils.MintTrustedNodeBond(rp, ownerAccount, trustedNodeAccount1); err != nil { + t.Fatal(err) + } + if err := nodeutils.MintTrustedNodeBond(rp, ownerAccount, trustedNodeAccount2); err != nil { + t.Fatal(err) + } + if err := nodeutils.MintTrustedNodeBond(rp, ownerAccount, trustedNodeAccount3); err != nil { + t.Fatal(err) + } + if _, err := trustednodedao.Join(rp, trustedNodeAccount1.GetTransactor()); err != nil { + t.Fatal(err) + } + if _, err := trustednodedao.Join(rp, trustedNodeAccount2.GetTransactor()); err != nil { + t.Fatal(err) + } + if _, err := trustednodedao.Join(rp, trustedNodeAccount3.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Submit a proposal + if _, _, err := trustednodedao.ProposeMemberLeave(rp, "bye", trustedNodeAccount1.Address, trustedNodeAccount1.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Create an unbonded minipool + if _, err := minipoolutils.CreateMinipool(t, rp, ownerAccount, trustedNodeAccount1, eth.EthToWei(16), 1); err != nil { + t.Fatal(err) + } + + // Get & check updated member details + if members, err := trustednodedao.GetMembers(rp, nil); err != nil { + t.Error(err) + } else if len(members) != 3 { + t.Error("Incorrect updated trusted node DAO member count") + } else { + member := members[0] + if !bytes.Equal(member.Address.Bytes(), trustedNodeAccount1.Address.Bytes()) { + t.Errorf("Incorrect member address %s", member.Address.Hex()) + } + if !member.Exists { + t.Error("Incorrect member exists status") + } + if member.ID != memberId { + t.Errorf("Incorrect member ID %s", member.ID) + } + if member.Url != memberEmail { + t.Errorf("Incorrect member email %s", member.Url) + } + if member.JoinedTime == 0 { + t.Errorf("Incorrect member joined time %d", member.JoinedTime) + } + if member.LastProposalTime == 0 { + t.Errorf("Incorrect member last proposal time %d", member.LastProposalTime) + } + if member.RPLBondAmount.Cmp(rplBondAmount) != 0 { + t.Errorf("Incorrect member RPL bond amount %s", member.RPLBondAmount.String()) + } + /* TEMPORARILY DISABLED + if member.UnbondedValidatorCount != 1 { + t.Errorf("Incorrect member unbonded validator count %d", member.UnbondedValidatorCount) + } + */ + } + +} + +func TestUpgradeContract(t *testing.T) { + + // State snapshotting + if err := evm.TakeSnapshot(); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := evm.RevertSnapshot(); err != nil { + t.Fatal(err) + } + }) + + // Upgrade contract + contractName := "rocketDepositPool" + contractNewAddress := common.HexToAddress("0x1111111111111111111111111111111111111111") + contractNewAbi := "[{\"name\":\"foo\",\"type\":\"function\",\"inputs\":[],\"outputs\":[]}]" + if _, err := trustednodedao.BootstrapUpgrade(rp, "upgradeContract", contractName, contractNewAbi, contractNewAddress, ownerAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Get & check updated contract details + if contractAddress, err := rp.GetAddress(contractName); err != nil { + t.Error(err) + } else if !bytes.Equal(contractAddress.Bytes(), contractNewAddress.Bytes()) { + t.Errorf("Incorrect updated contract address %s", contractAddress.Hex()) + } + if contractAbi, err := rp.GetABI(contractName); err != nil { + t.Error(err) + } else if _, ok := contractAbi.Methods["foo"]; !ok { + t.Errorf("Incorrect updated contract ABI") + } + +} diff --git a/bindings/tests/dao/trustednode/main_test.go b/bindings/tests/dao/trustednode/main_test.go new file mode 100644 index 000000000..75aa2bd2e --- /dev/null +++ b/bindings/tests/dao/trustednode/main_test.go @@ -0,0 +1,73 @@ +package trustednode + +import ( + "log" + "os" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethclient" + + "github.com/rocket-pool/smartnode/bindings/rocketpool" + + "github.com/rocket-pool/smartnode/bindings/tests" + "github.com/rocket-pool/smartnode/bindings/tests/testutils/accounts" +) + +var ( + client *ethclient.Client + rp *rocketpool.RocketPool + + ownerAccount *accounts.Account + trustedNodeAccount1 *accounts.Account + trustedNodeAccount2 *accounts.Account + trustedNodeAccount3 *accounts.Account + trustedNodeAccount4 *accounts.Account + nodeAccount *accounts.Account +) + +func TestMain(m *testing.M) { + var err error + + // Initialize eth client + client, err = ethclient.Dial(tests.Eth1ProviderAddress) + if err != nil { + log.Fatal(err) + } + + // Initialize contract manager + rp, err = rocketpool.NewRocketPool(client, common.HexToAddress(tests.RocketStorageAddress)) + if err != nil { + log.Fatal(err) + } + + // Initialize accounts + ownerAccount, err = accounts.GetAccount(0) + if err != nil { + log.Fatal(err) + } + trustedNodeAccount1, err = accounts.GetAccount(1) + if err != nil { + log.Fatal(err) + } + trustedNodeAccount2, err = accounts.GetAccount(2) + if err != nil { + log.Fatal(err) + } + trustedNodeAccount3, err = accounts.GetAccount(3) + if err != nil { + log.Fatal(err) + } + trustedNodeAccount4, err = accounts.GetAccount(4) + if err != nil { + log.Fatal(err) + } + nodeAccount, err = accounts.GetAccount(5) + if err != nil { + log.Fatal(err) + } + + // Run tests + os.Exit(m.Run()) + +} diff --git a/bindings/tests/dao/trustednode/proposals_test.go b/bindings/tests/dao/trustednode/proposals_test.go new file mode 100644 index 000000000..7221e5027 --- /dev/null +++ b/bindings/tests/dao/trustednode/proposals_test.go @@ -0,0 +1,321 @@ +package trustednode + +import ( + "bytes" + "fmt" + "testing" + + "github.com/ethereum/go-ethereum/common" + + "github.com/rocket-pool/smartnode/bindings/dao" + trustednodedao "github.com/rocket-pool/smartnode/bindings/dao/trustednode" + "github.com/rocket-pool/smartnode/bindings/node" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + trustednodesettings "github.com/rocket-pool/smartnode/bindings/settings/trustednode" + "github.com/rocket-pool/smartnode/bindings/utils/eth" + + "github.com/rocket-pool/smartnode/bindings/tests/testutils/accounts" + daoutils "github.com/rocket-pool/smartnode/bindings/tests/testutils/dao" + "github.com/rocket-pool/smartnode/bindings/tests/testutils/evm" + nodeutils "github.com/rocket-pool/smartnode/bindings/tests/testutils/node" +) + +func TestProposeInviteMember(t *testing.T) { + + // State snapshotting + if err := evm.TakeSnapshot(); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := evm.RevertSnapshot(); err != nil { + t.Fatal(err) + } + }) + + // Set proposal cooldown + if _, err := trustednodesettings.BootstrapProposalCooldownTime(rp, 0, ownerAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + if _, err := trustednodesettings.BootstrapProposalVoteDelayTime(rp, 5, ownerAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Register nodes + if _, err := node.RegisterNode(rp, "Australia/Brisbane", nodeAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + if err := nodeutils.RegisterTrustedNode(rp, ownerAccount, trustedNodeAccount1); err != nil { + t.Fatal(err) + } + if err := nodeutils.RegisterTrustedNode(rp, ownerAccount, trustedNodeAccount2); err != nil { + t.Fatal(err) + } + if err := nodeutils.RegisterTrustedNode(rp, ownerAccount, trustedNodeAccount3); err != nil { + t.Fatal(err) + } + + // Submit, pass & execute invite member proposal + proposalMemberAddress := nodeAccount.Address + proposalMemberId := "coolguy" + proposalMemberEmail := "coolguy@rocketpool.net" + proposalId, _, err := trustednodedao.ProposeInviteMember(rp, "invite coolguy", proposalMemberAddress, proposalMemberId, proposalMemberEmail, trustedNodeAccount1.GetTransactor()) + if err != nil { + t.Fatal(err) + } + if err := daoutils.PassAndExecuteProposal(rp, proposalId, []*accounts.Account{trustedNodeAccount1, trustedNodeAccount2}); err != nil { + t.Fatal(err) + } + + // Get & check initial member exists status + if exists, err := trustednodedao.GetMemberExists(rp, nodeAccount.Address, nil); err != nil { + t.Error(err) + } else if exists { + t.Error("Incorrect initial member exists status") + } + + // Mint trusted node RPL bond & join trusted node DAO + if err := nodeutils.MintTrustedNodeBond(rp, ownerAccount, nodeAccount); err != nil { + t.Fatal(err) + } + if _, err := trustednodedao.Join(rp, nodeAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Get & check updated member exists status + if exists, err := trustednodedao.GetMemberExists(rp, nodeAccount.Address, nil); err != nil { + t.Error(err) + } else if !exists { + t.Error("Incorrect updated member exists status") + } + + // Get & check proposal payload string + if payloadStr, err := dao.GetProposalPayloadStr(rp, proposalId, nil); err != nil { + t.Error(err) + } else if payloadStr != fmt.Sprintf("proposalInvite(%s,%s,%s)", proposalMemberId, proposalMemberEmail, proposalMemberAddress.Hex()) { + t.Errorf("Incorrect proposal payload string %s", payloadStr) + } + + // Get & check member invite executed block + if inviteExecutedTime, err := trustednodedao.GetMemberInviteProposalExecutedTime(rp, proposalMemberAddress, nil); err != nil { + t.Error(err) + } else if inviteExecutedTime == 0 { + t.Errorf("Incorrect member invite proposal executed time %d", inviteExecutedTime) + } + +} + +func TestProposeMemberLeave(t *testing.T) { + + // State snapshotting + if err := evm.TakeSnapshot(); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := evm.RevertSnapshot(); err != nil { + t.Fatal(err) + } + }) + + // Set proposal cooldown + if _, err := trustednodesettings.BootstrapProposalCooldownTime(rp, 0, ownerAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + if _, err := trustednodesettings.BootstrapProposalVoteDelayTime(rp, 5, ownerAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Register nodes + if err := nodeutils.RegisterTrustedNode(rp, ownerAccount, trustedNodeAccount1); err != nil { + t.Fatal(err) + } + if err := nodeutils.RegisterTrustedNode(rp, ownerAccount, trustedNodeAccount2); err != nil { + t.Fatal(err) + } + if err := nodeutils.RegisterTrustedNode(rp, ownerAccount, trustedNodeAccount3); err != nil { + t.Fatal(err) + } + if err := nodeutils.RegisterTrustedNode(rp, ownerAccount, trustedNodeAccount4); err != nil { + t.Fatal(err) + } + + // Submit, pass & execute member leave proposal + proposalMemberAddress := trustedNodeAccount1.Address + proposalId, _, err := trustednodedao.ProposeMemberLeave(rp, "node 1 leave", proposalMemberAddress, trustedNodeAccount1.GetTransactor()) + if err != nil { + t.Fatal(err) + } + if err := daoutils.PassAndExecuteProposal(rp, proposalId, []*accounts.Account{ + trustedNodeAccount1, + trustedNodeAccount2, + trustedNodeAccount3, + trustedNodeAccount4, + }); err != nil { + t.Fatal(err) + } + + // Get & check member leave executed time + if leaveExecutedTime, err := trustednodedao.GetMemberLeaveProposalExecutedTime(rp, proposalMemberAddress, nil); err != nil { + t.Error(err) + } else if leaveExecutedTime == 0 { + t.Errorf("Incorrect member leave proposal executed time %d", leaveExecutedTime) + } + + // Get & check initial member exists status + if exists, err := trustednodedao.GetMemberExists(rp, trustedNodeAccount1.Address, nil); err != nil { + t.Error(err) + } else if !exists { + t.Error("Incorrect initial member exists status") + } + + // Leave trusted node DAO + if _, err := trustednodedao.Leave(rp, trustedNodeAccount1.Address, trustedNodeAccount1.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Get & check updated member exists status + if exists, err := trustednodedao.GetMemberExists(rp, trustedNodeAccount1.Address, nil); err != nil { + t.Error(err) + } else if exists { + t.Error("Incorrect updated member exists status") + } + + // Get & check proposal payload string + if payloadStr, err := dao.GetProposalPayloadStr(rp, proposalId, nil); err != nil { + t.Error(err) + } else if payloadStr != fmt.Sprintf("proposalLeave(%s)", proposalMemberAddress.Hex()) { + t.Errorf("Incorrect proposal payload string %s", payloadStr) + } + +} + +func TestProposeKickMember(t *testing.T) { + + // State snapshotting + if err := evm.TakeSnapshot(); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := evm.RevertSnapshot(); err != nil { + t.Fatal(err) + } + }) + + // Set proposal cooldown + if _, err := trustednodesettings.BootstrapProposalCooldownTime(rp, 0, ownerAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + if _, err := trustednodesettings.BootstrapProposalVoteDelayTime(rp, 5, ownerAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Register nodes + if err := nodeutils.RegisterTrustedNode(rp, ownerAccount, trustedNodeAccount1); err != nil { + t.Fatal(err) + } + if err := nodeutils.RegisterTrustedNode(rp, ownerAccount, trustedNodeAccount2); err != nil { + t.Fatal(err) + } + if err := nodeutils.RegisterTrustedNode(rp, ownerAccount, trustedNodeAccount3); err != nil { + t.Fatal(err) + } + + // Get & check initial member exists status + if exists, err := trustednodedao.GetMemberExists(rp, trustedNodeAccount2.Address, nil); err != nil { + t.Error(err) + } else if !exists { + t.Error("Incorrect initial member exists status") + } + + // Submit, pass & execute kick member proposal + proposalMemberAddress := trustedNodeAccount2.Address + proposalFineAmount := eth.EthToWei(1000) + proposalId, _, err := trustednodedao.ProposeKickMember(rp, "kick node 2", proposalMemberAddress, proposalFineAmount, trustedNodeAccount1.GetTransactor()) + if err != nil { + t.Fatal(err) + } + if err := daoutils.PassAndExecuteProposal(rp, proposalId, []*accounts.Account{trustedNodeAccount1, trustedNodeAccount2}); err != nil { + t.Fatal(err) + } + + // Get & check updated member exists status + if exists, err := trustednodedao.GetMemberExists(rp, trustedNodeAccount2.Address, nil); err != nil { + t.Error(err) + } else if exists { + t.Error("Incorrect updated member exists status") + } + + // Get & check proposal payload string + if payloadStr, err := dao.GetProposalPayloadStr(rp, proposalId, nil); err != nil { + t.Error(err) + } else if payloadStr != fmt.Sprintf("proposalKick(%s,%s)", proposalMemberAddress.Hex(), proposalFineAmount.String()) { + t.Errorf("Incorrect proposal payload string %s", payloadStr) + } + +} + +func TestProposeUpgradeContract(t *testing.T) { + + // State snapshotting + if err := evm.TakeSnapshot(); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := evm.RevertSnapshot(); err != nil { + t.Fatal(err) + } + }) + + // Set proposal cooldown + if _, err := trustednodesettings.BootstrapProposalCooldownTime(rp, 0, ownerAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + if _, err := trustednodesettings.BootstrapProposalVoteDelayTime(rp, 5, ownerAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Register node + if err := nodeutils.RegisterTrustedNode(rp, ownerAccount, trustedNodeAccount1); err != nil { + t.Fatal(err) + } + if err := nodeutils.RegisterTrustedNode(rp, ownerAccount, trustedNodeAccount2); err != nil { + t.Fatal(err) + } + if err := nodeutils.RegisterTrustedNode(rp, ownerAccount, trustedNodeAccount3); err != nil { + t.Fatal(err) + } + + // Submit, pass & execute upgrade contract proposal + proposalUpgradeType := "upgradeContract" + proposalContractName := "rocketDepositPool" + proposalContractAddress := common.HexToAddress("0x1111111111111111111111111111111111111111") + proposalContractAbi := "[{\"name\":\"foo\",\"type\":\"function\",\"inputs\":[],\"outputs\":[]}]" + proposalId, _, err := trustednodedao.ProposeUpgradeContract(rp, "upgrade rocketDepositPool", proposalUpgradeType, proposalContractName, proposalContractAbi, proposalContractAddress, trustedNodeAccount1.GetTransactor()) + if err != nil { + t.Fatal(err) + } + if err := daoutils.PassAndExecuteProposal(rp, proposalId, []*accounts.Account{trustedNodeAccount1, trustedNodeAccount2}); err != nil { + t.Fatal(err) + } + + // Get & check updated contract details + if contractAddress, err := rp.GetAddress(proposalContractName); err != nil { + t.Error(err) + } else if !bytes.Equal(contractAddress.Bytes(), proposalContractAddress.Bytes()) { + t.Errorf("Incorrect updated contract address %s", contractAddress.Hex()) + } + if contractAbi, err := rp.GetABI(proposalContractName); err != nil { + t.Error(err) + } else if _, ok := contractAbi.Methods["foo"]; !ok { + t.Errorf("Incorrect updated contract ABI") + } + + // Get & check proposal payload string + if payloadStr, err := dao.GetProposalPayloadStr(rp, proposalId, nil); err != nil { + t.Error(err) + } else if encodedAbi, err := rocketpool.EncodeAbiStr(proposalContractAbi); err != nil { + t.Error(err) + } else if payloadStr != fmt.Sprintf("proposalUpgrade(%s,%s,%s,%s)", proposalUpgradeType, proposalContractName, encodedAbi, proposalContractAddress.Hex()) { + t.Errorf("Incorrect proposal payload string %s", payloadStr) + } + +} diff --git a/bindings/tests/deposit/deposit_test.go b/bindings/tests/deposit/deposit_test.go new file mode 100644 index 000000000..61459d22d --- /dev/null +++ b/bindings/tests/deposit/deposit_test.go @@ -0,0 +1,106 @@ +package deposit + +import ( + "testing" + + "github.com/rocket-pool/smartnode/bindings/deposit" + "github.com/rocket-pool/smartnode/bindings/node" + "github.com/rocket-pool/smartnode/bindings/settings/protocol" + "github.com/rocket-pool/smartnode/bindings/utils/eth" + + "github.com/rocket-pool/smartnode/bindings/tests/testutils/evm" + minipoolutils "github.com/rocket-pool/smartnode/bindings/tests/testutils/minipool" +) + +func TestDeposit(t *testing.T) { + + // State snapshotting + if err := evm.TakeSnapshot(); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := evm.RevertSnapshot(); err != nil { + t.Fatal(err) + } + }) + + // Make deposit + opts := userAccount.GetTransactor() + opts.Value = eth.EthToWei(10) + if _, err := deposit.Deposit(rp, opts); err != nil { + t.Fatal(err) + } + + // Get & check deposit pool balance + if balance, err := deposit.GetBalance(rp, nil); err != nil { + t.Error(err) + } else if balance.Cmp(opts.Value) != 0 { + t.Error("Incorrect deposit pool balance") + } + + // Get & check deposit pool excess balance + if excessBalance, err := deposit.GetExcessBalance(rp, nil); err != nil { + t.Error(err) + } else if excessBalance.Cmp(opts.Value) != 0 { + t.Error("Incorrect deposit pool excess balance") + } + +} + +func TestAssignDeposits(t *testing.T) { + + // State snapshotting + if err := evm.TakeSnapshot(); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := evm.RevertSnapshot(); err != nil { + t.Fatal(err) + } + }) + + // Disable deposit assignments + if _, err := protocol.BootstrapAssignDepositsEnabled(rp, false, ownerAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Make user deposit + userDepositOpts := userAccount.GetTransactor() + userDepositOpts.Value = eth.EthToWei(32) + if _, err := deposit.Deposit(rp, userDepositOpts); err != nil { + t.Fatal(err) + } + + // Register node & create minipool + if _, err := node.RegisterNode(rp, "Australia/Brisbane", nodeAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + if _, err := minipoolutils.CreateMinipool(t, rp, ownerAccount, nodeAccount, eth.EthToWei(16), 1); err != nil { + t.Fatal(err) + } + + // Re-enable deposit assignments + if _, err := protocol.BootstrapAssignDepositsEnabled(rp, true, ownerAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Get initial deposit pool balance + balance1, err := deposit.GetBalance(rp, nil) + if err != nil { + t.Fatal(err) + } + + // Assign deposits + if _, err := deposit.AssignDeposits(rp, userAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Get & check updated deposit pool balance + balance2, err := deposit.GetBalance(rp, nil) + if err != nil { + t.Fatal(err) + } else if balance2.Cmp(balance1) != -1 { + t.Error("Deposit pool balance did not decrease after assigning deposits") + } + +} diff --git a/bindings/tests/deposit/main_test.go b/bindings/tests/deposit/main_test.go new file mode 100644 index 000000000..b91d2707c --- /dev/null +++ b/bindings/tests/deposit/main_test.go @@ -0,0 +1,58 @@ +package deposit + +import ( + "log" + "os" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethclient" + + "github.com/rocket-pool/smartnode/bindings/rocketpool" + + "github.com/rocket-pool/smartnode/bindings/tests" + "github.com/rocket-pool/smartnode/bindings/tests/testutils/accounts" +) + +var ( + client *ethclient.Client + rp *rocketpool.RocketPool + + ownerAccount *accounts.Account + nodeAccount *accounts.Account + userAccount *accounts.Account +) + +func TestMain(m *testing.M) { + var err error + + // Initialize eth client + client, err = ethclient.Dial(tests.Eth1ProviderAddress) + if err != nil { + log.Fatal(err) + } + + // Initialize contract manager + rp, err = rocketpool.NewRocketPool(client, common.HexToAddress(tests.RocketStorageAddress)) + if err != nil { + log.Fatal(err) + } + + // Initialize accounts + ownerAccount, err = accounts.GetAccount(0) + if err != nil { + log.Fatal(err) + } + nodeAccount, err = accounts.GetAccount(1) + if err != nil { + log.Fatal(err) + } + userAccount, err = accounts.GetAccount(9) + if err != nil { + log.Fatal(err) + } + + // Run tests + os.Exit(m.Run()) + +} diff --git a/bindings/tests/minipool/contract_test.go b/bindings/tests/minipool/contract_test.go new file mode 100644 index 000000000..99ab8b342 --- /dev/null +++ b/bindings/tests/minipool/contract_test.go @@ -0,0 +1,757 @@ +package minipool + +import ( + "bytes" + "encoding/hex" + "fmt" + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + trustednodedao "github.com/rocket-pool/smartnode/bindings/dao/trustednode" + "github.com/rocket-pool/smartnode/bindings/settings/trustednode" + "github.com/rocket-pool/smartnode/bindings/utils" + + "github.com/rocket-pool/smartnode/bindings/deposit" + "github.com/rocket-pool/smartnode/bindings/minipool" + "github.com/rocket-pool/smartnode/bindings/network" + "github.com/rocket-pool/smartnode/bindings/node" + "github.com/rocket-pool/smartnode/bindings/tokens" + rptypes "github.com/rocket-pool/smartnode/bindings/types" + "github.com/rocket-pool/smartnode/bindings/utils/eth" + + "github.com/rocket-pool/smartnode/bindings/tests/testutils/evm" + minipoolutils "github.com/rocket-pool/smartnode/bindings/tests/testutils/minipool" + nodeutils "github.com/rocket-pool/smartnode/bindings/tests/testutils/node" + "github.com/rocket-pool/smartnode/bindings/tests/testutils/validator" +) + +func TestDetails(t *testing.T) { + + // State snapshotting + if err := evm.TakeSnapshot(); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := evm.RevertSnapshot(); err != nil { + t.Fatal(err) + } + }) + + // Register nodes + if _, err := node.RegisterNode(rp, "Australia/Brisbane", nodeAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + if err := nodeutils.RegisterTrustedNode(rp, ownerAccount, trustedNodeAccount); err != nil { + t.Fatal(err) + } + + // Get current network node fee + networkNodeFee, err := network.GetNodeFee(rp, nil) + if err != nil { + t.Fatal(err) + } + + // Create minipool + mp, err := minipoolutils.CreateMinipool(t, rp, ownerAccount, nodeAccount, eth.EthToWei(32), 1) + if err != nil { + t.Fatal(err) + } + + // Make user deposit + depositOpts := userAccount.GetTransactor() + depositOpts.Value = eth.EthToWei(16) + if _, err := deposit.Deposit(rp, depositOpts); err != nil { + t.Fatal(err) + } + + // Delay for the time between depositing and staking + scrubPeriod, err := trustednode.GetScrubPeriod(rp, nil) + if err != nil { + t.Fatal(err) + } + err = evm.IncreaseTime(int(scrubPeriod + 1)) + if err != nil { + t.Fatal(fmt.Errorf("error increasing time: %w", err)) + } + + // Stake minipool + if err := minipoolutils.StakeMinipool(rp, mp, nodeAccount); err != nil { + t.Fatal(err) + } + + // Set minipool withdrawable status + if _, err := minipool.SubmitMinipoolWithdrawable(rp, mp.Address, trustedNodeAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Get & check minipool details + if status, err := mp.GetStatusDetails(nil); err != nil { + t.Error(err) + } else { + if status.Status != rptypes.Withdrawable { + t.Errorf("Incorrect minipool status %s", status.Status.String()) + } + if status.StatusBlock == 0 { + t.Errorf("Incorrect minipool status block %d", status.StatusBlock) + } + if status.StatusTime.Unix() == 0 { + t.Errorf("Incorrect minipool status time %v", status.StatusTime) + } + } + if depositType, err := mp.GetDepositType(nil); err != nil { + t.Error(err) + } else if depositType != rptypes.Full { + t.Errorf("Incorrect minipool deposit type %s", depositType.String()) + } + if node, err := mp.GetNodeDetails(nil); err != nil { + t.Error(err) + } else { + if !bytes.Equal(node.Address.Bytes(), nodeAccount.Address.Bytes()) { + t.Errorf("Incorrect minipool node address %s", node.Address.Hex()) + } + if node.Fee != networkNodeFee { + t.Errorf("Incorrect minipool node fee %f", node.Fee) + } + if node.DepositBalance.Cmp(eth.EthToWei(16)) != 0 { + t.Errorf("Incorrect minipool node deposit balance %s", node.DepositBalance.String()) + } + if node.RefundBalance.Cmp(eth.EthToWei(16)) != 0 { + t.Errorf("Incorrect minipool node refund balance %s", node.RefundBalance.String()) + } + if !node.DepositAssigned { + t.Error("Incorrect minipool node deposit assigned status") + } + } + if user, err := mp.GetUserDetails(nil); err != nil { + t.Error(err) + } else { + if user.DepositBalance.Cmp(eth.EthToWei(16)) != 0 { + t.Errorf("Incorrect minipool user deposit balance %s", user.DepositBalance.String()) + } + if !user.DepositAssigned { + t.Error("Incorrect minipool user deposit assigned status") + } + if user.DepositAssignedTime.Unix() == 0 { + t.Errorf("Incorrect minipool user deposit assigned time %v", user.DepositAssignedTime) + } + } + if withdrawalCredentials, err := minipool.GetMinipoolWithdrawalCredentials(rp, mp.Address, nil); err != nil { + t.Error(err) + } else { + withdrawalPrefix := byte(1) + padding := make([]byte, 11) + expectedWithdrawalCredentials := bytes.Join([][]byte{{withdrawalPrefix}, padding, mp.Address.Bytes()}, []byte{}) + if !bytes.Equal(withdrawalCredentials.Bytes(), expectedWithdrawalCredentials) { + t.Errorf("Incorrect minipool withdrawal credentials %s", hex.EncodeToString(withdrawalCredentials.Bytes())) + } + } + +} + +func TestRefund(t *testing.T) { + + // State snapshotting + if err := evm.TakeSnapshot(); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := evm.RevertSnapshot(); err != nil { + t.Fatal(err) + } + }) + + // Register node + if _, err := node.RegisterNode(rp, "Australia/Brisbane", nodeAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Create minipool + mp, err := minipoolutils.CreateMinipool(t, rp, ownerAccount, nodeAccount, eth.EthToWei(32), 1) + if err != nil { + t.Fatal(err) + } + + // Make user deposit + depositOpts := userAccount.GetTransactor() + depositOpts.Value = eth.EthToWei(16) + if _, err := deposit.Deposit(rp, depositOpts); err != nil { + t.Fatal(err) + } + + // Get initial node refund balance + nodeRefundBalance1, err := mp.GetNodeRefundBalance(nil) + if err != nil { + t.Fatal(err) + } + + // Refund + if _, err := mp.Refund(nodeAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Get & check updated node refund balance + nodeRefundBalance2, err := mp.GetNodeRefundBalance(nil) + if err != nil { + t.Fatal(err) + } else if nodeRefundBalance2.Cmp(nodeRefundBalance1) != -1 { + t.Error("Node refund balance did not decrease after refunding from minipool") + } + +} + +func TestStake(t *testing.T) { + + // State snapshotting + if err := evm.TakeSnapshot(); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := evm.RevertSnapshot(); err != nil { + t.Fatal(err) + } + }) + + // Register node + if _, err := node.RegisterNode(rp, "Australia/Brisbane", nodeAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Create minipool + mp, err := minipoolutils.CreateMinipool(t, rp, ownerAccount, nodeAccount, eth.EthToWei(32), 1) + if err != nil { + t.Fatal(err) + } + + // Get validator & deposit data + validatorPubkey, err := validator.GetValidatorPubkey(1) + if err != nil { + t.Fatal(err) + } + withdrawalCredentials, err := minipool.GetMinipoolWithdrawalCredentials(rp, mp.Address, nil) + if err != nil { + t.Fatal(err) + } + validatorSignature, err := validator.GetValidatorSignature(1) + if err != nil { + t.Fatal(err) + } + depositDataRoot, err := validator.GetDepositDataRoot(validatorPubkey, withdrawalCredentials, validatorSignature) + if err != nil { + t.Fatal(err) + } + + // Get & check initial minipool status + if status, err := mp.GetStatus(nil); err != nil { + t.Error(err) + } else if status != rptypes.Prelaunch { + t.Errorf("Incorrect initial minipool status %s", status.String()) + } + + // Delay for the time between depositing and staking + scrubPeriod, err := trustednode.GetScrubPeriod(rp, nil) + if err != nil { + t.Fatal(err) + } + err = evm.IncreaseTime(int(scrubPeriod + 1)) + if err != nil { + t.Fatal(fmt.Errorf("error increasing time: %w", err)) + } + + // Stake minipool + if _, err := mp.Stake(validatorSignature, depositDataRoot, nodeAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Get & check updated minipool status + if status, err := mp.GetStatus(nil); err != nil { + t.Error(err) + } else if status != rptypes.Staking { + t.Errorf("Incorrect updated minipool status %s", status.String()) + } + +} + +func TestDissolve(t *testing.T) { + + // State snapshotting + if err := evm.TakeSnapshot(); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := evm.RevertSnapshot(); err != nil { + t.Fatal(err) + } + }) + + // Register node + if _, err := node.RegisterNode(rp, "Australia/Brisbane", nodeAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Create minipool + mp, err := minipoolutils.CreateMinipool(t, rp, ownerAccount, nodeAccount, eth.EthToWei(16), 1) + if err != nil { + t.Fatal(err) + } + + // Get & check initial minipool status + if status, err := mp.GetStatus(nil); err != nil { + t.Error(err) + } else if status != rptypes.Initialized { + t.Errorf("Incorrect initial minipool status %s", status.String()) + } + + // Dissolve minipool + if _, err := mp.Dissolve(nodeAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Get & check updated minipool status + if status, err := mp.GetStatus(nil); err != nil { + t.Error(err) + } else if status != rptypes.Dissolved { + t.Errorf("Incorrect updated minipool status %s", status.String()) + } + +} + +func TestClose(t *testing.T) { + + // State snapshotting + if err := evm.TakeSnapshot(); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := evm.RevertSnapshot(); err != nil { + t.Fatal(err) + } + }) + + // Register node + if _, err := node.RegisterNode(rp, "Australia/Brisbane", nodeAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Create minipool + mp, err := minipoolutils.CreateMinipool(t, rp, ownerAccount, nodeAccount, eth.EthToWei(16), 1) + if err != nil { + t.Fatal(err) + } + + // Dissolve minipool + if _, err := mp.Dissolve(nodeAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Get & check initial minipool exists status + if exists, err := minipool.GetMinipoolExists(rp, mp.Address, nil); err != nil { + t.Error(err) + } else if !exists { + t.Error("Incorrect initial minipool exists status") + } + + // Simulate a post-merge withdrawal by sending 16 ETH to the minipool + opts := nodeAccount.GetTransactor() + opts.Value = eth.EthToWei(16) + hash, err := eth.SendTransaction(rp.Client, mp.Address, big.NewInt(1337), opts) // Ganache's default chain ID is 1337 + if err != nil { + t.Errorf("Error sending ETH to minipool: %s", err.Error()) + } + utils.WaitForTransaction(rp.Client, hash) + + // Close minipool + if _, err := mp.Close(nodeAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Get & check updated minipool exists status + if exists, err := minipool.GetMinipoolExists(rp, mp.Address, nil); err != nil { + t.Error(err) + } else if exists { + t.Error("Incorrect updated minipool exists status") + } + +} + +func TestFinalise(t *testing.T) { + + // TODO + +} + +func TestWithdrawValidatorBalance(t *testing.T) { + + // State snapshotting + if err := evm.TakeSnapshot(); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := evm.RevertSnapshot(); err != nil { + t.Fatal(err) + } + }) + + // Register nodes + if _, err := node.RegisterNode(rp, "Australia/Brisbane", nodeAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + if err := nodeutils.RegisterTrustedNode(rp, ownerAccount, trustedNodeAccount); err != nil { + t.Fatal(err) + } + + // Create minipool + mp, err := minipoolutils.CreateMinipool(t, rp, ownerAccount, nodeAccount, eth.EthToWei(16), 1) + if err != nil { + t.Fatal(err) + } + + // Make user deposit + userDepositAmount := eth.EthToWei(16) + userDepositOpts := userAccount.GetTransactor() + userDepositOpts.Value = userDepositAmount + if _, err := deposit.Deposit(rp, userDepositOpts); err != nil { + t.Fatal(err) + } + + // Delay for the time between depositing and staking + scrubPeriod, err := trustednode.GetScrubPeriod(rp, nil) + if err != nil { + t.Fatal(err) + } + err = evm.IncreaseTime(int(scrubPeriod + 1)) + if err != nil { + t.Fatal(fmt.Errorf("error increasing time: %w", err)) + } + + // Stake minipool + if err := minipoolutils.StakeMinipool(rp, mp, nodeAccount); err != nil { + t.Fatal(err) + } + + // Set minipool withdrawable status + if _, err := minipool.SubmitMinipoolWithdrawable(rp, mp.Address, trustedNodeAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Get initial token contract ETH balances + rethContractBalance1, err := tokens.GetRETHContractETHBalance(rp, nil) + if err != nil { + t.Fatal(err) + } + + // Withdraw minipool validator balance + opts := swcAccount.GetTransactor() + opts.Value = eth.EthToWei(32) + if _, err := mp.Contract.Transfer(opts); err != nil { + t.Fatal(err) + } + + // Get node balances before withdrawal + nodeBalance1, err := tokens.GetBalances(rp, nodeAccount.Address, nil) + if err != nil { + t.Fatal(err) + } + + // Call ProcessWithdrawal method + if _, err := mp.DistributeBalance(nodeAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Call refund method to withdraw node's balance + if _, err := mp.Refund(nodeAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Get & check updated node ETH balances + if nodeBalance2, err := tokens.GetBalances(rp, nodeAccount.Address, nil); err != nil { + t.Fatal(err) + } else if nodeBalance2.ETH.Cmp(nodeBalance1.ETH) != 1 { + t.Error("node ETH balance did not increase after processing withdrawal") + } + + // Get & check updated token contract ETH balances + if rethContractBalance2, err := tokens.GetRETHContractETHBalance(rp, nil); err != nil { + t.Fatal(err) + } else if rethContractBalance2.Cmp(rethContractBalance1) != 1 { + t.Error("rETH contract ETH balance did not increase after processing withdrawal") + } + + // Get & check rETH collateral amount & rate + if rethTotalCollateral, err := tokens.GetRETHTotalCollateral(rp, nil); err != nil { + t.Fatal(err) + } else if rethTotalCollateral.Cmp(userDepositAmount) != 0 { + t.Errorf("Incorrect rETH total collateral amount %s", rethTotalCollateral.String()) + } + if rethCollateralRate, err := tokens.GetRETHCollateralRate(rp, nil); err != nil { + t.Fatal(err) + } else if rethCollateralRate != 1 { + t.Errorf("Incorrect rETH collateral rate %f", rethCollateralRate) + } + + // Confirm the minipool still exists + if exists, err := minipool.GetMinipoolExists(rp, mp.Address, nil); err != nil { + t.Error(err) + } else if !exists { + t.Error("Minipool no longer exists but it should") + } + +} + +func TestWithdrawValidatorBalanceAndFinalise(t *testing.T) { + + // State snapshotting + if err := evm.TakeSnapshot(); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := evm.RevertSnapshot(); err != nil { + t.Fatal(err) + } + }) + + // Register nodes + if _, err := node.RegisterNode(rp, "Australia/Brisbane", nodeAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + if err := nodeutils.RegisterTrustedNode(rp, ownerAccount, trustedNodeAccount); err != nil { + t.Fatal(err) + } + + // Create minipool + mp, err := minipoolutils.CreateMinipool(t, rp, ownerAccount, nodeAccount, eth.EthToWei(16), 1) + if err != nil { + t.Fatal(err) + } + + // Make user deposit + userDepositAmount := eth.EthToWei(16) + userDepositOpts := userAccount.GetTransactor() + userDepositOpts.Value = userDepositAmount + if _, err := deposit.Deposit(rp, userDepositOpts); err != nil { + t.Fatal(err) + } + + // Delay for the time between depositing and staking + scrubPeriod, err := trustednode.GetScrubPeriod(rp, nil) + if err != nil { + t.Fatal(err) + } + err = evm.IncreaseTime(int(scrubPeriod + 1)) + if err != nil { + t.Fatal(fmt.Errorf("error increasing time: %w", err)) + } + + // Stake minipool + if err := minipoolutils.StakeMinipool(rp, mp, nodeAccount); err != nil { + t.Fatal(err) + } + + // Set minipool withdrawable status + if _, err := minipool.SubmitMinipoolWithdrawable(rp, mp.Address, trustedNodeAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Get initial token contract ETH balances + rethContractBalance1, err := tokens.GetRETHContractETHBalance(rp, nil) + if err != nil { + t.Fatal(err) + } + + // Withdraw minipool validator balance + opts := swcAccount.GetTransactor() + opts.Value = eth.EthToWei(32) + if _, err := mp.Contract.Transfer(opts); err != nil { + t.Fatal(err) + } + + // Get node balances before withdrawal + nodeBalance1, err := tokens.GetBalances(rp, nodeAccount.Address, nil) + if err != nil { + t.Fatal(err) + } + + // Call DistributeBalanceAndFinalise method + if _, err := mp.DistributeBalanceAndFinalise(nodeAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Get & check updated node ETH balances + if nodeBalance2, err := tokens.GetBalances(rp, nodeAccount.Address, nil); err != nil { + t.Fatal(err) + } else if nodeBalance2.ETH.Cmp(nodeBalance1.ETH) != 1 { + t.Error("node ETH balance did not increase after processing withdrawal") + } + + // Get & check updated token contract ETH balances + if rethContractBalance2, err := tokens.GetRETHContractETHBalance(rp, nil); err != nil { + t.Fatal(err) + } else if rethContractBalance2.Cmp(rethContractBalance1) != 1 { + t.Error("rETH contract ETH balance did not increase after processing withdrawal") + } + + // Get & check rETH collateral amount & rate + if rethTotalCollateral, err := tokens.GetRETHTotalCollateral(rp, nil); err != nil { + t.Fatal(err) + } else if rethTotalCollateral.Cmp(userDepositAmount) != 0 { + t.Errorf("Incorrect rETH total collateral amount %s", rethTotalCollateral.String()) + } + if rethCollateralRate, err := tokens.GetRETHCollateralRate(rp, nil); err != nil { + t.Fatal(err) + } else if rethCollateralRate != 0.1 { + t.Errorf("Incorrect rETH collateral rate %f", rethCollateralRate) + } + + // Confirm the minipool still exists + if exists, err := minipool.GetMinipoolExists(rp, mp.Address, nil); err != nil { + t.Error(err) + } else if !exists { + t.Error("Minipool doesn't exist but it should") + } + +} + +func TestDelegateUpgradeAndRollback(t *testing.T) { + // State snapshotting + if err := evm.TakeSnapshot(); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := evm.RevertSnapshot(); err != nil { + t.Fatal(err) + } + }) + + // Register nodes + if _, err := node.RegisterNode(rp, "Australia/Brisbane", nodeAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + if err := nodeutils.RegisterTrustedNode(rp, ownerAccount, trustedNodeAccount); err != nil { + t.Fatal(err) + } + + // Create minipool + mp, err := minipoolutils.CreateMinipool(t, rp, ownerAccount, nodeAccount, eth.EthToWei(16), 1) + if err != nil { + t.Fatal(err) + } + + // Get original delegate contract + originalDelegate, err := mp.GetEffectiveDelegate(nil) + if err != nil { + t.Fatal(err) + } + + newDelegate := common.HexToAddress("0x1111111111111111111111111111111111111111") + newAbi := "[{\"name\":\"foo\",\"type\":\"function\",\"inputs\":[],\"outputs\":[]}]" + + // Upgrade the network delegate contract + _, err = trustednodedao.BootstrapUpgrade(rp, "upgradeContract", "rocketMinipoolDelegate", newAbi, newDelegate, ownerAccount.GetTransactor()) + if err != nil { + t.Fatal(err) + } + + // Get new effective delegate + effectiveDelegate, err := mp.GetEffectiveDelegate(nil) + if err != nil { + t.Fatal(err) + } + + // Check + if effectiveDelegate != originalDelegate { + t.Errorf("Effective delegate %s did not match original delegate %s", effectiveDelegate.Hex(), originalDelegate.Hex()) + } + + // Call upgrade + if _, err := mp.DelegateUpgrade(nodeAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Check effective delegate + if effectiveDelegate, err = mp.GetEffectiveDelegate(nil); err != nil { + t.Fatal(err) + } else if effectiveDelegate != newDelegate { + t.Errorf("Effective delegate %s did not match new delegate %s", effectiveDelegate.Hex(), newDelegate.Hex()) + } + + // Check previous delegate + if previousDelegate, err := mp.GetPreviousDelegate(nil); err != nil { + t.Fatal(err) + } else if previousDelegate != originalDelegate { + t.Errorf("Previous delegate %s did not match original delegate %s", previousDelegate.Hex(), originalDelegate.Hex()) + } + + // Check current delegate + if currentDelegate, err := mp.GetDelegate(nil); err != nil { + t.Fatal(err) + } else if currentDelegate != newDelegate { + t.Errorf("Current delegate %s did not match new delegate %s", currentDelegate.Hex(), newDelegate.Hex()) + } + + // Rollback + if _, err := mp.DelegateRollback(nodeAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Get new effective delegate + if effectiveDelegate, err = mp.GetEffectiveDelegate(nil); err != nil { + t.Fatal(err) + } else if effectiveDelegate != originalDelegate { + t.Errorf("Effective delegate %s did not match original delegate %s", effectiveDelegate.Hex(), newDelegate.Hex()) + } +} + +func TestUseLatestDelegate(t *testing.T) { + // State snapshotting + if err := evm.TakeSnapshot(); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := evm.RevertSnapshot(); err != nil { + t.Fatal(err) + } + }) + + // Register nodes + if _, err := node.RegisterNode(rp, "Australia/Brisbane", nodeAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + if err := nodeutils.RegisterTrustedNode(rp, ownerAccount, trustedNodeAccount); err != nil { + t.Fatal(err) + } + + // Create minipool + mp, err := minipoolutils.CreateMinipool(t, rp, ownerAccount, nodeAccount, eth.EthToWei(16), 1) + if err != nil { + t.Fatal(err) + } + + // New delegate params + newDelegate := common.HexToAddress("0x1111111111111111111111111111111111111111") + newAbi := "[{\"name\":\"foo\",\"type\":\"function\",\"inputs\":[],\"outputs\":[]}]" + + // Upgrade the network delegate contract + _, err = trustednodedao.BootstrapUpgrade(rp, "upgradeContract", "rocketMinipoolDelegate", newAbi, newDelegate, ownerAccount.GetTransactor()) + if err != nil { + t.Fatal(err) + } + + // Set use latest delegate + if _, err = mp.SetUseLatestDelegate(true, nodeAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Get use latest delegate + if useLatest, err := mp.GetUseLatestDelegate(nil); err != nil { + t.Fatal(err) + } else if !useLatest { + t.Error("GetUseLatestDelegate returned false after being set") + } + + // Check effective delegate + if effectiveDelegate, err := mp.GetEffectiveDelegate(nil); err != nil { + t.Fatal(err) + } else if effectiveDelegate != newDelegate { + t.Errorf("Effective delegate %s did not match new delegate %s", effectiveDelegate.Hex(), newDelegate.Hex()) + } +} diff --git a/bindings/tests/minipool/main_test.go b/bindings/tests/minipool/main_test.go new file mode 100644 index 000000000..8f85c0155 --- /dev/null +++ b/bindings/tests/minipool/main_test.go @@ -0,0 +1,68 @@ +package minipool + +import ( + "log" + "os" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethclient" + + "github.com/rocket-pool/smartnode/bindings/rocketpool" + + "github.com/rocket-pool/smartnode/bindings/tests" + "github.com/rocket-pool/smartnode/bindings/tests/testutils/accounts" +) + +var ( + client *ethclient.Client + rp *rocketpool.RocketPool + + ownerAccount *accounts.Account + trustedNodeAccount *accounts.Account + nodeAccount *accounts.Account + userAccount *accounts.Account + swcAccount *accounts.Account +) + +func TestMain(m *testing.M) { + var err error + + // Initialize eth client + client, err = ethclient.Dial(tests.Eth1ProviderAddress) + if err != nil { + log.Fatal(err) + } + + // Initialize contract manager + rp, err = rocketpool.NewRocketPool(client, common.HexToAddress(tests.RocketStorageAddress)) + if err != nil { + log.Fatal(err) + } + + // Initialize accounts + ownerAccount, err = accounts.GetAccount(0) + if err != nil { + log.Fatal(err) + } + trustedNodeAccount, err = accounts.GetAccount(1) + if err != nil { + log.Fatal(err) + } + nodeAccount, err = accounts.GetAccount(2) + if err != nil { + log.Fatal(err) + } + userAccount, err = accounts.GetAccount(8) + if err != nil { + log.Fatal(err) + } + swcAccount, err = accounts.GetAccount(9) + if err != nil { + log.Fatal(err) + } + + // Run tests + os.Exit(m.Run()) + +} diff --git a/bindings/tests/minipool/minipool_test.go b/bindings/tests/minipool/minipool_test.go new file mode 100644 index 000000000..6a9131353 --- /dev/null +++ b/bindings/tests/minipool/minipool_test.go @@ -0,0 +1,139 @@ +package minipool + +import ( + "bytes" + "fmt" + "testing" + + "github.com/rocket-pool/smartnode/bindings/settings/trustednode" + "github.com/rocket-pool/smartnode/bindings/types" + + "github.com/rocket-pool/smartnode/bindings/minipool" + "github.com/rocket-pool/smartnode/bindings/node" + "github.com/rocket-pool/smartnode/bindings/utils/eth" + + "github.com/rocket-pool/smartnode/bindings/tests/testutils/evm" + minipoolutils "github.com/rocket-pool/smartnode/bindings/tests/testutils/minipool" + nodeutils "github.com/rocket-pool/smartnode/bindings/tests/testutils/node" + "github.com/rocket-pool/smartnode/bindings/tests/testutils/validator" +) + +func TestMinipoolDetails(t *testing.T) { + + // State snapshotting + if err := evm.TakeSnapshot(); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := evm.RevertSnapshot(); err != nil { + t.Fatal(err) + } + }) + + // Register nodes + if _, err := node.RegisterNode(rp, "Australia/Brisbane", nodeAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + if err := nodeutils.RegisterTrustedNode(rp, ownerAccount, trustedNodeAccount); err != nil { + t.Fatal(err) + } + + // Get & check initial minipool details + if minipools, err := minipool.GetMinipools(rp, nil); err != nil { + t.Error(err) + } else if len(minipools) != 0 { + t.Error("Incorrect initial minipool count") + } + if nodeMinipools, err := minipool.GetNodeMinipools(rp, nodeAccount.Address, nil); err != nil { + t.Error(err) + } else if len(nodeMinipools) != 0 { + t.Error("Incorrect initial node minipool count") + } + if nodeMinipoolPubkeys, err := minipool.GetNodeValidatingMinipoolPubkeys(rp, nodeAccount.Address, nil); err != nil { + t.Error(err) + } else if len(nodeMinipoolPubkeys) != 0 { + t.Error("Incorrect initial node minipool pubkeys count") + } + + // Minipool deposit/withdrawal amounts + minipoolDepositAmount := eth.EthToWei(32) + + // Create & stake minipool + mp, err := minipoolutils.CreateMinipool(t, rp, ownerAccount, nodeAccount, minipoolDepositAmount, 1) + if err != nil { + t.Fatal(err) + } + + // Delay for the time between depositing and staking + scrubPeriod, err := trustednode.GetScrubPeriod(rp, nil) + if err != nil { + t.Fatal(err) + } + err = evm.IncreaseTime(int(scrubPeriod + 1)) + if err != nil { + t.Fatal(fmt.Errorf("error increasing time: %w", err)) + } + + if err := minipoolutils.StakeMinipool(rp, mp, nodeAccount); err != nil { + t.Fatal(err) + } + + // Mark minipool as withdrawable + if _, err := minipool.SubmitMinipoolWithdrawable(rp, mp.Address, trustedNodeAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Get minipool validator pubkey + validatorPubkey, err := validator.GetValidatorPubkey(1) + if err != nil { + t.Fatal(err) + } + + // Get & check updated minipool details + if minipools, err := minipool.GetMinipools(rp, nil); err != nil { + t.Error(err) + } else if len(minipools) != 1 { + t.Error("Incorrect updated minipool count") + } else { + mpDetails := minipools[0] + if !bytes.Equal(mpDetails.Address.Bytes(), mp.Address.Bytes()) { + t.Errorf("Incorrect minipool address %s", mpDetails.Address.Hex()) + } + if !mpDetails.Exists { + t.Error("Incorrect minipool exists status") + } + if !bytes.Equal(mpDetails.Pubkey.Bytes(), validatorPubkey.Bytes()) { + t.Errorf("Incorrect minipool validator pubkey %s", mpDetails.Pubkey.Hex()) + } + } + // Check status + if status, err := mp.GetStatus(nil); err != nil { + t.Error(err) + } else { + if status != types.Withdrawable { + t.Error("Incorrect minipool withdrawable status") + } + } + if nodeMinipools, err := minipool.GetNodeMinipools(rp, nodeAccount.Address, nil); err != nil { + t.Error(err) + } else if len(nodeMinipools) != 1 { + t.Error("Incorrect updated node minipool count") + } else if !bytes.Equal(nodeMinipools[0].Address.Bytes(), mp.Address.Bytes()) { + t.Errorf("Incorrect node minipool address %s", nodeMinipools[0].Address.Hex()) + } + if nodeMinipoolPubkeys, err := minipool.GetNodeValidatingMinipoolPubkeys(rp, nodeAccount.Address, nil); err != nil { + t.Error(err) + } else if len(nodeMinipoolPubkeys) != 1 { + t.Error("Incorrect updated node minipool pubkeys count") + } else if !bytes.Equal(nodeMinipoolPubkeys[0].Bytes(), validatorPubkey.Bytes()) { + t.Errorf("Incorrect node minipool pubkey %s", nodeMinipoolPubkeys[0].Hex()) + } + + // Get & check minipool address by pubkey + if minipoolAddress, err := minipool.GetMinipoolByPubkey(rp, validatorPubkey, nil); err != nil { + t.Error(err) + } else if !bytes.Equal(minipoolAddress.Bytes(), mp.Address.Bytes()) { + t.Errorf("Incorrect minipool address %s for pubkey %s", minipoolAddress.Hex(), validatorPubkey.Hex()) + } + +} diff --git a/bindings/tests/minipool/queue_test.go b/bindings/tests/minipool/queue_test.go new file mode 100644 index 000000000..5cf3cd9be --- /dev/null +++ b/bindings/tests/minipool/queue_test.go @@ -0,0 +1,229 @@ +package minipool + +import ( + "testing" + + trustednodesettings "github.com/rocket-pool/smartnode/bindings/settings/trustednode" + + "github.com/rocket-pool/smartnode/bindings/minipool" + "github.com/rocket-pool/smartnode/bindings/node" + "github.com/rocket-pool/smartnode/bindings/utils/eth" + + "github.com/rocket-pool/smartnode/bindings/tests/testutils/evm" + minipoolutils "github.com/rocket-pool/smartnode/bindings/tests/testutils/minipool" + nodeutils "github.com/rocket-pool/smartnode/bindings/tests/testutils/node" +) + +func TestQueueLengths(t *testing.T) { + + // State snapshotting + if err := evm.TakeSnapshot(); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := evm.RevertSnapshot(); err != nil { + t.Fatal(err) + } + }) + + // Register nodes + if _, err := node.RegisterNode(rp, "Australia/Brisbane", nodeAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + if err := nodeutils.RegisterTrustedNode(rp, ownerAccount, trustedNodeAccount); err != nil { + t.Fatal(err) + } + + // Disable min commission rate for unbonded pools + if _, err := trustednodesettings.BootstrapMinipoolUnbondedMinFee(rp, uint64(0), ownerAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Get & check queue lengths + if queueLengths, err := minipool.GetQueueLengths(rp, nil); err != nil { + t.Error(err) + } else { + if queueLengths.Total != 0 { + t.Errorf("Incorrect total queue length 1 %d", queueLengths.Total) + } + if queueLengths.FullDeposit != 0 { + t.Errorf("Incorrect full deposit queue length 1 %d", queueLengths.FullDeposit) + } + if queueLengths.HalfDeposit != 0 { + t.Errorf("Incorrect half deposit queue length 1 %d", queueLengths.HalfDeposit) + } + if queueLengths.EmptyDeposit != 0 { + t.Errorf("Incorrect empty deposit queue length 1 %d", queueLengths.EmptyDeposit) + } + } + + // Create full deposit minipool + if _, err := minipoolutils.CreateMinipool(t, rp, ownerAccount, nodeAccount, eth.EthToWei(32), 1); err != nil { + t.Fatal(err) + } + + // Get & check queue lengths + if queueLengths, err := minipool.GetQueueLengths(rp, nil); err != nil { + t.Error(err) + } else { + if queueLengths.Total != 1 { + t.Errorf("Incorrect total queue length 2 %d", queueLengths.Total) + } + if queueLengths.FullDeposit != 1 { + t.Errorf("Incorrect full deposit queue length 2 %d", queueLengths.FullDeposit) + } + if queueLengths.HalfDeposit != 0 { + t.Errorf("Incorrect half deposit queue length 2 %d", queueLengths.HalfDeposit) + } + if queueLengths.EmptyDeposit != 0 { + t.Errorf("Incorrect empty deposit queue length 2 %d", queueLengths.EmptyDeposit) + } + } + + // Create half deposit minipool + if _, err := minipoolutils.CreateMinipool(t, rp, ownerAccount, nodeAccount, eth.EthToWei(16), 2); err != nil { + t.Fatal(err) + } + + // Get & check queue lengths + if queueLengths, err := minipool.GetQueueLengths(rp, nil); err != nil { + t.Error(err) + } else { + if queueLengths.Total != 2 { + t.Errorf("Incorrect total queue length 3 %d", queueLengths.Total) + } + if queueLengths.FullDeposit != 1 { + t.Errorf("Incorrect full deposit queue length 3 %d", queueLengths.FullDeposit) + } + if queueLengths.HalfDeposit != 1 { + t.Errorf("Incorrect half deposit queue length 3 %d", queueLengths.HalfDeposit) + } + if queueLengths.EmptyDeposit != 0 { + t.Errorf("Incorrect empty deposit queue length 3 %d", queueLengths.EmptyDeposit) + } + } + + // Create empty deposit minipool + //if _, err := minipoolutils.CreateMinipool(t, rp, ownerAccount, trustedNodeAccount, eth.EthToWei(0), 3); err != nil { t.Fatal(err) } + + // Get & check queue lengths + if queueLengths, err := minipool.GetQueueLengths(rp, nil); err != nil { + t.Error(err) + } else { + if queueLengths.Total != 2 { + t.Errorf("Incorrect total queue length 4 %d", queueLengths.Total) + } + if queueLengths.FullDeposit != 1 { + t.Errorf("Incorrect full deposit queue length 4 %d", queueLengths.FullDeposit) + } + if queueLengths.HalfDeposit != 1 { + t.Errorf("Incorrect half deposit queue length 4 %d", queueLengths.HalfDeposit) + } + //if queueLengths.EmptyDeposit != 1 { + // t.Errorf("Incorrect empty deposit queue length 4 %d", queueLengths.EmptyDeposit) + //} + } + +} + +func TestQueueCapacity(t *testing.T) { + + // State snapshotting + if err := evm.TakeSnapshot(); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := evm.RevertSnapshot(); err != nil { + t.Fatal(err) + } + }) + + // Register nodes + if _, err := node.RegisterNode(rp, "Australia/Brisbane", nodeAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + if err := nodeutils.RegisterTrustedNode(rp, ownerAccount, trustedNodeAccount); err != nil { + t.Fatal(err) + } + + // Disable min commission rate for unbonded pools + if _, err := trustednodesettings.BootstrapMinipoolUnbondedMinFee(rp, uint64(0), ownerAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Get & check queue capacity + if queueCapacity, err := minipool.GetQueueCapacity(rp, nil); err != nil { + t.Error(err) + } else { + if queueCapacity.Total.Cmp(eth.EthToWei(0)) != 0 { + t.Errorf("Incorrect queue total capacity 1 %s", queueCapacity.Total.String()) + } + if queueCapacity.Effective.Cmp(eth.EthToWei(0)) != 0 { + t.Errorf("Incorrect queue effective capacity 1 %s", queueCapacity.Effective.String()) + } + if queueCapacity.NextMinipool.Cmp(eth.EthToWei(0)) != 0 { + t.Errorf("Incorrect queue next minipool capacity 1 %s", queueCapacity.NextMinipool.String()) + } + } + + /* TODO: Unbonded minipools are temporarily disabled + // Create empty deposit minipool + if _, err := minipoolutils.CreateMinipool(t, rp, ownerAccount, trustedNodeAccount, eth.EthToWei(0)); err != nil { t.Fatal(err) } + + // Get & check queue capacity + if queueCapacity, err := minipool.GetQueueCapacity(rp, nil); err != nil { + t.Error(err) + } else { + if queueCapacity.Total.Cmp(eth.EthToWei(32)) != 0 { + t.Errorf("Incorrect queue total capacity 2 %s", queueCapacity.Total.String()) + } + if queueCapacity.Effective.Cmp(eth.EthToWei(0)) != 0 { + t.Errorf("Incorrect queue effective capacity 2 %s", queueCapacity.Effective.String()) + } + if queueCapacity.NextMinipool.Cmp(eth.EthToWei(32)) != 0 { + t.Errorf("Incorrect queue next minipool capacity 2 %s", queueCapacity.NextMinipool.String()) + } + } + */ + + // Create half deposit minipool + if _, err := minipoolutils.CreateMinipool(t, rp, ownerAccount, nodeAccount, eth.EthToWei(16), 1); err != nil { + t.Fatal(err) + } + + // Get & check queue capacity + if queueCapacity, err := minipool.GetQueueCapacity(rp, nil); err != nil { + t.Error(err) + } else { + if queueCapacity.Total.Cmp(eth.EthToWei(16)) != 0 { + t.Errorf("Incorrect queue total capacity 3 %s", queueCapacity.Total.String()) + } + if queueCapacity.Effective.Cmp(eth.EthToWei(16)) != 0 { + t.Errorf("Incorrect queue effective capacity 3 %s", queueCapacity.Effective.String()) + } + if queueCapacity.NextMinipool.Cmp(eth.EthToWei(16)) != 0 { + t.Errorf("Incorrect queue next minipool capacity 3 %s", queueCapacity.NextMinipool.String()) + } + } + + // Create full deposit minipool + if _, err := minipoolutils.CreateMinipool(t, rp, ownerAccount, nodeAccount, eth.EthToWei(32), 2); err != nil { + t.Fatal(err) + } + + // Get & check queue capacity + if queueCapacity, err := minipool.GetQueueCapacity(rp, nil); err != nil { + t.Error(err) + } else { + if queueCapacity.Total.Cmp(eth.EthToWei(32)) != 0 { + t.Errorf("Incorrect queue total capacity 4 %s", queueCapacity.Total.String()) + } + if queueCapacity.Effective.Cmp(eth.EthToWei(32)) != 0 { + t.Errorf("Incorrect queue effective capacity 4 %s", queueCapacity.Effective.String()) + } + if queueCapacity.NextMinipool.Cmp(eth.EthToWei(16)) != 0 { + t.Errorf("Incorrect queue next minipool capacity 4 %s", queueCapacity.NextMinipool.String()) + } + } + +} diff --git a/bindings/tests/minipool/status_test.go b/bindings/tests/minipool/status_test.go new file mode 100644 index 000000000..41bc685d6 --- /dev/null +++ b/bindings/tests/minipool/status_test.go @@ -0,0 +1,78 @@ +package minipool + +import ( + "fmt" + "testing" + + "github.com/rocket-pool/smartnode/bindings/settings/trustednode" + "github.com/rocket-pool/smartnode/bindings/types" + + "github.com/rocket-pool/smartnode/bindings/minipool" + "github.com/rocket-pool/smartnode/bindings/node" + "github.com/rocket-pool/smartnode/bindings/utils/eth" + + "github.com/rocket-pool/smartnode/bindings/tests/testutils/evm" + minipoolutils "github.com/rocket-pool/smartnode/bindings/tests/testutils/minipool" + nodeutils "github.com/rocket-pool/smartnode/bindings/tests/testutils/node" +) + +func TestSubmitMinipoolWithdrawable(t *testing.T) { + + // State snapshotting + if err := evm.TakeSnapshot(); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := evm.RevertSnapshot(); err != nil { + t.Fatal(err) + } + }) + + // Register nodes + if _, err := node.RegisterNode(rp, "Australia/Brisbane", nodeAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + if err := nodeutils.RegisterTrustedNode(rp, ownerAccount, trustedNodeAccount); err != nil { + t.Fatal(err) + } + + // Create & stake minipool + mp, err := minipoolutils.CreateMinipool(t, rp, ownerAccount, nodeAccount, eth.EthToWei(32), 1) + if err != nil { + t.Fatal(err) + } + + // Delay for the time between depositing and staking + scrubPeriod, err := trustednode.GetScrubPeriod(rp, nil) + if err != nil { + t.Fatal(err) + } + err = evm.IncreaseTime(int(scrubPeriod + 1)) + if err != nil { + t.Fatal(fmt.Errorf("error increasing time: %w", err)) + } + + if err := minipoolutils.StakeMinipool(rp, mp, nodeAccount); err != nil { + t.Fatal(err) + } + + // Get & check initial minipool withdrawable status + if status, err := mp.GetStatus(nil); err != nil { + t.Error(err) + } else if status == types.Withdrawable { + t.Error("Incorrect initial minipool withdrawable status") + } + + // Submit minipool withdrawable status + if _, err := minipool.SubmitMinipoolWithdrawable(rp, mp.Address, trustedNodeAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Get & check updated minipool withdrawable status + if status, err := mp.GetStatus(nil); err != nil { + t.Error(err) + } else if status != types.Withdrawable { + t.Error("Incorrect updated minipool withdrawable status") + } + +} diff --git a/bindings/tests/network/balances_test.go b/bindings/tests/network/balances_test.go new file mode 100644 index 000000000..83fd7a1f4 --- /dev/null +++ b/bindings/tests/network/balances_test.go @@ -0,0 +1,75 @@ +package network + +import ( + "testing" + + "github.com/rocket-pool/smartnode/bindings/network" + "github.com/rocket-pool/smartnode/bindings/utils/eth" + + "github.com/rocket-pool/smartnode/bindings/tests/testutils/evm" + nodeutils "github.com/rocket-pool/smartnode/bindings/tests/testutils/node" +) + +func TestSubmitBalances(t *testing.T) { + + // State snapshotting + if err := evm.TakeSnapshot(); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := evm.RevertSnapshot(); err != nil { + t.Fatal(err) + } + }) + + // Register trusted node + if err := nodeutils.RegisterTrustedNode(rp, ownerAccount, trustedNodeAccount); err != nil { + t.Fatal(err) + } + + // Submit balances + var balancesBlock uint64 = 100 + var slotTimestamp uint64 = 16000000 + totalEth := eth.EthToWei(100) + stakingEth := eth.EthToWei(80) + rethSupply := eth.EthToWei(70) + if _, err := network.SubmitBalances(rp, balancesBlock, slotTimestamp, totalEth, stakingEth, rethSupply, trustedNodeAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Get & check network balances block + if networkBalancesBlock, err := network.GetBalancesBlock(rp, nil); err != nil { + t.Error(err) + } else if networkBalancesBlock != balancesBlock { + t.Errorf("Incorrect network balances block %d", networkBalancesBlock) + } + + // Get & check network total ETH + if networkTotalEth, err := network.GetTotalETHBalance(rp, nil); err != nil { + t.Error(err) + } else if networkTotalEth.Cmp(totalEth) != 0 { + t.Errorf("Incorrect network total ETH balance %s", networkTotalEth.String()) + } + + // Get & check network staking ETH + if networkStakingEth, err := network.GetStakingETHBalance(rp, nil); err != nil { + t.Error(err) + } else if networkStakingEth.Cmp(stakingEth) != 0 { + t.Errorf("Incorrect network staking ETH balance %s", networkStakingEth.String()) + } + + // Get & check network rETH supply + if networkRethSupply, err := network.GetTotalRETHSupply(rp, nil); err != nil { + t.Error(err) + } else if networkRethSupply.Cmp(rethSupply) != 0 { + t.Errorf("Incorrect network total rETH supply %s", networkRethSupply.String()) + } + + // Get & check ETH utilization rate + if ethUtilizationRate, err := network.GetETHUtilizationRate(rp, nil); err != nil { + t.Error(err) + } else if ethUtilizationRate != eth.WeiToEth(stakingEth)/eth.WeiToEth(totalEth) { + t.Errorf("Incorrect network ETH utilization rate %f", ethUtilizationRate) + } + +} diff --git a/bindings/tests/network/fees_test.go b/bindings/tests/network/fees_test.go new file mode 100644 index 000000000..d8af92d4a --- /dev/null +++ b/bindings/tests/network/fees_test.go @@ -0,0 +1,98 @@ +package network + +import ( + "math/big" + "testing" + + "github.com/rocket-pool/smartnode/bindings/deposit" + "github.com/rocket-pool/smartnode/bindings/network" + "github.com/rocket-pool/smartnode/bindings/settings/protocol" + + "github.com/rocket-pool/smartnode/bindings/tests/testutils/evm" +) + +func TestNodeFee(t *testing.T) { + + // State snapshotting + if err := evm.TakeSnapshot(); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := evm.RevertSnapshot(); err != nil { + t.Fatal(err) + } + }) + + // Get settings + targetNodeFee, err := protocol.GetTargetNodeFee(rp, nil) + if err != nil { + t.Fatal(err) + } + minNodeFee, err := protocol.GetMinimumNodeFee(rp, nil) + if err != nil { + t.Fatal(err) + } + maxNodeFee, err := protocol.GetMaximumNodeFee(rp, nil) + if err != nil { + t.Fatal(err) + } + demandRange, err := protocol.GetNodeFeeDemandRange(rp, nil) + if err != nil { + t.Fatal(err) + } + + // Get & check initial node demand + if nodeDemand, err := network.GetNodeDemand(rp, nil); err != nil { + t.Error(err) + } else if nodeDemand.Cmp(big.NewInt(0)) != 0 { + t.Errorf("Incorrect initial node demand value %s", nodeDemand.String()) + } + + // Get & check initial node fee + if nodeFee, err := network.GetNodeFee(rp, nil); err != nil { + t.Error(err) + } else if nodeFee != targetNodeFee { + t.Errorf("Incorrect initial node fee %f", nodeFee) + } + + // Make user deposit + opts := userAccount.GetTransactor() + opts.Value = demandRange + if _, err := deposit.Deposit(rp, opts); err != nil { + t.Fatal(err) + } + + // Get & check updated node demand + if nodeDemand, err := network.GetNodeDemand(rp, nil); err != nil { + t.Error(err) + } else if nodeDemand.Cmp(opts.Value) != 0 { + t.Errorf("Incorrect updated node demand value %s", nodeDemand.String()) + } + + // Get & check updated node fee + if nodeFee, err := network.GetNodeFee(rp, nil); err != nil { + t.Error(err) + } else if nodeFee != maxNodeFee { + t.Errorf("Incorrect updated node fee %f", nodeFee) + } + + // Get & check node fees by demand values + negDemandRange := new(big.Int) + negDemandRange.Neg(demandRange) + if nodeFee, err := network.GetNodeFeeByDemand(rp, big.NewInt(0), nil); err != nil { + t.Error(err) + } else if nodeFee != targetNodeFee { + t.Errorf("Incorrect node fee for zero demand %f", nodeFee) + } + if nodeFee, err := network.GetNodeFeeByDemand(rp, negDemandRange, nil); err != nil { + t.Error(err) + } else if nodeFee != minNodeFee { + t.Errorf("Incorrect node fee for negative demand %f", nodeFee) + } + if nodeFee, err := network.GetNodeFeeByDemand(rp, demandRange, nil); err != nil { + t.Error(err) + } else if nodeFee != maxNodeFee { + t.Errorf("Incorrect node fee for positive demand %f", nodeFee) + } + +} diff --git a/bindings/tests/network/main_test.go b/bindings/tests/network/main_test.go new file mode 100644 index 000000000..a32da6428 --- /dev/null +++ b/bindings/tests/network/main_test.go @@ -0,0 +1,63 @@ +package network + +import ( + "log" + "os" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethclient" + + "github.com/rocket-pool/smartnode/bindings/rocketpool" + + "github.com/rocket-pool/smartnode/bindings/tests" + "github.com/rocket-pool/smartnode/bindings/tests/testutils/accounts" +) + +var ( + client *ethclient.Client + rp *rocketpool.RocketPool + + ownerAccount *accounts.Account + trustedNodeAccount *accounts.Account + nodeAccount *accounts.Account + userAccount *accounts.Account +) + +func TestMain(m *testing.M) { + var err error + + // Initialize eth client + client, err = ethclient.Dial(tests.Eth1ProviderAddress) + if err != nil { + log.Fatal(err) + } + + // Initialize contract manager + rp, err = rocketpool.NewRocketPool(client, common.HexToAddress(tests.RocketStorageAddress)) + if err != nil { + log.Fatal(err) + } + + // Initialize accounts + ownerAccount, err = accounts.GetAccount(0) + if err != nil { + log.Fatal(err) + } + trustedNodeAccount, err = accounts.GetAccount(1) + if err != nil { + log.Fatal(err) + } + nodeAccount, err = accounts.GetAccount(2) + if err != nil { + log.Fatal(err) + } + userAccount, err = accounts.GetAccount(9) + if err != nil { + log.Fatal(err) + } + + // Run tests + os.Exit(m.Run()) + +} diff --git a/bindings/tests/network/prices_test.go b/bindings/tests/network/prices_test.go new file mode 100644 index 000000000..05162e040 --- /dev/null +++ b/bindings/tests/network/prices_test.go @@ -0,0 +1,53 @@ +package network + +import ( + "testing" + + "github.com/rocket-pool/smartnode/bindings/network" + "github.com/rocket-pool/smartnode/bindings/utils/eth" + + "github.com/rocket-pool/smartnode/bindings/tests/testutils/evm" + nodeutils "github.com/rocket-pool/smartnode/bindings/tests/testutils/node" +) + +func TestSubmitPrices(t *testing.T) { + + // State snapshotting + if err := evm.TakeSnapshot(); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := evm.RevertSnapshot(); err != nil { + t.Fatal(err) + } + }) + + // Register trusted node + if err := nodeutils.RegisterTrustedNode(rp, ownerAccount, trustedNodeAccount); err != nil { + t.Fatal(err) + } + + // Submit prices + var pricesBlock uint64 = 100 + var slotTimestamp uint64 = 16000000 + rplPrice := eth.EthToWei(1000) + effectiveRplStake := eth.EthToWei(24000) + if _, err := network.SubmitPrices(rp, pricesBlock, slotTimestamp, rplPrice, effectiveRplStake, trustedNodeAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Get & check network prices block + if networkPricesBlock, err := network.GetPricesBlock(rp, nil); err != nil { + t.Error(err) + } else if networkPricesBlock != pricesBlock { + t.Errorf("Incorrect network prices block %d", networkPricesBlock) + } + + // Get & check network RPL price + if networkRplPrice, err := network.GetRPLPrice(rp, nil); err != nil { + t.Error(err) + } else if networkRplPrice.Cmp(rplPrice) != 0 { + t.Errorf("Incorrect network RPL price %s", networkRplPrice.String()) + } + +} diff --git a/bindings/tests/node/deposit_test.go b/bindings/tests/node/deposit_test.go new file mode 100644 index 000000000..d39d14be4 --- /dev/null +++ b/bindings/tests/node/deposit_test.go @@ -0,0 +1,60 @@ +package node + +import ( + "testing" + + "github.com/rocket-pool/smartnode/bindings/minipool" + "github.com/rocket-pool/smartnode/bindings/node" + "github.com/rocket-pool/smartnode/bindings/utils/eth" + + "github.com/rocket-pool/smartnode/bindings/tests/testutils/evm" + minipoolutils "github.com/rocket-pool/smartnode/bindings/tests/testutils/minipool" + nodeutils "github.com/rocket-pool/smartnode/bindings/tests/testutils/node" +) + +func TestDeposit(t *testing.T) { + + // State snapshotting + if err := evm.TakeSnapshot(); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := evm.RevertSnapshot(); err != nil { + t.Fatal(err) + } + }) + + // Register node + if _, err := node.RegisterNode(rp, "Australia/Brisbane", nodeAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Get initial node minipool count + minipoolCount1, err := minipool.GetNodeMinipoolCount(rp, nodeAccount.Address, nil) + if err != nil { + t.Fatal(err) + } + + // Mint & stake RPL required for mininpool + rplRequired, err := minipoolutils.GetMinipoolRPLRequired(rp) + if err != nil { + t.Fatal(err) + } + if err := nodeutils.StakeRPL(rp, ownerAccount, nodeAccount, rplRequired); err != nil { + t.Fatal(err) + } + + // Deposit + if _, _, err := nodeutils.Deposit(t, rp, nodeAccount, eth.EthToWei(16), 1); err != nil { + t.Fatal(err) + } + + // Get & check updated node minipool count + minipoolCount2, err := minipool.GetNodeMinipoolCount(rp, nodeAccount.Address, nil) + if err != nil { + t.Fatal(err) + } else if minipoolCount2 != minipoolCount1+1 { + t.Error("Incorrect node minipool count") + } + +} diff --git a/bindings/tests/node/distributor_test.go b/bindings/tests/node/distributor_test.go new file mode 100644 index 000000000..0816441bc --- /dev/null +++ b/bindings/tests/node/distributor_test.go @@ -0,0 +1,31 @@ +package node + +import ( + "testing" + + "github.com/rocket-pool/smartnode/bindings/node" + + "github.com/rocket-pool/smartnode/bindings/tests/testutils/evm" +) + +func TestNodeDistributor(t *testing.T) { + + // State snapshotting + if err := evm.TakeSnapshot(); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := evm.RevertSnapshot(); err != nil { + t.Fatal(err) + } + }) + + distributorAddress, err := node.GetDistributorAddress(rp, nodeAccount.Address, nil) + if err != nil { + t.Fatal(err) + } + + if distributorAddress.Hex() == "0x0000000000000000000000000000000000000000" { + t.Errorf("Invalid distributor address") + } +} diff --git a/bindings/tests/node/main_test.go b/bindings/tests/node/main_test.go new file mode 100644 index 000000000..e44d41caa --- /dev/null +++ b/bindings/tests/node/main_test.go @@ -0,0 +1,57 @@ +package node + +import ( + "log" + "os" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethclient" + + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/tests" + "github.com/rocket-pool/smartnode/bindings/tests/testutils/accounts" +) + +var ( + client *ethclient.Client + rp *rocketpool.RocketPool + + ownerAccount *accounts.Account + nodeAccount *accounts.Account + withdrawalAccount *accounts.Account +) + +func TestMain(m *testing.M) { + var err error + + // Initialize eth client + client, err = ethclient.Dial(tests.Eth1ProviderAddress) + if err != nil { + log.Fatal(err) + } + + // Initialize contract manager + rp, err = rocketpool.NewRocketPool(client, common.HexToAddress(tests.RocketStorageAddress)) + if err != nil { + log.Fatal(err) + } + + // Initialize accounts + ownerAccount, err = accounts.GetAccount(0) + if err != nil { + log.Fatal(err) + } + nodeAccount, err = accounts.GetAccount(1) + if err != nil { + log.Fatal(err) + } + withdrawalAccount, err = accounts.GetAccount(2) + if err != nil { + log.Fatal(err) + } + + // Run tests + os.Exit(m.Run()) + +} diff --git a/bindings/tests/node/node_test.go b/bindings/tests/node/node_test.go new file mode 100644 index 000000000..20e4e6318 --- /dev/null +++ b/bindings/tests/node/node_test.go @@ -0,0 +1,169 @@ +package node + +import ( + "bytes" + "testing" + + "github.com/ethereum/go-ethereum/common" + + "github.com/rocket-pool/smartnode/bindings/node" + "github.com/rocket-pool/smartnode/bindings/storage" + + "github.com/rocket-pool/smartnode/bindings/tests/testutils/evm" +) + +func TestRegisterNode(t *testing.T) { + + // State snapshotting + if err := evm.TakeSnapshot(); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := evm.RevertSnapshot(); err != nil { + t.Fatal(err) + } + }) + + // Get & check initial node exists status + if exists, err := node.GetNodeExists(rp, nodeAccount.Address, nil); err != nil { + t.Error(err) + } else if exists { + t.Error("Node already existed before registration") + } + + // Get & check initial node details + if details, err := node.GetNodes(rp, nil); err != nil { + t.Error(err) + } else if len(details) != 0 { + t.Error("Incorrect initial node count") + } + + // Register node + timezoneLocation := "Australia/Brisbane" + if _, err := node.RegisterNode(rp, timezoneLocation, nodeAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Get & check updated node details + if details, err := node.GetNodes(rp, nil); err != nil { + t.Error(err) + } else if len(details) != 1 { + t.Error("Incorrect updated node count") + } else { + nodeDetails := details[0] + if !bytes.Equal(nodeDetails.Address.Bytes(), nodeAccount.Address.Bytes()) { + t.Errorf("Incorrect node address %s", nodeDetails.Address.Hex()) + } + if !nodeDetails.Exists { + t.Error("Incorrect node exists status") + } + if !bytes.Equal(nodeDetails.PrimaryWithdrawalAddress.Bytes(), nodeAccount.Address.Bytes()) { + t.Errorf("Incorrect node withdrawal address '%s'", nodeDetails.PrimaryWithdrawalAddress.Hex()) + } + if nodeDetails.TimezoneLocation != timezoneLocation { + t.Errorf("Incorrect node timezone location '%s'", nodeDetails.TimezoneLocation) + } + } + +} + +func TestSetWithdrawalAddress(t *testing.T) { + + // State snapshotting + if err := evm.TakeSnapshot(); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := evm.RevertSnapshot(); err != nil { + t.Fatal(err) + } + }) + + // Register node + if _, err := node.RegisterNode(rp, "Australia/Brisbane", nodeAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Set withdrawal address + withdrawalAddress := common.HexToAddress("0x1111111111111111111111111111111111111111") + if _, err := storage.SetWithdrawalAddress(rp, nodeAccount.Address, withdrawalAddress, true, nodeAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Get & check node withdrawal address + if nodeWithdrawalAddress, err := storage.GetNodeWithdrawalAddress(rp, nodeAccount.Address, nil); err != nil { + t.Error(err) + } else if !bytes.Equal(nodeWithdrawalAddress.Bytes(), withdrawalAddress.Bytes()) { + t.Errorf("Incorrect node withdrawal address '%s'", nodeWithdrawalAddress.Hex()) + } + +} + +func TestSetWithdrawalAddressConfirmation(t *testing.T) { + + // State snapshotting + if err := evm.TakeSnapshot(); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := evm.RevertSnapshot(); err != nil { + t.Fatal(err) + } + }) + + // Register node + if _, err := node.RegisterNode(rp, "Australia/Brisbane", nodeAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Set withdrawal address + withdrawalAddress := withdrawalAccount.Address + if _, err := storage.SetWithdrawalAddress(rp, nodeAccount.Address, withdrawalAddress, false, nodeAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Confirm withdrawal address + if _, err := storage.ConfirmWithdrawalAddress(rp, nodeAccount.Address, withdrawalAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Get & check node withdrawal address + if nodeWithdrawalAddress, err := storage.GetNodeWithdrawalAddress(rp, nodeAccount.Address, nil); err != nil { + t.Error(err) + } else if !bytes.Equal(nodeWithdrawalAddress.Bytes(), withdrawalAddress.Bytes()) { + t.Errorf("Incorrect node withdrawal address '%s'", nodeWithdrawalAddress.Hex()) + } + +} + +func TestSetTimezoneLocation(t *testing.T) { + + // State snapshotting + if err := evm.TakeSnapshot(); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := evm.RevertSnapshot(); err != nil { + t.Fatal(err) + } + }) + + // Register node + if _, err := node.RegisterNode(rp, "Australia/Brisbane", nodeAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Set timezone + timezoneLocation := "Australia/Sydney" + if _, err := node.SetTimezoneLocation(rp, timezoneLocation, nodeAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Get & check node timezone location + if nodeTimezoneLocation, err := node.GetNodeTimezoneLocation(rp, nodeAccount.Address, nil); err != nil { + t.Error(err) + } else if nodeTimezoneLocation != timezoneLocation { + t.Errorf("Incorrect node timezone location '%s'", nodeTimezoneLocation) + } + +} diff --git a/bindings/tests/node/staking_test.go b/bindings/tests/node/staking_test.go new file mode 100644 index 000000000..3db07ccd6 --- /dev/null +++ b/bindings/tests/node/staking_test.go @@ -0,0 +1,267 @@ +package node + +import ( + "fmt" + "math/big" + "testing" + + "github.com/rocket-pool/smartnode/bindings/deposit" + "github.com/rocket-pool/smartnode/bindings/minipool" + "github.com/rocket-pool/smartnode/bindings/node" + "github.com/rocket-pool/smartnode/bindings/settings/protocol" + "github.com/rocket-pool/smartnode/bindings/settings/trustednode" + "github.com/rocket-pool/smartnode/bindings/tokens" + "github.com/rocket-pool/smartnode/bindings/utils/eth" + + "github.com/rocket-pool/smartnode/bindings/tests/testutils/evm" + minipoolutils "github.com/rocket-pool/smartnode/bindings/tests/testutils/minipool" + nodeutils "github.com/rocket-pool/smartnode/bindings/tests/testutils/node" + rplutils "github.com/rocket-pool/smartnode/bindings/tests/testutils/tokens/rpl" +) + +func TestStakeRPL(t *testing.T) { + + // State snapshotting + if err := evm.TakeSnapshot(); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := evm.RevertSnapshot(); err != nil { + t.Fatal(err) + } + }) + + // Register node + if _, err := node.RegisterNode(rp, "Australia/Brisbane", nodeAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Get RPL amount required for 2 minipools + minipoolRplRequired, err := minipoolutils.GetMinipoolRPLRequired(rp) + if err != nil { + t.Fatal(err) + } + rplAmount := new(big.Int) + rplAmount.Mul(minipoolRplRequired, big.NewInt(2)) + + // Mint RPL + if err := rplutils.MintRPL(rp, ownerAccount, nodeAccount, rplAmount); err != nil { + t.Fatal(err) + } + + // Approve RPL transfer for staking + rocketNodeStakingAddress, err := rp.GetAddress("rocketNodeStaking") + if err != nil { + t.Fatal(err) + } + if _, err := tokens.ApproveRPL(rp, *rocketNodeStakingAddress, rplAmount, nodeAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Check initial staking details + if totalRplStake, err := node.GetTotalRPLStake(rp, nil); err != nil { + t.Error(err) + } else if totalRplStake.Cmp(big.NewInt(0)) != 0 { + t.Errorf("Incorrect initial total RPL stake %s", totalRplStake.String()) + } + if totalEffectiveRplStake, err := node.GetTotalEffectiveRPLStake(rp, nil); err != nil { + t.Error(err) + } else if totalEffectiveRplStake.Cmp(big.NewInt(0)) != 0 { + t.Errorf("Incorrect initial total effective RPL stake %s", totalEffectiveRplStake.String()) + } + if nodeRplStake, err := node.GetNodeRPLStake(rp, nodeAccount.Address, nil); err != nil { + t.Error(err) + } else if nodeRplStake.Cmp(big.NewInt(0)) != 0 { + t.Errorf("Incorrect initial node RPL stake %s", nodeRplStake.String()) + } + if nodeEffectiveRplStake, err := node.GetNodeEffectiveRPLStake(rp, nodeAccount.Address, nil); err != nil { + t.Error(err) + } else if nodeEffectiveRplStake.Cmp(big.NewInt(0)) != 0 { + t.Errorf("Incorrect initial node effective RPL stake %s", nodeEffectiveRplStake.String()) + } + if nodeMinimumRplStake, err := node.GetNodeMinimumRPLStake(rp, nodeAccount.Address, nil); err != nil { + t.Error(err) + } else if nodeMinimumRplStake.Cmp(big.NewInt(0)) != 0 { + t.Errorf("Incorrect initial node minimum RPL stake %s", nodeMinimumRplStake.String()) + } + if nodeRplStakedTime, err := node.GetNodeRPLStakedTime(rp, nodeAccount.Address, nil); err != nil { + t.Error(err) + } else if nodeRplStakedTime != 0 { + t.Errorf("Incorrect initial node RPL staked time %d", nodeRplStakedTime) + } + if nodeMinipoolLimit, err := node.GetNodeMinipoolLimit(rp, nodeAccount.Address, nil); err != nil { + t.Error(err) + } else if nodeMinipoolLimit != 0 { + t.Errorf("Incorrect initial node minipool limit %d", nodeMinipoolLimit) + } + + // Stake RPL + if _, err := node.StakeRPL(rp, rplAmount, nodeAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Check updated staking details + if totalRplStake, err := node.GetTotalRPLStake(rp, nil); err != nil { + t.Error(err) + } else if totalRplStake.Cmp(rplAmount) != 0 { + t.Errorf("Incorrect updated total RPL stake 1 %s", totalRplStake.String()) + } + if totalEffectiveRplStake, err := node.GetTotalEffectiveRPLStake(rp, nil); err != nil { + t.Error(err) + } else if totalEffectiveRplStake.Cmp(big.NewInt(0)) != 0 { + t.Errorf("Incorrect updated total effective RPL stake 1 %s", totalEffectiveRplStake.String()) + } + if nodeRplStake, err := node.GetNodeRPLStake(rp, nodeAccount.Address, nil); err != nil { + t.Error(err) + } else if nodeRplStake.Cmp(rplAmount) != 0 { + t.Errorf("Incorrect updated node RPL stake 1 %s", nodeRplStake.String()) + } + if nodeEffectiveRplStake, err := node.GetNodeEffectiveRPLStake(rp, nodeAccount.Address, nil); err != nil { + t.Error(err) + } else if nodeEffectiveRplStake.Cmp(big.NewInt(0)) != 0 { + t.Errorf("Incorrect updated node effective RPL stake 1 %s", nodeEffectiveRplStake.String()) + } + if nodeMinimumRplStake, err := node.GetNodeMinimumRPLStake(rp, nodeAccount.Address, nil); err != nil { + t.Error(err) + } else if nodeMinimumRplStake.Cmp(big.NewInt(0)) != 0 { + t.Errorf("Incorrect updated node minimum RPL stake 1 %s", nodeMinimumRplStake.String()) + } + if nodeRplStakedTime, err := node.GetNodeRPLStakedTime(rp, nodeAccount.Address, nil); err != nil { + t.Error(err) + } else if nodeRplStakedTime == 0 { + t.Errorf("Incorrect updated node RPL staked time 1 %d", nodeRplStakedTime) + } + if nodeMinipoolLimit, err := node.GetNodeMinipoolLimit(rp, nodeAccount.Address, nil); err != nil { + t.Error(err) + } else if nodeMinipoolLimit != 2 { + t.Errorf("Incorrect updated node minipool limit 1 %d", nodeMinipoolLimit) + } + + // Make node deposit to create minipool + minipoolAddress, _, err := nodeutils.Deposit(t, rp, nodeAccount, eth.EthToWei(16), 1) + if err != nil { + t.Fatal(err) + } + mp, err := minipool.NewMinipool(rp, minipoolAddress) + if err != nil { + t.Fatal(err) + } + + // Make user deposit + depositOpts := nodeAccount.GetTransactor() + depositOpts.Value = eth.EthToWei(16) + if _, err := deposit.Deposit(rp, depositOpts); err != nil { + t.Fatal(err) + } + + // Delay for the time between depositing and staking + scrubPeriod, err := trustednode.GetScrubPeriod(rp, nil) + if err != nil { + t.Fatal(err) + } + err = evm.IncreaseTime(int(scrubPeriod + 1)) + if err != nil { + t.Fatal(fmt.Errorf("error increasing time: %w", err)) + } + + // Stake minipool + if err := minipoolutils.StakeMinipool(rp, mp, nodeAccount); err != nil { + t.Fatal(err) + } + + // Check updated staking details + if totalEffectiveRplStake, err := node.GetTotalEffectiveRPLStake(rp, nil); err != nil { + t.Error(err) + } else if totalEffectiveRplStake.Cmp(rplAmount) != 0 { + t.Errorf("Incorrect updated total effective RPL stake 2 %s", totalEffectiveRplStake.String()) + } + if nodeEffectiveRplStake, err := node.GetNodeEffectiveRPLStake(rp, nodeAccount.Address, nil); err != nil { + t.Error(err) + } else if nodeEffectiveRplStake.Cmp(rplAmount) != 0 { + t.Errorf("Incorrect updated node effective RPL stake 2 %s", nodeEffectiveRplStake.String()) + } + if nodeMinimumRplStake, err := node.GetNodeMinimumRPLStake(rp, nodeAccount.Address, nil); err != nil { + t.Error(err) + } else if nodeMinimumRplStake.Cmp(minipoolRplRequired) != 0 { + t.Errorf("Incorrect updated node minimum RPL stake 2 %s", nodeMinimumRplStake.String()) + } + +} + +func TestWithdrawRPL(t *testing.T) { + + // State snapshotting + if err := evm.TakeSnapshot(); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := evm.RevertSnapshot(); err != nil { + t.Fatal(err) + } + }) + + // State snapshotting + if err := evm.TakeSnapshot(); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := evm.RevertSnapshot(); err != nil { + t.Fatal(err) + } + }) + + // Register node + if _, err := node.RegisterNode(rp, "Australia/Brisbane", nodeAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Mint & stake RPL + rplAmount := eth.EthToWei(1000) + if err := nodeutils.StakeRPL(rp, ownerAccount, nodeAccount, rplAmount); err != nil { + t.Fatal(err) + } + + // Get & set rewards claim interval + rewardsClaimIntervalTime, err := protocol.GetRewardsClaimIntervalTime(rp, nil) + if err != nil { + t.Fatal(err) + } + if _, err := protocol.BootstrapRewardsClaimIntervalTime(rp, 0, ownerAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Check initial staking details + if totalRplStake, err := node.GetTotalRPLStake(rp, nil); err != nil { + t.Error(err) + } else if totalRplStake.Cmp(rplAmount) != 0 { + t.Errorf("Incorrect initial total RPL stake %s", totalRplStake.String()) + } + if nodeRplStake, err := node.GetNodeRPLStake(rp, nodeAccount.Address, nil); err != nil { + t.Error(err) + } else if nodeRplStake.Cmp(rplAmount) != 0 { + t.Errorf("Incorrect initial node RPL stake %s", nodeRplStake.String()) + } + + // Withdraw RPL + if _, err := node.WithdrawRPL(rp, rplAmount, nodeAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Check updated staking details + if totalRplStake, err := node.GetTotalRPLStake(rp, nil); err != nil { + t.Error(err) + } else if totalRplStake.Cmp(big.NewInt(0)) != 0 { + t.Errorf("Incorrect updated total RPL stake %s", totalRplStake.String()) + } + if nodeRplStake, err := node.GetNodeRPLStake(rp, nodeAccount.Address, nil); err != nil { + t.Error(err) + } else if nodeRplStake.Cmp(big.NewInt(0)) != 0 { + t.Errorf("Incorrect updated node RPL stake %s", nodeRplStake.String()) + } + + // Reset rewards claim interval + if _, err := protocol.BootstrapRewardsClaimIntervalTime(rp, rewardsClaimIntervalTime, ownerAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + +} diff --git a/bindings/tests/rewards/main_test.go b/bindings/tests/rewards/main_test.go new file mode 100644 index 000000000..999112a03 --- /dev/null +++ b/bindings/tests/rewards/main_test.go @@ -0,0 +1,58 @@ +package rewards + +import ( + "log" + "os" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethclient" + + "github.com/rocket-pool/smartnode/bindings/rocketpool" + + "github.com/rocket-pool/smartnode/bindings/tests" + "github.com/rocket-pool/smartnode/bindings/tests/testutils/accounts" +) + +var ( + client *ethclient.Client + rp *rocketpool.RocketPool + + ownerAccount *accounts.Account + trustedNodeAccount *accounts.Account + nodeAccount *accounts.Account +) + +func TestMain(m *testing.M) { + var err error + + // Initialize eth client + client, err = ethclient.Dial(tests.Eth1ProviderAddress) + if err != nil { + log.Fatal(err) + } + + // Initialize contract manager + rp, err = rocketpool.NewRocketPool(client, common.HexToAddress(tests.RocketStorageAddress)) + if err != nil { + log.Fatal(err) + } + + // Initialize accounts + ownerAccount, err = accounts.GetAccount(0) + if err != nil { + log.Fatal(err) + } + trustedNodeAccount, err = accounts.GetAccount(1) + if err != nil { + log.Fatal(err) + } + nodeAccount, err = accounts.GetAccount(2) + if err != nil { + log.Fatal(err) + } + + // Run tests + os.Exit(m.Run()) + +} diff --git a/bindings/tests/rewards/node_test.go b/bindings/tests/rewards/node_test.go new file mode 100644 index 000000000..ec164fd4b --- /dev/null +++ b/bindings/tests/rewards/node_test.go @@ -0,0 +1,173 @@ +package rewards + +import ( + "context" + "fmt" + "math/big" + "testing" + + "github.com/rocket-pool/smartnode/bindings/deposit" + "github.com/rocket-pool/smartnode/bindings/node" + "github.com/rocket-pool/smartnode/bindings/rewards" + "github.com/rocket-pool/smartnode/bindings/settings/protocol" + "github.com/rocket-pool/smartnode/bindings/settings/trustednode" + "github.com/rocket-pool/smartnode/bindings/tests/testutils/evm" + minipoolutils "github.com/rocket-pool/smartnode/bindings/tests/testutils/minipool" + "github.com/rocket-pool/smartnode/bindings/tokens" + "github.com/rocket-pool/smartnode/bindings/utils/eth" +) + +func TestNodeRewards(t *testing.T) { + + // State snapshotting + if err := evm.TakeSnapshot(); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := evm.RevertSnapshot(); err != nil { + t.Fatal(err) + } + }) + + // Constants + oneDay := 24 * 60 * 60 + rewardInterval := oneDay + + // Register node + if _, err := node.RegisterNode(rp, "Australia/Brisbane", nodeAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Set network parameters + if _, err := protocol.BootstrapRewardsClaimIntervalTime(rp, uint64(rewardInterval), ownerAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Get & check node claims enabled status + if claimsEnabled, err := rewards.GetNodeClaimsEnabled(rp, nil); err != nil { + t.Error(err) + } else if !claimsEnabled { + t.Error("Incorrect node claims enabled status") + } + + // Get & check initial node claim possible status + if nodeClaimPossible, err := rewards.GetNodeClaimPossible(rp, nodeAccount.Address, nil); err != nil { + t.Error(err) + } else if nodeClaimPossible { + t.Error("Incorrect initial node claim possible status") + } + + // Increase time until node claims are possible + if err := evm.IncreaseTime(rewardInterval); err != nil { + t.Fatal(err) + } + + // Get & check updated node claim possible status + if nodeClaimPossible, err := rewards.GetNodeClaimPossible(rp, nodeAccount.Address, nil); err != nil { + t.Error(err) + } else if !nodeClaimPossible { + t.Error("Incorrect updated node claim possible status") + } + + // Get & check initial node claim rewards percent + if rewardsPerc, err := rewards.GetNodeClaimRewardsPerc(rp, nodeAccount.Address, nil); err != nil { + t.Error(err) + } else if rewardsPerc != 0 { + t.Errorf("Incorrect initial node claim rewards perc %f", rewardsPerc) + } + + // Stake RPL & create a minipool + mp, err := minipoolutils.CreateMinipool(t, rp, ownerAccount, nodeAccount, eth.EthToWei(16), 1) + if err != nil { + t.Fatal(err) + } + + // Deposit user ETH to minipool + opts := nodeAccount.GetTransactor() + opts.Value = eth.EthToWei(16) + if _, err := deposit.Deposit(rp, opts); err != nil { + t.Error(err) + } + + // Delay for the time between depositing and staking + scrubPeriod, err := trustednode.GetScrubPeriod(rp, nil) + if err != nil { + t.Fatal(err) + } + err = evm.IncreaseTime(int(scrubPeriod + 1)) + if err != nil { + t.Fatal(fmt.Errorf("error increasing time: %w", err)) + } + + // Stake minipool + if err := minipoolutils.StakeMinipool(rp, mp, nodeAccount); err != nil { + t.Error(err) + } + + // Get & check updated node claim rewards percent + if rewardsPerc, err := rewards.GetNodeClaimRewardsPerc(rp, nodeAccount.Address, nil); err != nil { + t.Error(err) + } else if rewardsPerc != 1 { + t.Errorf("Incorrect updated node claim rewards perc %f", rewardsPerc) + } + + // Get & check initial node claim rewards amount + if rewardsAmount, err := rewards.GetNodeClaimRewardsAmount(rp, nodeAccount.Address, nil); err != nil { + t.Error(err) + } else if rewardsAmount.Cmp(big.NewInt(0)) != 0 { + t.Errorf("Incorrect initial node claim rewards amount %s", rewardsAmount.String()) + } + + // Get & check initial RPL rewards amount + if pendingRewards, err := rewards.GetPendingRewards(rp, nil); err != nil { + t.Error(err) + } else if pendingRewards != 0 { + t.Errorf("Incorrect initial pending rewards amount %f", pendingRewards) + } + + // Start RPL inflation + if header, err := rp.Client.HeaderByNumber(context.Background(), nil); err != nil { + t.Fatal(err) + } else if _, err := protocol.BootstrapInflationStartTime(rp, header.Time+uint64(oneDay), ownerAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Increase time until rewards are available + if err := evm.IncreaseTime(oneDay + oneDay); err != nil { + t.Fatal(err) + } + + // Get & check updated node claim rewards amount + if rewardsAmount, err := rewards.GetNodeClaimRewardsAmount(rp, nodeAccount.Address, nil); err != nil { + t.Error(err) + } else if rewardsAmount.Cmp(big.NewInt(0)) != 1 { + t.Errorf("Incorrect updated node claim rewards amount %s", rewardsAmount.String()) + } + + // Get & check updated RPL rewards amount + if pendingRewards, err := rewards.GetPendingRewards(rp, nil); err != nil { + t.Error(err) + } else if pendingRewards <= 0 { + t.Errorf("Incorrect updated pending rewards amount %f", pendingRewards) + } + + // Get & check initial node RPL balance + if rplBalance, err := tokens.GetRPLBalance(rp, nodeAccount.Address, nil); err != nil { + t.Error(err) + } else if rplBalance.Cmp(big.NewInt(0)) != 0 { + t.Errorf("Incorrect initial node RPL balance %s", rplBalance.String()) + } + + // Claim node rewards + if _, err := rewards.ClaimNodeRewards(rp, nodeAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Get & check updated node RPL balance + if rplBalance, err := tokens.GetRPLBalance(rp, nodeAccount.Address, nil); err != nil { + t.Error(err) + } else if rplBalance.Cmp(big.NewInt(0)) != 1 { + t.Errorf("Incorrect updated node RPL balance %s", rplBalance.String()) + } + +} diff --git a/bindings/tests/rewards/trusted_node_test.go b/bindings/tests/rewards/trusted_node_test.go new file mode 100644 index 000000000..85dc84d7f --- /dev/null +++ b/bindings/tests/rewards/trusted_node_test.go @@ -0,0 +1,120 @@ +package rewards + +import ( + "context" + "math/big" + "testing" + + "github.com/rocket-pool/smartnode/bindings/rewards" + "github.com/rocket-pool/smartnode/bindings/settings/protocol" + "github.com/rocket-pool/smartnode/bindings/tokens" + + "github.com/rocket-pool/smartnode/bindings/tests/testutils/evm" + nodeutils "github.com/rocket-pool/smartnode/bindings/tests/testutils/node" +) + +func TestTrustedNodeRewards(t *testing.T) { + + // State snapshotting + if err := evm.TakeSnapshot(); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := evm.RevertSnapshot(); err != nil { + t.Fatal(err) + } + }) + + // Constants + oneDay := 24 * 60 * 60 + rewardInterval := oneDay + + // Register node + if err := nodeutils.RegisterTrustedNode(rp, ownerAccount, trustedNodeAccount); err != nil { + t.Fatal(err) + } + + // Set network parameters + if _, err := protocol.BootstrapRewardsClaimIntervalTime(rp, uint64(rewardInterval), ownerAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Get & check trusted node claims enabled status + if claimsEnabled, err := rewards.GetTrustedNodeClaimsEnabled(rp, nil); err != nil { + t.Error(err) + } else if !claimsEnabled { + t.Error("Incorrect trusted node claims enabled status") + } + + // Get & check initial trusted node claim possible status + if nodeClaimPossible, err := rewards.GetTrustedNodeClaimPossible(rp, trustedNodeAccount.Address, nil); err != nil { + t.Error(err) + } else if nodeClaimPossible { + t.Error("Incorrect initial trusted node claim possible status") + } + + // Increase time until node claims are possible + if err := evm.IncreaseTime(rewardInterval); err != nil { + t.Fatal(err) + } + + // Get & check updated trusted node claim possible status + if nodeClaimPossible, err := rewards.GetTrustedNodeClaimPossible(rp, trustedNodeAccount.Address, nil); err != nil { + t.Error(err) + } else if !nodeClaimPossible { + t.Error("Incorrect updated trusted node claim possible status") + } + + // Get & check trusted node claim rewards percent + if rewardsPerc, err := rewards.GetTrustedNodeClaimRewardsPerc(rp, trustedNodeAccount.Address, nil); err != nil { + t.Error(err) + } else if rewardsPerc != 1 { + t.Errorf("Incorrect trusted node claim rewards perc %f", rewardsPerc) + } + + // Get & check initial trusted node claim rewards amount + if rewardsAmount, err := rewards.GetTrustedNodeClaimRewardsAmount(rp, trustedNodeAccount.Address, nil); err != nil { + t.Error(err) + } else if rewardsAmount.Cmp(big.NewInt(0)) != 0 { + t.Errorf("Incorrect initial trusted node claim rewards amount %s", rewardsAmount.String()) + } + + // Start RPL inflation + if header, err := rp.Client.HeaderByNumber(context.Background(), nil); err != nil { + t.Fatal(err) + } else if _, err := protocol.BootstrapInflationStartTime(rp, header.Time+uint64(oneDay), ownerAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Increase time until rewards are available + if err := evm.IncreaseTime(oneDay + oneDay); err != nil { + t.Fatal(err) + } + + // Get & check updated trusted node claim rewards amount + if rewardsAmount, err := rewards.GetTrustedNodeClaimRewardsAmount(rp, trustedNodeAccount.Address, nil); err != nil { + t.Error(err) + } else if rewardsAmount.Cmp(big.NewInt(0)) != 1 { + t.Errorf("Incorrect updated trusted node claim rewards amount %s", rewardsAmount.String()) + } + + // Get & check initial node RPL balance + if rplBalance, err := tokens.GetRPLBalance(rp, trustedNodeAccount.Address, nil); err != nil { + t.Error(err) + } else if rplBalance.Cmp(big.NewInt(0)) != 0 { + t.Errorf("Incorrect initial node RPL balance %s", rplBalance.String()) + } + + // Claim node rewards + if _, err := rewards.ClaimTrustedNodeRewards(rp, trustedNodeAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Get & check updated node RPL balance + if rplBalance, err := tokens.GetRPLBalance(rp, trustedNodeAccount.Address, nil); err != nil { + t.Error(err) + } else if rplBalance.Cmp(big.NewInt(0)) != 1 { + t.Errorf("Incorrect updated node RPL balance %s", rplBalance.String()) + } + +} diff --git a/bindings/tests/rocketpool/main_test.go b/bindings/tests/rocketpool/main_test.go new file mode 100644 index 000000000..07d0e61c4 --- /dev/null +++ b/bindings/tests/rocketpool/main_test.go @@ -0,0 +1,39 @@ +package rocketpool + +import ( + "log" + "os" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethclient" + + "github.com/rocket-pool/smartnode/bindings/rocketpool" + + "github.com/rocket-pool/smartnode/bindings/tests" +) + +var ( + client *ethclient.Client + rp *rocketpool.RocketPool +) + +func TestMain(m *testing.M) { + var err error + + // Initialize eth client + client, err = ethclient.Dial(tests.Eth1ProviderAddress) + if err != nil { + log.Fatal(err) + } + + // Initialize contract manager + rp, err = rocketpool.NewRocketPool(client, common.HexToAddress(tests.RocketStorageAddress)) + if err != nil { + log.Fatal(err) + } + + // Run tests + os.Exit(m.Run()) + +} diff --git a/bindings/tests/rocketpool/rocketpool_test.go b/bindings/tests/rocketpool/rocketpool_test.go new file mode 100644 index 000000000..4c23c15be --- /dev/null +++ b/bindings/tests/rocketpool/rocketpool_test.go @@ -0,0 +1,157 @@ +package rocketpool + +import ( + "bytes" + "encoding/json" + "testing" + + "github.com/ethereum/go-ethereum/common" +) + +func TestGetAddress(t *testing.T) { + + // Get contract address + address1, err := rp.GetAddress("rocketDepositPool") + if err != nil { + t.Fatalf("error getting contract address: %s", err) + } else if bytes.Equal(address1.Bytes(), common.Address{}.Bytes()) { + t.Error("Contract address was not found") + } + + // Get cached contract address + address2, err := rp.GetAddress("rocketDepositPool") + if err != nil { + t.Fatalf("error getting cached contract address: %s", err) + } else if !bytes.Equal(address2.Bytes(), address1.Bytes()) { + t.Error("Cached contract address did not match original contract address") + } + +} + +func TestGetAddresses(t *testing.T) { + + // Get contract addresses + addresses1, err := rp.GetAddresses("rocketNodeManager", "rocketNodeDeposit") + if err != nil { + t.Fatalf("error getting contract addresses: %s", err) + } else { + for ai, address := range addresses1 { + if bytes.Equal(address.Bytes(), common.Address{}.Bytes()) { + t.Errorf("Contract address %d was not found", ai) + } + } + } + + // Get cached contract addresses + addresses2, err := rp.GetAddresses("rocketNodeManager", "rocketNodeDeposit") + if err != nil { + t.Fatalf("error getting cached contract addresses: %s", err) + } else { + for ai := 0; ai < len(addresses2); ai++ { + if !bytes.Equal(addresses2[ai].Bytes(), addresses1[ai].Bytes()) { + t.Errorf("Cached contract address %d did not match original contract address", ai) + } + } + } + +} + +func TestGetABI(t *testing.T) { + + // Get ABI + abi1, err := rp.GetABI("rocketDepositPool") + if err != nil { + t.Fatalf("error getting contract ABI: %s", err) + } + + // Get cached ABI + abi2, err := rp.GetABI("rocketDepositPool") + if err != nil { + t.Fatalf("error getting cached contract ABI: %s", err) + } else { + abi2Json, err := json.Marshal(abi2) + if err != nil { + t.Fatal(err) + } + abi1Json, err := json.Marshal(abi1) + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(abi2Json, abi1Json) { + t.Error("Cached contract ABI did not match original contract ABI") + } + } + +} + +func TestGetABIs(t *testing.T) { + + // Get ABIs + abis1, err := rp.GetABIs("rocketNodeManager", "rocketNodeDeposit") + if err != nil { + t.Fatalf("error getting contract ABIs: %s", err) + } + + // Get cached ABIs + abis2, err := rp.GetABIs("rocketNodeManager", "rocketNodeDeposit") + if err != nil { + t.Fatalf("error getting cached contract ABIs: %s", err) + } else { + for ai := 0; ai < len(abis2); ai++ { + abi2Json, err := json.Marshal(abis2[ai]) + if err != nil { + t.Fatal(err) + } + abi1Json, err := json.Marshal(abis1[ai]) + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(abi2Json, abi1Json) { + t.Errorf("Cached contract ABI %d did not match original contract ABI", ai) + } + } + } + +} + +func TestGetContract(t *testing.T) { + + // Get contract + if _, err := rp.GetContract("rocketDepositPool"); err != nil { + t.Fatalf("error getting contract: %s", err) + } + + // Get cached contract + if _, err := rp.GetContract("rocketDepositPool"); err != nil { + t.Fatalf("error getting cached contract: %s", err) + } + +} + +func TestGetContracts(t *testing.T) { + + // Get contracts + if _, err := rp.GetContracts("rocketNodeManager", "rocketNodeDeposit"); err != nil { + t.Fatalf("error getting contracts: %s", err) + } + + // Get cached contracts + if _, err := rp.GetContracts("rocketNodeManager", "rocketNodeDeposit"); err != nil { + t.Fatalf("error getting cached contracts: %s", err) + } + +} + +func TestMakeContract(t *testing.T) { + + // Make contract + if _, err := rp.MakeContract("rocketMinipool", common.HexToAddress("0x1111111111111111111111111111111111111111")); err != nil { + t.Fatalf("error making contract: %s", err) + } + + // Make contract with cached ABI + if _, err := rp.MakeContract("rocketMinipool", common.HexToAddress("0x2222222222222222222222222222222222222222")); err != nil { + t.Fatalf("error making contract with cached ABI: %s", err) + } + +} diff --git a/bindings/tests/settings/protocol/auction_test.go b/bindings/tests/settings/protocol/auction_test.go new file mode 100644 index 000000000..0a48d236c --- /dev/null +++ b/bindings/tests/settings/protocol/auction_test.go @@ -0,0 +1,94 @@ +package protocol + +import ( + "testing" + + "github.com/rocket-pool/smartnode/bindings/settings/protocol" + "github.com/rocket-pool/smartnode/bindings/utils/eth" + + "github.com/rocket-pool/smartnode/bindings/tests/testutils/evm" +) + +func TestAuctionSettings(t *testing.T) { + + // State snapshotting + if err := evm.TakeSnapshot(); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := evm.RevertSnapshot(); err != nil { + t.Fatal(err) + } + }) + + // Set & get creat lots enabled + createLotEnabled := false + if _, err := protocol.BootstrapCreateLotEnabled(rp, createLotEnabled, ownerAccount.GetTransactor()); err != nil { + t.Error(err) + } else if value, err := protocol.GetCreateLotEnabled(rp, nil); err != nil { + t.Error(err) + } else if value != createLotEnabled { + t.Error("Incorrect creat lots enabled value") + } + + // Set & get bid on lot enabled + bidOnLotEnabled := false + if _, err := protocol.BootstrapBidOnLotEnabled(rp, bidOnLotEnabled, ownerAccount.GetTransactor()); err != nil { + t.Error(err) + } else if value, err := protocol.GetBidOnLotEnabled(rp, nil); err != nil { + t.Error(err) + } else if value != bidOnLotEnabled { + t.Error("Incorrect bid on lot enabled value") + } + + // Set & get lot minimum ETH value + lotMinimumEthValue := eth.EthToWei(1000) + if _, err := protocol.BootstrapLotMinimumEthValue(rp, lotMinimumEthValue, ownerAccount.GetTransactor()); err != nil { + t.Error(err) + } else if value, err := protocol.GetLotMinimumEthValue(rp, nil); err != nil { + t.Error(err) + } else if value.Cmp(lotMinimumEthValue) != 0 { + t.Error("Incorrect lot minimum ETH value value") + } + + // Set & get lot maximum ETH value + lotMaximumEthValue := eth.EthToWei(0.01) + if _, err := protocol.BootstrapLotMaximumEthValue(rp, lotMaximumEthValue, ownerAccount.GetTransactor()); err != nil { + t.Error(err) + } else if value, err := protocol.GetLotMaximumEthValue(rp, nil); err != nil { + t.Error(err) + } else if value.Cmp(lotMaximumEthValue) != 0 { + t.Error("Incorrect lot maximum ETH value value") + } + + // Set & get lot duration + var lotDuration uint64 = 1 + if _, err := protocol.BootstrapLotDuration(rp, lotDuration, ownerAccount.GetTransactor()); err != nil { + t.Error(err) + } else if value, err := protocol.GetLotDuration(rp, nil); err != nil { + t.Error(err) + } else if value != lotDuration { + t.Error("Incorrect lot duration value") + } + + // Set & get lot starting price ratio + lotStartingPriceRatio := 2.0 + if _, err := protocol.BootstrapLotStartingPriceRatio(rp, lotStartingPriceRatio, ownerAccount.GetTransactor()); err != nil { + t.Error(err) + } else if value, err := protocol.GetLotStartingPriceRatio(rp, nil); err != nil { + t.Error(err) + } else if value != lotStartingPriceRatio { + t.Error("Incorrect lot starting price ratio value") + } + + // Set & get lot reserve price ratio + lotReservePriceRatio := 1.9 + if _, err := protocol.BootstrapLotReservePriceRatio(rp, lotReservePriceRatio, ownerAccount.GetTransactor()); err != nil { + t.Error(err) + } else if value, err := protocol.GetLotReservePriceRatio(rp, nil); err != nil { + t.Error(err) + } else if value != lotReservePriceRatio { + t.Error("Incorrect lot reserve price ratio value") + } + +} diff --git a/bindings/tests/settings/protocol/deposit_test.go b/bindings/tests/settings/protocol/deposit_test.go new file mode 100644 index 000000000..35dc9f3e4 --- /dev/null +++ b/bindings/tests/settings/protocol/deposit_test.go @@ -0,0 +1,74 @@ +package protocol + +import ( + "testing" + + "github.com/rocket-pool/smartnode/bindings/settings/protocol" + "github.com/rocket-pool/smartnode/bindings/utils/eth" + + "github.com/rocket-pool/smartnode/bindings/tests/testutils/evm" +) + +func TestDepositSettings(t *testing.T) { + + // State snapshotting + if err := evm.TakeSnapshot(); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := evm.RevertSnapshot(); err != nil { + t.Fatal(err) + } + }) + + // Set & get deposits enabled + depositEnabled := false + if _, err := protocol.BootstrapDepositEnabled(rp, depositEnabled, ownerAccount.GetTransactor()); err != nil { + t.Error(err) + } else if value, err := protocol.GetDepositEnabled(rp, nil); err != nil { + t.Error(err) + } else if value != depositEnabled { + t.Error("Incorrect deposit enabled value") + } + + // Set & get deposit assignments enabled + assignDepositsEnabled := false + if _, err := protocol.BootstrapAssignDepositsEnabled(rp, assignDepositsEnabled, ownerAccount.GetTransactor()); err != nil { + t.Error(err) + } else if value, err := protocol.GetAssignDepositsEnabled(rp, nil); err != nil { + t.Error(err) + } else if value != assignDepositsEnabled { + t.Error("Incorrect assign deposits enabled value") + } + + // Set & get minimum deposit amount + minimumDeposit := eth.EthToWei(1000) + if _, err := protocol.BootstrapMinimumDeposit(rp, minimumDeposit, ownerAccount.GetTransactor()); err != nil { + t.Error(err) + } else if value, err := protocol.GetMinimumDeposit(rp, nil); err != nil { + t.Error(err) + } else if value.Cmp(minimumDeposit) != 0 { + t.Error("Incorrect minimum deposit value") + } + + // Set & get maximum deposit pool size + maximumDepositPoolSize := eth.EthToWei(1) + if _, err := protocol.BootstrapMaximumDepositPoolSize(rp, maximumDepositPoolSize, ownerAccount.GetTransactor()); err != nil { + t.Error(err) + } else if value, err := protocol.GetMaximumDepositPoolSize(rp, nil); err != nil { + t.Error(err) + } else if value.Cmp(maximumDepositPoolSize) != 0 { + t.Error("Incorrect maximum deposit pool size value") + } + + // Set & get maximum deposit assignments + var maximumDepositAssignments uint64 = 50 + if _, err := protocol.BootstrapMaximumDepositAssignments(rp, maximumDepositAssignments, ownerAccount.GetTransactor()); err != nil { + t.Error(err) + } else if value, err := protocol.GetMaximumDepositAssignments(rp, nil); err != nil { + t.Error(err) + } else if value != maximumDepositAssignments { + t.Error("Incorrect maximum deposit assignments value") + } + +} diff --git a/bindings/tests/settings/protocol/inflation_test.go b/bindings/tests/settings/protocol/inflation_test.go new file mode 100644 index 000000000..97e7381c7 --- /dev/null +++ b/bindings/tests/settings/protocol/inflation_test.go @@ -0,0 +1,44 @@ +package protocol + +import ( + "testing" + "time" + + "github.com/rocket-pool/smartnode/bindings/settings/protocol" + + "github.com/rocket-pool/smartnode/bindings/tests/testutils/evm" +) + +func TestInflationSettings(t *testing.T) { + + // State snapshotting + if err := evm.TakeSnapshot(); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := evm.RevertSnapshot(); err != nil { + t.Fatal(err) + } + }) + + // Set & get inflation interval rate + inflationIntervalRate := 0.5 + if _, err := protocol.BootstrapInflationIntervalRate(rp, inflationIntervalRate, ownerAccount.GetTransactor()); err != nil { + t.Error(err) + } else if value, err := protocol.GetInflationIntervalRate(rp, nil); err != nil { + t.Error(err) + } else if value != inflationIntervalRate { + t.Error("Incorrect inflation interval rate value") + } + + // Set & get inflation start block + inflationStartTime := uint64(time.Now().Unix()) + 3600 + if _, err := protocol.BootstrapInflationStartTime(rp, inflationStartTime, ownerAccount.GetTransactor()); err != nil { + t.Error(err) + } else if value, err := protocol.GetInflationStartTime(rp, nil); err != nil { + t.Error(err) + } else if value != inflationStartTime { + t.Error("Incorrect inflation start time value") + } + +} diff --git a/bindings/tests/settings/protocol/main_test.go b/bindings/tests/settings/protocol/main_test.go new file mode 100644 index 000000000..4b5d546f4 --- /dev/null +++ b/bindings/tests/settings/protocol/main_test.go @@ -0,0 +1,48 @@ +package protocol + +import ( + "log" + "os" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethclient" + + "github.com/rocket-pool/smartnode/bindings/rocketpool" + + "github.com/rocket-pool/smartnode/bindings/tests" + "github.com/rocket-pool/smartnode/bindings/tests/testutils/accounts" +) + +var ( + client *ethclient.Client + rp *rocketpool.RocketPool + + ownerAccount *accounts.Account +) + +func TestMain(m *testing.M) { + var err error + + // Initialize eth client + client, err = ethclient.Dial(tests.Eth1ProviderAddress) + if err != nil { + log.Fatal(err) + } + + // Initialize contract manager + rp, err = rocketpool.NewRocketPool(client, common.HexToAddress(tests.RocketStorageAddress)) + if err != nil { + log.Fatal(err) + } + + // Initialize accounts + ownerAccount, err = accounts.GetAccount(0) + if err != nil { + log.Fatal(err) + } + + // Run tests + os.Exit(m.Run()) + +} diff --git a/bindings/tests/settings/protocol/minipool_test.go b/bindings/tests/settings/protocol/minipool_test.go new file mode 100644 index 000000000..1af631eed --- /dev/null +++ b/bindings/tests/settings/protocol/minipool_test.go @@ -0,0 +1,84 @@ +package protocol + +import ( + "testing" + "time" + + "github.com/rocket-pool/smartnode/bindings/settings/protocol" + "github.com/rocket-pool/smartnode/bindings/utils/eth" + + "github.com/rocket-pool/smartnode/bindings/tests/testutils/evm" +) + +func TestMinipoolSettings(t *testing.T) { + + // State snapshotting + if err := evm.TakeSnapshot(); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := evm.RevertSnapshot(); err != nil { + t.Fatal(err) + } + }) + + // Get & check launch balance and deposit amounts + fullMinipoolBalance := eth.EthToWei(32) + halfMinipoolBalance := eth.EthToWei(16) + emptyMinipoolBalance := eth.EthToWei(0) + if value, err := protocol.GetMinipoolLaunchBalance(rp, nil); err != nil { + t.Error(err) + } else if value.Cmp(fullMinipoolBalance) != 0 { + t.Error("Incorrect minipool launch balance") + } + if value, err := protocol.GetMinipoolFullDepositNodeAmount(rp, nil); err != nil { + t.Error(err) + } else if value.Cmp(fullMinipoolBalance) != 0 { + t.Error("Incorrect minipool full deposit node amount") + } + if value, err := protocol.GetMinipoolHalfDepositNodeAmount(rp, nil); err != nil { + t.Error(err) + } else if value.Cmp(halfMinipoolBalance) != 0 { + t.Error("Incorrect minipool half deposit node amount") + } + if value, err := protocol.GetMinipoolEmptyDepositNodeAmount(rp, nil); err != nil { + t.Error(err) + } else if value.Cmp(emptyMinipoolBalance) != 0 { + t.Error("Incorrect minipool empty deposit node amount") + } + if value, err := protocol.GetMinipoolFullDepositUserAmount(rp, nil); err != nil { + t.Error(err) + } else if value.Cmp(halfMinipoolBalance) != 0 { + t.Error("Incorrect minipool full deposit user amount") + } + if value, err := protocol.GetMinipoolHalfDepositUserAmount(rp, nil); err != nil { + t.Error(err) + } else if value.Cmp(halfMinipoolBalance) != 0 { + t.Error("Incorrect minipool half deposit user amount") + } + if value, err := protocol.GetMinipoolEmptyDepositUserAmount(rp, nil); err != nil { + t.Error(err) + } else if value.Cmp(fullMinipoolBalance) != 0 { + t.Error("Incorrect minipool empty deposit user amount") + } + + // Set & get submit withdrawable enabled + submitWithdrawableEnabled := false + if _, err := protocol.BootstrapMinipoolSubmitWithdrawableEnabled(rp, submitWithdrawableEnabled, ownerAccount.GetTransactor()); err != nil { + t.Error(err) + } else if value, err := protocol.GetMinipoolSubmitWithdrawableEnabled(rp, nil); err != nil { + t.Error(err) + } else if value != submitWithdrawableEnabled { + t.Error("Incorrect minipool withdrawable submissions enabled value") + } + + // Set & get minipool launch timeout + var minipoolLaunchTimeout time.Duration = 5 * time.Second + if _, err := protocol.BootstrapMinipoolLaunchTimeout(rp, minipoolLaunchTimeout, ownerAccount.GetTransactor()); err != nil { + t.Error(err) + } else if value, err := protocol.GetMinipoolLaunchTimeout(rp, nil); err != nil { + t.Error(err) + } else if value != minipoolLaunchTimeout { + t.Error("Incorrect minipool launch timeout value") + } +} diff --git a/bindings/tests/settings/protocol/network_test.go b/bindings/tests/settings/protocol/network_test.go new file mode 100644 index 000000000..75ed259b1 --- /dev/null +++ b/bindings/tests/settings/protocol/network_test.go @@ -0,0 +1,124 @@ +package protocol + +import ( + "testing" + + "github.com/rocket-pool/smartnode/bindings/settings/protocol" + "github.com/rocket-pool/smartnode/bindings/utils/eth" + + "github.com/rocket-pool/smartnode/bindings/tests/testutils/evm" +) + +func TestNetworkSettings(t *testing.T) { + + // State snapshotting + if err := evm.TakeSnapshot(); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := evm.RevertSnapshot(); err != nil { + t.Fatal(err) + } + }) + + // Set & get node consensus threshold + nodeConsensusThreshold := 0.1 + if _, err := protocol.BootstrapNodeConsensusThreshold(rp, nodeConsensusThreshold, ownerAccount.GetTransactor()); err != nil { + t.Error(err) + } else if value, err := protocol.GetNodeConsensusThreshold(rp, nil); err != nil { + t.Error(err) + } else if value != nodeConsensusThreshold { + t.Error("Incorrect node consensus threshold value") + } + + // Set & get network balance submissions enabled + submitBalancesEnabled := false + if _, err := protocol.BootstrapSubmitBalancesEnabled(rp, submitBalancesEnabled, ownerAccount.GetTransactor()); err != nil { + t.Error(err) + } else if value, err := protocol.GetSubmitBalancesEnabled(rp, nil); err != nil { + t.Error(err) + } else if value != submitBalancesEnabled { + t.Error("Incorrect network balance submissions enabled value") + } + + // Set & get network balance submission frequency + var submitBalancesFrequency uint64 = 10 + if _, err := protocol.BootstrapSubmitBalancesFrequency(rp, submitBalancesFrequency, ownerAccount.GetTransactor()); err != nil { + t.Error(err) + } else if value, err := protocol.GetSubmitBalancesFrequency(rp, nil); err != nil { + t.Error(err) + } else if value != submitBalancesFrequency { + t.Error("Incorrect network balance submission frequency value") + } + + // Set & get network price submissions enabled + submitPricesEnabled := false + if _, err := protocol.BootstrapSubmitPricesEnabled(rp, submitPricesEnabled, ownerAccount.GetTransactor()); err != nil { + t.Error(err) + } else if value, err := protocol.GetSubmitPricesEnabled(rp, nil); err != nil { + t.Error(err) + } else if value != submitPricesEnabled { + t.Error("Incorrect network price submissions enabled value") + } + + // Set & get network price submission frequency + var submitPricesFrequency uint64 = 10 + if _, err := protocol.BootstrapSubmitPricesFrequency(rp, submitPricesFrequency, ownerAccount.GetTransactor()); err != nil { + t.Error(err) + } else if value, err := protocol.GetSubmitPricesFrequency(rp, nil); err != nil { + t.Error(err) + } else if value != submitPricesFrequency { + t.Error("Incorrect network price submission frequency value") + } + + // Set & get minimum node fee + minimumNodeFee := 0.80 + if _, err := protocol.BootstrapMinimumNodeFee(rp, minimumNodeFee, ownerAccount.GetTransactor()); err != nil { + t.Error(err) + } else if value, err := protocol.GetMinimumNodeFee(rp, nil); err != nil { + t.Error(err) + } else if value != minimumNodeFee { + t.Error("Incorrect minimum node fee value") + } + + // Set & get target node fee + targetNodeFee := 0.85 + if _, err := protocol.BootstrapTargetNodeFee(rp, targetNodeFee, ownerAccount.GetTransactor()); err != nil { + t.Error(err) + } else if value, err := protocol.GetTargetNodeFee(rp, nil); err != nil { + t.Error(err) + } else if value != targetNodeFee { + t.Error("Incorrect target node fee value") + } + + // Set & get maximum node fee + maximumNodeFee := 0.90 + if _, err := protocol.BootstrapMaximumNodeFee(rp, maximumNodeFee, ownerAccount.GetTransactor()); err != nil { + t.Error(err) + } else if value, err := protocol.GetMaximumNodeFee(rp, nil); err != nil { + t.Error(err) + } else if value != maximumNodeFee { + t.Error("Incorrect maximum node fee value") + } + + // Set & get node fee demand range + nodeFeeDemandRange := eth.EthToWei(10) + if _, err := protocol.BootstrapNodeFeeDemandRange(rp, nodeFeeDemandRange, ownerAccount.GetTransactor()); err != nil { + t.Error(err) + } else if value, err := protocol.GetNodeFeeDemandRange(rp, nil); err != nil { + t.Error(err) + } else if value.Cmp(nodeFeeDemandRange) != 0 { + t.Error("Incorrect node fee demand range value") + } + + // Set & get target rETH collateral rate + targetRethCollateralRate := 0.95 + if _, err := protocol.BootstrapTargetRethCollateralRate(rp, targetRethCollateralRate, ownerAccount.GetTransactor()); err != nil { + t.Error(err) + } else if value, err := protocol.GetTargetRethCollateralRate(rp, nil); err != nil { + t.Error(err) + } else if value != targetRethCollateralRate { + t.Error("Incorrect target rETH collateral rate value") + } + +} diff --git a/bindings/tests/settings/protocol/node_test.go b/bindings/tests/settings/protocol/node_test.go new file mode 100644 index 000000000..6624cdc44 --- /dev/null +++ b/bindings/tests/settings/protocol/node_test.go @@ -0,0 +1,63 @@ +package protocol + +import ( + "testing" + + "github.com/rocket-pool/smartnode/bindings/settings/protocol" + + "github.com/rocket-pool/smartnode/bindings/tests/testutils/evm" +) + +func TestNodeSettings(t *testing.T) { + + // State snapshotting + if err := evm.TakeSnapshot(); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := evm.RevertSnapshot(); err != nil { + t.Fatal(err) + } + }) + + // Set & get node registrations enabled + nodeRegistrationsEnabled := false + if _, err := protocol.BootstrapNodeRegistrationEnabled(rp, nodeRegistrationsEnabled, ownerAccount.GetTransactor()); err != nil { + t.Error(err) + } else if value, err := protocol.GetNodeRegistrationEnabled(rp, nil); err != nil { + t.Error(err) + } else if value != nodeRegistrationsEnabled { + t.Error("Incorrect node registrations enabled value") + } + + // Set & get node deposits enabled + nodeDepositsEnabled := false + if _, err := protocol.BootstrapNodeDepositEnabled(rp, nodeDepositsEnabled, ownerAccount.GetTransactor()); err != nil { + t.Error(err) + } else if value, err := protocol.GetNodeDepositEnabled(rp, nil); err != nil { + t.Error(err) + } else if value != nodeDepositsEnabled { + t.Error("Incorrect node deposits enabled value") + } + + // Set & get minimum per minipool RPL stake + minimumPerMinipoolStake := 1.0 + if _, err := protocol.BootstrapMinimumPerMinipoolStake(rp, minimumPerMinipoolStake, ownerAccount.GetTransactor()); err != nil { + t.Error(err) + } else if value, err := protocol.GetMinimumPerMinipoolStake(rp, nil); err != nil { + t.Error(err) + } else if value != minimumPerMinipoolStake { + t.Error("Incorrect minimum per minipool stake value") + } + + // Set & get maximum per minipool RPL stake + maximumPerMinipoolStake := 10.0 + if _, err := protocol.BootstrapMaximumPerMinipoolStake(rp, maximumPerMinipoolStake, ownerAccount.GetTransactor()); err != nil { + t.Error(err) + } else if value, err := protocol.GetMaximumPerMinipoolStake(rp, nil); err != nil { + t.Error(err) + } else if value != maximumPerMinipoolStake { + t.Error("Incorrect maximum per minipool stake value") + } + +} diff --git a/bindings/tests/settings/protocol/rewards_test.go b/bindings/tests/settings/protocol/rewards_test.go new file mode 100644 index 000000000..a8b8f12f0 --- /dev/null +++ b/bindings/tests/settings/protocol/rewards_test.go @@ -0,0 +1,56 @@ +package protocol + +import ( + "testing" + + protocoldao "github.com/rocket-pool/smartnode/bindings/dao/protocol" + protocolsettings "github.com/rocket-pool/smartnode/bindings/settings/protocol" + + "github.com/rocket-pool/smartnode/bindings/tests/testutils/evm" +) + +func TestRewardsSettings(t *testing.T) { + + // State snapshotting + if err := evm.TakeSnapshot(); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := evm.RevertSnapshot(); err != nil { + t.Fatal(err) + } + }) + + // Bootstrap a claimer & get claimer settings + claimerPerc := 0.1 + if _, err := protocoldao.BootstrapClaimer(rp, "rocketClaimNode", claimerPerc, ownerAccount.GetTransactor()); err != nil { + t.Error(err) + } else { + if value, err := protocolsettings.GetRewardsClaimerPerc(rp, "rocketClaimNode", nil); err != nil { + t.Error(err) + } else if value != claimerPerc { + t.Errorf("Incorrect rewards claimer percent %f", value) + } + if value, err := protocolsettings.GetRewardsClaimerPercTimeUpdated(rp, "rocketClaimNode", nil); err != nil { + t.Error(err) + } else if value == 0 { + t.Errorf("Incorrect rewards claimer percent time updated %d", value) + } + if value, err := protocolsettings.GetRewardsClaimersPercTotal(rp, nil); err != nil { + t.Error(err) + } else if value == 0 { + t.Errorf("Incorrect rewards claimers total percent %f", value) + } + } + + // Set & get rewards claim interval time + var rewardsClaimIntervalTime uint64 = 1 + if _, err := protocolsettings.BootstrapRewardsClaimIntervalTime(rp, rewardsClaimIntervalTime, ownerAccount.GetTransactor()); err != nil { + t.Error(err) + } else if value, err := protocolsettings.GetRewardsClaimIntervalTime(rp, nil); err != nil { + t.Error(err) + } else if value != rewardsClaimIntervalTime { + t.Error("Incorrect rewards claim interval time value") + } + +} diff --git a/bindings/tests/settings/trustednode/main_test.go b/bindings/tests/settings/trustednode/main_test.go new file mode 100644 index 000000000..374b1c344 --- /dev/null +++ b/bindings/tests/settings/trustednode/main_test.go @@ -0,0 +1,63 @@ +package trustednode + +import ( + "log" + "os" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethclient" + + "github.com/rocket-pool/smartnode/bindings/rocketpool" + + "github.com/rocket-pool/smartnode/bindings/tests" + "github.com/rocket-pool/smartnode/bindings/tests/testutils/accounts" +) + +var ( + client *ethclient.Client + rp *rocketpool.RocketPool + + ownerAccount *accounts.Account + trustedNodeAccount1 *accounts.Account + trustedNodeAccount2 *accounts.Account + trustedNodeAccount3 *accounts.Account +) + +func TestMain(m *testing.M) { + var err error + + // Initialize eth client + client, err = ethclient.Dial(tests.Eth1ProviderAddress) + if err != nil { + log.Fatal(err) + } + + // Initialize contract manager + rp, err = rocketpool.NewRocketPool(client, common.HexToAddress(tests.RocketStorageAddress)) + if err != nil { + log.Fatal(err) + } + + // Initialize accounts + ownerAccount, err = accounts.GetAccount(0) + if err != nil { + log.Fatal(err) + } + trustedNodeAccount1, err = accounts.GetAccount(1) + if err != nil { + log.Fatal(err) + } + trustedNodeAccount2, err = accounts.GetAccount(2) + if err != nil { + log.Fatal(err) + } + trustedNodeAccount3, err = accounts.GetAccount(3) + if err != nil { + log.Fatal(err) + } + + // Run tests + os.Exit(m.Run()) + +} diff --git a/bindings/tests/settings/trustednode/members_test.go b/bindings/tests/settings/trustednode/members_test.go new file mode 100644 index 000000000..e1c0fdf99 --- /dev/null +++ b/bindings/tests/settings/trustednode/members_test.go @@ -0,0 +1,162 @@ +package trustednode + +import ( + "testing" + + "github.com/rocket-pool/smartnode/bindings/settings/trustednode" + "github.com/rocket-pool/smartnode/bindings/utils/eth" + + "github.com/rocket-pool/smartnode/bindings/tests/testutils/accounts" + daoutils "github.com/rocket-pool/smartnode/bindings/tests/testutils/dao" + "github.com/rocket-pool/smartnode/bindings/tests/testutils/evm" + nodeutils "github.com/rocket-pool/smartnode/bindings/tests/testutils/node" +) + +func TestBootstrapMembersSettings(t *testing.T) { + + // State snapshotting + if err := evm.TakeSnapshot(); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := evm.RevertSnapshot(); err != nil { + t.Fatal(err) + } + }) + + // Set & get quorum + quorum := 0.1 + if _, err := trustednode.BootstrapQuorum(rp, quorum, ownerAccount.GetTransactor()); err != nil { + t.Error(err) + } else if value, err := trustednode.GetQuorum(rp, nil); err != nil { + t.Error(err) + } else if value != quorum { + t.Error("Incorrect quorum value") + } + + // Set & get rpl bond + rplBond := eth.EthToWei(1) + if _, err := trustednode.BootstrapRPLBond(rp, rplBond, ownerAccount.GetTransactor()); err != nil { + t.Error(err) + } else if value, err := trustednode.GetRPLBond(rp, nil); err != nil { + t.Error(err) + } else if value.Cmp(rplBond) != 0 { + t.Error("Incorrect rpl bond value") + } + + // Set & get maximum unbonded minipools + var minipoolUnbondedMax uint64 = 1 + if _, err := trustednode.BootstrapMinipoolUnbondedMax(rp, minipoolUnbondedMax, ownerAccount.GetTransactor()); err != nil { + t.Error(err) + } else if value, err := trustednode.GetMinipoolUnbondedMax(rp, nil); err != nil { + t.Error(err) + } else if value != minipoolUnbondedMax { + t.Error("Incorrect maximum unbonded minipools value") + } + +} + +func TestProposeMembersSettings(t *testing.T) { + + // State snapshotting + if err := evm.TakeSnapshot(); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := evm.RevertSnapshot(); err != nil { + t.Fatal(err) + } + }) + + // Set proposal cooldown + if _, err := trustednode.BootstrapProposalCooldownTime(rp, 0, ownerAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + if _, err := trustednode.BootstrapProposalVoteDelayTime(rp, 5, ownerAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Register trusted node + if err := nodeutils.RegisterTrustedNode(rp, ownerAccount, trustedNodeAccount1); err != nil { + t.Fatal(err) + } + if err := nodeutils.RegisterTrustedNode(rp, ownerAccount, trustedNodeAccount2); err != nil { + t.Fatal(err) + } + if err := nodeutils.RegisterTrustedNode(rp, ownerAccount, trustedNodeAccount3); err != nil { + t.Fatal(err) + } + + // Set & get quorum + quorum := 0.1 + if proposalId, _, err := trustednode.ProposeQuorum(rp, quorum, trustedNodeAccount1.GetTransactor()); err != nil { + t.Error(err) + } else if err := daoutils.PassAndExecuteProposal(rp, proposalId, []*accounts.Account{trustedNodeAccount1, trustedNodeAccount2}); err != nil { + t.Error(err) + } else if value, err := trustednode.GetQuorum(rp, nil); err != nil { + t.Error(err) + } else if value != quorum { + t.Error("Incorrect quorum value") + } + + // Set & get rpl bond + rplBond := eth.EthToWei(1) + if proposalId, _, err := trustednode.ProposeRPLBond(rp, rplBond, trustedNodeAccount1.GetTransactor()); err != nil { + t.Error(err) + } else if err := daoutils.PassAndExecuteProposal(rp, proposalId, []*accounts.Account{trustedNodeAccount1, trustedNodeAccount2}); err != nil { + t.Error(err) + } else if value, err := trustednode.GetRPLBond(rp, nil); err != nil { + t.Error(err) + } else if value.Cmp(rplBond) != 0 { + t.Error("Incorrect rpl bond value") + } + + // Set & get maximum unbonded minipools + var minipoolUnbondedMax uint64 = 1 + if proposalId, _, err := trustednode.ProposeMinipoolUnbondedMax(rp, minipoolUnbondedMax, trustedNodeAccount1.GetTransactor()); err != nil { + t.Error(err) + } else if err := daoutils.PassAndExecuteProposal(rp, proposalId, []*accounts.Account{trustedNodeAccount1, trustedNodeAccount2}); err != nil { + t.Error(err) + } else if value, err := trustednode.GetMinipoolUnbondedMax(rp, nil); err != nil { + t.Error(err) + } else if value != minipoolUnbondedMax { + t.Error("Incorrect maximum unbonded minipools value") + } + + // Set & get member challenge cooldown period + var memberChallengeCooldown uint64 = 1 + if proposalId, _, err := trustednode.ProposeChallengeCooldown(rp, memberChallengeCooldown, trustedNodeAccount1.GetTransactor()); err != nil { + t.Error(err) + } else if err := daoutils.PassAndExecuteProposal(rp, proposalId, []*accounts.Account{trustedNodeAccount1, trustedNodeAccount2}); err != nil { + t.Error(err) + } else if value, err := trustednode.GetChallengeCooldown(rp, nil); err != nil { + t.Error(err) + } else if value != memberChallengeCooldown { + t.Error("Incorrect member challenge cooldown value") + } + + // Set & get member challenge window period + var memberChallengeWindow uint64 = 1 + if proposalId, _, err := trustednode.ProposeChallengeWindow(rp, memberChallengeWindow, trustedNodeAccount1.GetTransactor()); err != nil { + t.Error(err) + } else if err := daoutils.PassAndExecuteProposal(rp, proposalId, []*accounts.Account{trustedNodeAccount1, trustedNodeAccount2}); err != nil { + t.Error(err) + } else if value, err := trustednode.GetChallengeWindow(rp, nil); err != nil { + t.Error(err) + } else if value != memberChallengeWindow { + t.Error("Incorrect member challenge window value") + } + + // Set & get member challenge cost amount + challengeCost := eth.EthToWei(1) + if proposalId, _, err := trustednode.ProposeChallengeCost(rp, challengeCost, trustedNodeAccount1.GetTransactor()); err != nil { + t.Error(err) + } else if err := daoutils.PassAndExecuteProposal(rp, proposalId, []*accounts.Account{trustedNodeAccount1, trustedNodeAccount2}); err != nil { + t.Error(err) + } else if value, err := trustednode.GetChallengeCost(rp, nil); err != nil { + t.Error(err) + } else if value.Cmp(challengeCost) != 0 { + t.Error("Incorrect member challenge cost value") + } + +} diff --git a/bindings/tests/settings/trustednode/proposals_test.go b/bindings/tests/settings/trustednode/proposals_test.go new file mode 100644 index 000000000..668b649e5 --- /dev/null +++ b/bindings/tests/settings/trustednode/proposals_test.go @@ -0,0 +1,169 @@ +package trustednode + +import ( + "testing" + + "github.com/rocket-pool/smartnode/bindings/settings/trustednode" + + "github.com/rocket-pool/smartnode/bindings/tests/testutils/accounts" + daoutils "github.com/rocket-pool/smartnode/bindings/tests/testutils/dao" + "github.com/rocket-pool/smartnode/bindings/tests/testutils/evm" + nodeutils "github.com/rocket-pool/smartnode/bindings/tests/testutils/node" +) + +func TestBootstrapProposalsSettings(t *testing.T) { + + // State snapshotting + if err := evm.TakeSnapshot(); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := evm.RevertSnapshot(); err != nil { + t.Fatal(err) + } + }) + + // Set & get cooldown + var cooldown uint64 = 1 + if _, err := trustednode.BootstrapProposalCooldownTime(rp, cooldown, ownerAccount.GetTransactor()); err != nil { + t.Error(err) + } else if value, err := trustednode.GetProposalCooldownTime(rp, nil); err != nil { + t.Error(err) + } else if value != cooldown { + t.Error("Incorrect cooldown value") + } + + // Set & get vote time + var voteTime uint64 = 10 + if _, err := trustednode.BootstrapProposalVoteTime(rp, voteTime, ownerAccount.GetTransactor()); err != nil { + t.Error(err) + } else if value, err := trustednode.GetProposalVoteTime(rp, nil); err != nil { + t.Error(err) + } else if value != voteTime { + t.Error("Incorrect vote time value") + } + + // Set & get execute time + var executeTime uint64 = 10 + if _, err := trustednode.BootstrapProposalExecuteTime(rp, executeTime, ownerAccount.GetTransactor()); err != nil { + t.Error(err) + } else if value, err := trustednode.GetProposalExecuteTime(rp, nil); err != nil { + t.Error(err) + } else if value != executeTime { + t.Error("Incorrect execute time value") + } + + // Set & get action time + var actionTime uint64 = 10 + if _, err := trustednode.BootstrapProposalActionTime(rp, actionTime, ownerAccount.GetTransactor()); err != nil { + t.Error(err) + } else if value, err := trustednode.GetProposalActionTime(rp, nil); err != nil { + t.Error(err) + } else if value != actionTime { + t.Error("Incorrect action time value") + } + + // Set & get vote delay time + var voteDelayTime uint64 = 1000 + if _, err := trustednode.BootstrapProposalVoteDelayTime(rp, voteDelayTime, ownerAccount.GetTransactor()); err != nil { + t.Error(err) + } else if value, err := trustednode.GetProposalVoteDelayTime(rp, nil); err != nil { + t.Error(err) + } else if value != voteDelayTime { + t.Error("Incorrect vote delay time value") + } + +} + +func TestProposeProposalsSettings(t *testing.T) { + + // State snapshotting + if err := evm.TakeSnapshot(); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := evm.RevertSnapshot(); err != nil { + t.Fatal(err) + } + }) + + // Set proposal cooldown + if _, err := trustednode.BootstrapProposalCooldownTime(rp, 0, ownerAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + if _, err := trustednode.BootstrapProposalVoteDelayTime(rp, 5, ownerAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Register trusted node + if err := nodeutils.RegisterTrustedNode(rp, ownerAccount, trustedNodeAccount1); err != nil { + t.Fatal(err) + } + if err := nodeutils.RegisterTrustedNode(rp, ownerAccount, trustedNodeAccount2); err != nil { + t.Fatal(err) + } + if err := nodeutils.RegisterTrustedNode(rp, ownerAccount, trustedNodeAccount3); err != nil { + t.Fatal(err) + } + + // Set & get cooldown + var cooldown uint64 = 1 + if proposalId, _, err := trustednode.ProposeProposalCooldownTime(rp, cooldown, trustedNodeAccount1.GetTransactor()); err != nil { + t.Error(err) + } else if err := daoutils.PassAndExecuteProposal(rp, proposalId, []*accounts.Account{trustedNodeAccount1, trustedNodeAccount2}); err != nil { + t.Error(err) + } else if value, err := trustednode.GetProposalCooldownTime(rp, nil); err != nil { + t.Error(err) + } else if value != cooldown { + t.Error("Incorrect cooldown value") + } + + // Set & get vote time + var voteTime uint64 = 10 + if proposalId, _, err := trustednode.ProposeProposalVoteTime(rp, voteTime, trustedNodeAccount1.GetTransactor()); err != nil { + t.Error(err) + } else if err := daoutils.PassAndExecuteProposal(rp, proposalId, []*accounts.Account{trustedNodeAccount1, trustedNodeAccount2}); err != nil { + t.Error(err) + } else if value, err := trustednode.GetProposalVoteTime(rp, nil); err != nil { + t.Error(err) + } else if value != voteTime { + t.Error("Incorrect vote time value") + } + + // Set & get execute time + var executeTime uint64 = 10 + if proposalId, _, err := trustednode.ProposeProposalExecuteTime(rp, executeTime, trustedNodeAccount1.GetTransactor()); err != nil { + t.Error(err) + } else if err := daoutils.PassAndExecuteProposal(rp, proposalId, []*accounts.Account{trustedNodeAccount1, trustedNodeAccount2}); err != nil { + t.Error(err) + } else if value, err := trustednode.GetProposalExecuteTime(rp, nil); err != nil { + t.Error(err) + } else if value != executeTime { + t.Error("Incorrect execute time value") + } + + // Set & get action time + var actionTime uint64 = 10 + if proposalId, _, err := trustednode.ProposeProposalActionTime(rp, actionTime, trustedNodeAccount1.GetTransactor()); err != nil { + t.Error(err) + } else if err := daoutils.PassAndExecuteProposal(rp, proposalId, []*accounts.Account{trustedNodeAccount1, trustedNodeAccount2}); err != nil { + t.Error(err) + } else if value, err := trustednode.GetProposalActionTime(rp, nil); err != nil { + t.Error(err) + } else if value != actionTime { + t.Error("Incorrect action time value") + } + + // Set & get vote delay time + var voteDelayTime uint64 = 1000 + if proposalId, _, err := trustednode.ProposeProposalVoteDelayTime(rp, voteDelayTime, trustedNodeAccount1.GetTransactor()); err != nil { + t.Error(err) + } else if err := daoutils.PassAndExecuteProposal(rp, proposalId, []*accounts.Account{trustedNodeAccount1, trustedNodeAccount2}); err != nil { + t.Error(err) + } else if value, err := trustednode.GetProposalVoteDelayTime(rp, nil); err != nil { + t.Error(err) + } else if value != voteDelayTime { + t.Error("Incorrect vote delay time value") + } + +} diff --git a/bindings/tests/testutils/accounts/accounts.go b/bindings/tests/testutils/accounts/accounts.go new file mode 100644 index 000000000..8b38afb83 --- /dev/null +++ b/bindings/tests/testutils/accounts/accounts.go @@ -0,0 +1,49 @@ +package accounts + +import ( + "context" + "crypto/ecdsa" + "encoding/hex" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + + "github.com/rocket-pool/smartnode/bindings/tests" +) + +// An account containing a keypair and address +type Account struct { + PrivateKey *ecdsa.PrivateKey + Address common.Address +} + +// Get an account by index +func GetAccount(index uint8) (*Account, error) { + + // Get private key data + privateKeyBytes, err := hex.DecodeString(tests.AccountPrivateKeys[index]) + if err != nil { + return nil, err + } + + // Get private key + privateKey, err := crypto.ToECDSA(privateKeyBytes) + if err != nil { + return nil, err + } + + // Return account + return &Account{ + PrivateKey: privateKey, + Address: crypto.PubkeyToAddress(privateKey.PublicKey), + }, nil + +} + +// Get a transactor for an account +func (a *Account) GetTransactor() *bind.TransactOpts { + opts := bind.NewKeyedTransactor(a.PrivateKey) + opts.Context = context.Background() + return opts +} diff --git a/bindings/tests/testutils/auction/auction.go b/bindings/tests/testutils/auction/auction.go new file mode 100644 index 000000000..2a840ae55 --- /dev/null +++ b/bindings/tests/testutils/auction/auction.go @@ -0,0 +1,78 @@ +package auction + +import ( + "fmt" + "testing" + + "github.com/rocket-pool/smartnode/bindings/deposit" + "github.com/rocket-pool/smartnode/bindings/minipool" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/settings/trustednode" + "github.com/rocket-pool/smartnode/bindings/utils/eth" + + "github.com/rocket-pool/smartnode/bindings/tests/testutils/accounts" + "github.com/rocket-pool/smartnode/bindings/tests/testutils/evm" + minipoolutils "github.com/rocket-pool/smartnode/bindings/tests/testutils/minipool" + nodeutils "github.com/rocket-pool/smartnode/bindings/tests/testutils/node" +) + +// Create an amount of slashed RPL in the auction contract +func CreateSlashedRPL(t *testing.T, rp *rocketpool.RocketPool, ownerAccount *accounts.Account, trustedNodeAccount, trustedNodeAccount2 *accounts.Account, userAccount *accounts.Account) error { + + // Stake a large amount of RPL against the node + if err := nodeutils.StakeRPL(rp, ownerAccount, trustedNodeAccount, eth.EthToWei(1000000)); err != nil { + return err + } + + // Make user deposit + depositOpts := userAccount.GetTransactor() + depositOpts.Value = eth.EthToWei(16) + if _, err := deposit.Deposit(rp, depositOpts); err != nil { + return err + } + + // Create unbonded minipool + mp, err := minipoolutils.CreateMinipool(t, rp, ownerAccount, trustedNodeAccount, eth.EthToWei(16), 1) + if err != nil { + return err + } + + // Deposit user ETH to minipool + opts := userAccount.GetTransactor() + opts.Value = eth.EthToWei(16) + if _, err := deposit.Deposit(rp, opts); err != nil { + return err + } + + // Delay for the time between depositing and staking + scrubPeriod, err := trustednode.GetScrubPeriod(rp, nil) + if err != nil { + return err + } + err = evm.IncreaseTime(int(scrubPeriod + 1)) + if err != nil { + return fmt.Errorf("error increasing time: %w", err) + } + + // Stake minipool + if err := minipoolutils.StakeMinipool(rp, mp, trustedNodeAccount); err != nil { + return err + } + + // Mark minipool as withdrawable with zero end balance + if _, err := minipool.SubmitMinipoolWithdrawable(rp, mp.Address, trustedNodeAccount.GetTransactor()); err != nil { + return err + } + if _, err := minipool.SubmitMinipoolWithdrawable(rp, mp.Address, trustedNodeAccount2.GetTransactor()); err != nil { + return err + } + + // Distribute balance and finalise pool to send slashed RPL to auction contract + if _, err := mp.DistributeBalanceAndFinalise(trustedNodeAccount.GetTransactor()); err != nil { + return err + } + + // Return + return nil + +} diff --git a/bindings/tests/testutils/dao/proposals.go b/bindings/tests/testutils/dao/proposals.go new file mode 100644 index 000000000..c19515fce --- /dev/null +++ b/bindings/tests/testutils/dao/proposals.go @@ -0,0 +1,48 @@ +package dao + +import ( + "github.com/rocket-pool/smartnode/bindings/dao" + trustednodedao "github.com/rocket-pool/smartnode/bindings/dao/trustednode" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + trustednodesettings "github.com/rocket-pool/smartnode/bindings/settings/trustednode" + rptypes "github.com/rocket-pool/smartnode/bindings/types" + + "github.com/rocket-pool/smartnode/bindings/tests/testutils/accounts" + "github.com/rocket-pool/smartnode/bindings/tests/testutils/evm" +) + +// Pass and execute a proposal +func PassAndExecuteProposal(rp *rocketpool.RocketPool, proposalId uint64, trustedNodeAccounts []*accounts.Account) error { + + // Get proposal voting delay + voteDelayTime, err := trustednodesettings.GetProposalVoteDelayTime(rp, nil) + if err != nil { + return err + } + + // Increase time until proposal voting delay has passed + if err := evm.IncreaseTime(int(voteDelayTime)); err != nil { + return err + } + + // Vote on proposal until passed + for _, account := range trustedNodeAccounts { + if state, err := dao.GetProposalState(rp, proposalId, nil); err != nil { + return err + } else if state == rptypes.Succeeded { + break + } + if _, err := trustednodedao.VoteOnProposal(rp, proposalId, true, account.GetTransactor()); err != nil { + return err + } + } + + // Execute proposal + if _, err := trustednodedao.ExecuteProposal(rp, proposalId, trustedNodeAccounts[0].GetTransactor()); err != nil { + return err + } + + // Return + return nil + +} diff --git a/bindings/tests/testutils/evm/mining.go b/bindings/tests/testutils/evm/mining.go new file mode 100644 index 000000000..137c21bb5 --- /dev/null +++ b/bindings/tests/testutils/evm/mining.go @@ -0,0 +1,50 @@ +package evm + +import ( + "github.com/ethereum/go-ethereum/rpc" + + "github.com/rocket-pool/smartnode/bindings/tests" +) + +// Mine a number of blocks +func MineBlocks(numBlocks int) error { + + // Initialize RPC client + client, err := rpc.Dial(tests.Eth1ProviderAddress) + if err != nil { + return err + } + + // Make RPC calls + for bi := 0; bi < numBlocks; bi++ { + if err := client.Call(nil, "evm_mine"); err != nil { + return err + } + } + + // Return + return nil + +} + +// Fast forward to some number of seconds +func IncreaseTime(time int) error { + + // Initialize RPC client + client, err := rpc.Dial(tests.Eth1ProviderAddress) + if err != nil { + return err + } + + // Make RPC calls + if err := client.Call(nil, "evm_increaseTime", time); err != nil { + return err + } + if err := MineBlocks(1); err != nil { + return err + } + + // Return + return nil + +} diff --git a/bindings/tests/testutils/evm/snapshots.go b/bindings/tests/testutils/evm/snapshots.go new file mode 100644 index 000000000..1beb3e81f --- /dev/null +++ b/bindings/tests/testutils/evm/snapshots.go @@ -0,0 +1,45 @@ +package evm + +import ( + "github.com/ethereum/go-ethereum/rpc" + + "github.com/rocket-pool/smartnode/bindings/tests" +) + +// The ID of the current snapshot of the EVM state +var snapshotId string + +// Take a snapshot of the EVM state +func TakeSnapshot() error { + + // Initialize RPC client + client, err := rpc.Dial(tests.Eth1ProviderAddress) + if err != nil { + return err + } + + // Make RPC call + var response string + if err := client.Call(&response, "evm_snapshot"); err != nil { + return err + } + + // Set snapshot ID & return + snapshotId = response + return nil + +} + +// Restore a snapshot of the EVM state +func RevertSnapshot() error { + + // Initialize RPC client + client, err := rpc.Dial(tests.Eth1ProviderAddress) + if err != nil { + return err + } + + // Make RPC call & return + return client.Call(nil, "evm_revert", snapshotId) + +} diff --git a/bindings/tests/testutils/minipool/minipool.go b/bindings/tests/testutils/minipool/minipool.go new file mode 100644 index 000000000..31253f203 --- /dev/null +++ b/bindings/tests/testutils/minipool/minipool.go @@ -0,0 +1,121 @@ +package minipool + +import ( + "errors" + "fmt" + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + + "github.com/rocket-pool/smartnode/bindings/minipool" + "github.com/rocket-pool/smartnode/bindings/network" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/settings/protocol" + "github.com/rocket-pool/smartnode/bindings/utils/eth" + + "github.com/rocket-pool/smartnode/bindings/tests/testutils/accounts" + nodeutils "github.com/rocket-pool/smartnode/bindings/tests/testutils/node" + "github.com/rocket-pool/smartnode/bindings/tests/testutils/validator" +) + +// Minipool created event +type minipoolCreated struct { + Minipool common.Address + Node common.Address + Time *big.Int +} + +// Create a minipool +func CreateMinipool(t *testing.T, rp *rocketpool.RocketPool, ownerAccount, nodeAccount *accounts.Account, depositAmount *big.Int, pubkey int) (*minipool.Minipool, error) { + + // Mint & stake RPL required for mininpool + rplRequired, err := GetMinipoolRPLRequired(rp) + if err != nil { + return nil, err + } + if err := nodeutils.StakeRPL(rp, ownerAccount, nodeAccount, rplRequired); err != nil { + return nil, err + } + + // Do the node deposit to generate the minipool + expectedMinipoolAddress, txReceipt, err := nodeutils.Deposit(t, rp, nodeAccount, depositAmount, pubkey) + if err != nil { + return nil, fmt.Errorf("error doing node deposit: %w", err) + } + + // Get minipool manager contract + rocketMinipoolManager, err := rp.GetContract("rocketMinipoolManager") + if err != nil { + return nil, err + } + + // Get created minipool address + minipoolCreatedEvents, err := rocketMinipoolManager.GetTransactionEvents(txReceipt, "MinipoolCreated", minipoolCreated{}) + if err != nil || len(minipoolCreatedEvents) == 0 { + return nil, errors.New("error getting minipool created event") + } + minipoolAddress := minipoolCreatedEvents[0].(minipoolCreated).Minipool + + // Sanity check to verify the created minipool is at the expected address + if expectedMinipoolAddress != minipoolAddress { + return nil, errors.New(fmt.Sprintf("Expected minipool address %s but got %s", expectedMinipoolAddress.Hex(), minipoolAddress.Hex())) + } + + // Return minipool instance + return minipool.NewMinipool(rp, minipoolAddress) + +} + +// Stake a minipool +func StakeMinipool(rp *rocketpool.RocketPool, mp *minipool.Minipool, nodeAccount *accounts.Account) error { + + // Get validator & deposit data + validatorPubkey, err := validator.GetValidatorPubkey(1) + if err != nil { + return err + } + withdrawalCredentials, err := minipool.GetMinipoolWithdrawalCredentials(rp, mp.Address, nil) + if err != nil { + return err + } + validatorSignature, err := validator.GetValidatorSignature(1) + if err != nil { + return err + } + depositDataRoot, err := validator.GetDepositDataRoot(validatorPubkey, withdrawalCredentials, validatorSignature) + if err != nil { + return err + } + + // Stake minipool & return + _, err = mp.Stake(validatorSignature, depositDataRoot, nodeAccount.GetTransactor()) + return err + +} + +// Get the RPL required per minipool +func GetMinipoolRPLRequired(rp *rocketpool.RocketPool) (*big.Int, error) { + + // Get data + depositUserAmount, err := protocol.GetMinipoolHalfDepositUserAmount(rp, nil) + if err != nil { + return nil, err + } + minimumPerMinipoolStake, err := protocol.GetMinimumPerMinipoolStake(rp, nil) + if err != nil { + return nil, err + } + rplPrice, err := network.GetRPLPrice(rp, nil) + if err != nil { + return nil, err + } + + // Calculate and return RPL required + var tmp big.Int + var rplRequired big.Int + tmp.Mul(depositUserAmount, eth.EthToWei(minimumPerMinipoolStake)) + rplRequired.Quo(&tmp, rplPrice) + return &rplRequired, nil + +} diff --git a/bindings/tests/testutils/node/deposit.go b/bindings/tests/testutils/node/deposit.go new file mode 100644 index 000000000..dbd608b87 --- /dev/null +++ b/bindings/tests/testutils/node/deposit.go @@ -0,0 +1,77 @@ +package node + +import ( + "fmt" + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + + "github.com/rocket-pool/smartnode/bindings/minipool" + "github.com/rocket-pool/smartnode/bindings/node" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/utils" + + "github.com/rocket-pool/smartnode/bindings/tests/testutils/accounts" + "github.com/rocket-pool/smartnode/bindings/tests/testutils/validator" +) + +var salt int64 = 0 + +// Returns a unique salt for minipool address generation +func GetSalt() *big.Int { + salt += 1 + return big.NewInt(salt) +} + +// Call deposit on the node using the validator test values +func Deposit(t *testing.T, rp *rocketpool.RocketPool, nodeAccount *accounts.Account, depositAmount *big.Int, pubkey int) (common.Address, *types.Receipt, error) { + + // Get the next salt + salt := GetSalt() + + // Get validator & deposit data + depositType, err := node.GetDepositType(rp, depositAmount, nil) + if err != nil { + return common.Address{}, nil, fmt.Errorf("Error getting deposit type: %w", err) + } + validatorPubkey, err := validator.GetValidatorPubkey(pubkey) + if err != nil { + return common.Address{}, nil, fmt.Errorf("Error getting validator pubkey: %w", err) + } + expectedMinipoolAddress, err := utils.GenerateAddress(rp, nodeAccount.Address, depositType, salt, nil) + if err != nil { + return common.Address{}, nil, fmt.Errorf("Error generating minipool address: %w", err) + } + withdrawalCredentials, err := minipool.GetMinipoolWithdrawalCredentials(rp, expectedMinipoolAddress, nil) + if err != nil { + return common.Address{}, nil, fmt.Errorf("Error getting minipool withdrawal credentials: %w", err) + } + validatorSignature, err := validator.GetValidatorSignature(pubkey) + if err != nil { + return common.Address{}, nil, fmt.Errorf("Error getting validator signature: %w", err) + } + depositDataRoot, err := validator.GetDepositDataRoot(validatorPubkey, withdrawalCredentials, validatorSignature) + if err != nil { + return common.Address{}, nil, fmt.Errorf("Error getting deposit data root: %w", err) + } + + // Make node deposit + opts := nodeAccount.GetTransactor() + opts.Value = depositAmount + + minNodeFee := 0.0 + //t.Logf("Deposit:\n\tMin Node Fee: %f\n\tValidator Pubkey: %s\n\tValidator Signature: %s\n\tDeposit Data Root: %s\n\tNode Address: %s\n\tSalt: %s\n\tExpected Minipool: %s\n", + // minNodeFee, validatorPubkey.Hex(), validatorSignature.Hex(), depositDataRoot.Hex(), nodeAccount.Address.Hex(), GetDefaultSalt().String(), expectedMinipoolAddress.Hex()) + tx, err := node.Deposit(rp, minNodeFee, validatorPubkey, validatorSignature, depositDataRoot, salt, expectedMinipoolAddress, opts) + if err != nil { + return common.Address{}, nil, fmt.Errorf("Error executing deposit: %w", err) + } + txReceipt, err := utils.WaitForTransaction(rp.Client, tx.Hash()) + if err != nil { + return common.Address{}, nil, fmt.Errorf("Error waiting for deposit transaction: %w", err) + } + + return expectedMinipoolAddress, txReceipt, nil +} diff --git a/bindings/tests/testutils/node/node.go b/bindings/tests/testutils/node/node.go new file mode 100644 index 000000000..4d62a87a6 --- /dev/null +++ b/bindings/tests/testutils/node/node.go @@ -0,0 +1,74 @@ +package node + +import ( + "fmt" + + trustednodedao "github.com/rocket-pool/smartnode/bindings/dao/trustednode" + "github.com/rocket-pool/smartnode/bindings/node" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + trustednodesettings "github.com/rocket-pool/smartnode/bindings/settings/trustednode" + "github.com/rocket-pool/smartnode/bindings/tokens" + + "github.com/rocket-pool/smartnode/bindings/tests/testutils/accounts" + rplutils "github.com/rocket-pool/smartnode/bindings/tests/testutils/tokens/rpl" +) + +// Trusted node counter +var trustedNodeIndex = 0 + +// Register a trusted node +func RegisterTrustedNode(rp *rocketpool.RocketPool, ownerAccount *accounts.Account, trustedNodeAccount *accounts.Account) error { + + // Register node + if _, err := node.RegisterNode(rp, "Australia/Brisbane", trustedNodeAccount.GetTransactor()); err != nil { + return err + } + + // Bootstrap trusted node DAO member + if _, err := trustednodedao.BootstrapMember(rp, fmt.Sprintf("tn%d", trustedNodeIndex), fmt.Sprintf("tn%d@rocketpool.net", trustedNodeIndex), trustedNodeAccount.Address, ownerAccount.GetTransactor()); err != nil { + return err + } + + // Mint trusted node RPL bond + if err := MintTrustedNodeBond(rp, ownerAccount, trustedNodeAccount); err != nil { + return err + } + + // Join trusted node DAO + if _, err := trustednodedao.Join(rp, trustedNodeAccount.GetTransactor()); err != nil { + return err + } + + // Increment trusted node counter & return + trustedNodeIndex++ + return nil + +} + +// Mint trusted node DAO RPL bond to a node account and approve it for spending +func MintTrustedNodeBond(rp *rocketpool.RocketPool, ownerAccount *accounts.Account, trustedNodeAccount *accounts.Account) error { + + // Get RPL bond amount + rplBondAmount, err := trustednodesettings.GetRPLBond(rp, nil) + if err != nil { + return err + } + + // Get RocketDAONodeTrustedActions contract address + rocketDAONodeTrustedActionsAddress, err := rp.GetAddress("rocketDAONodeTrustedActions") + if err != nil { + return err + } + + // Mint RPL to node & allow trusted node DAO contract to spend it + if err := rplutils.MintRPL(rp, ownerAccount, trustedNodeAccount, rplBondAmount); err != nil { + return err + } + if _, err := tokens.ApproveRPL(rp, *rocketDAONodeTrustedActionsAddress, rplBondAmount, trustedNodeAccount.GetTransactor()); err != nil { + return err + } + + // Return + return nil + +} diff --git a/bindings/tests/testutils/node/staking.go b/bindings/tests/testutils/node/staking.go new file mode 100644 index 000000000..3a5fb5c27 --- /dev/null +++ b/bindings/tests/testutils/node/staking.go @@ -0,0 +1,37 @@ +package node + +import ( + "math/big" + + "github.com/rocket-pool/smartnode/bindings/node" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/tokens" + + "github.com/rocket-pool/smartnode/bindings/tests/testutils/accounts" + rplutils "github.com/rocket-pool/smartnode/bindings/tests/testutils/tokens/rpl" +) + +// Mint & stake an amount of RPL against a node +func StakeRPL(rp *rocketpool.RocketPool, ownerAccount, nodeAccount *accounts.Account, amount *big.Int) error { + + // Get RocketNodeStaking contract address + rocketNodeStakingAddress, err := rp.GetAddress("rocketNodeStaking") + if err != nil { + return err + } + + // Mint, approve & stake RPL + if err := rplutils.MintRPL(rp, ownerAccount, nodeAccount, amount); err != nil { + return err + } + if _, err := tokens.ApproveRPL(rp, *rocketNodeStakingAddress, amount, nodeAccount.GetTransactor()); err != nil { + return err + } + if _, err := node.StakeRPL(rp, amount, nodeAccount.GetTransactor()); err != nil { + return err + } + + // Return + return nil + +} diff --git a/bindings/tests/testutils/tokens/reth/reth.go b/bindings/tests/testutils/tokens/reth/reth.go new file mode 100644 index 000000000..f955d9061 --- /dev/null +++ b/bindings/tests/testutils/tokens/reth/reth.go @@ -0,0 +1,28 @@ +package tokens + +import ( + "math/big" + + "github.com/rocket-pool/smartnode/bindings/deposit" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/tokens" + + "github.com/rocket-pool/smartnode/bindings/tests/testutils/accounts" +) + +// Mint an amount of rETH to an account +func MintRETH(rp *rocketpool.RocketPool, toAccount *accounts.Account, amount *big.Int) error { + + // Get ETH value of amount + ethValue, err := tokens.GetETHValueOfRETH(rp, amount, nil) + if err != nil { + return err + } + + // Deposit from account to mint rETH + opts := toAccount.GetTransactor() + opts.Value = ethValue + _, err = deposit.Deposit(rp, opts) + return err + +} diff --git a/bindings/tests/testutils/tokens/rpl/rpl.go b/bindings/tests/testutils/tokens/rpl/rpl.go new file mode 100644 index 000000000..0c8d28859 --- /dev/null +++ b/bindings/tests/testutils/tokens/rpl/rpl.go @@ -0,0 +1,48 @@ +package rpl + +import ( + "fmt" + "math/big" + + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/tokens" + + "github.com/rocket-pool/smartnode/bindings/tests/testutils/accounts" +) + +// Mint an amount of RPL to an account +func MintRPL(rp *rocketpool.RocketPool, ownerAccount *accounts.Account, toAccount *accounts.Account, amount *big.Int) error { + + // Get RPL token contract address + rocketTokenRPLAddress, err := rp.GetAddress("rocketTokenRPL") + if err != nil { + return err + } + + // Mint, approve & swap fixed-supply RPL + if err := MintFixedSupplyRPL(rp, ownerAccount, toAccount, amount); err != nil { + return err + } + if _, err := tokens.ApproveFixedSupplyRPL(rp, *rocketTokenRPLAddress, amount, toAccount.GetTransactor()); err != nil { + return err + } + if _, err := tokens.SwapFixedSupplyRPLForRPL(rp, amount, toAccount.GetTransactor()); err != nil { + return err + } + + // Return + return nil + +} + +// Mint an amount of fixed-supply RPL to an account +func MintFixedSupplyRPL(rp *rocketpool.RocketPool, ownerAccount *accounts.Account, toAccount *accounts.Account, amount *big.Int) error { + rocketTokenFixedSupplyRPL, err := rp.GetContract("rocketTokenRPLFixedSupply") + if err != nil { + return err + } + if _, err := rocketTokenFixedSupplyRPL.Transact(ownerAccount.GetTransactor(), "mint", toAccount.Address, amount); err != nil { + return fmt.Errorf("error minting fixed-supply RPL tokens to %s: %w", toAccount.Address.Hex(), err) + } + return nil +} diff --git a/bindings/tests/testutils/validator/deposit-data.go b/bindings/tests/testutils/validator/deposit-data.go new file mode 100644 index 000000000..aaf2e03e6 --- /dev/null +++ b/bindings/tests/testutils/validator/deposit-data.go @@ -0,0 +1,59 @@ +package validator + +import ( + "fmt" + + "github.com/ethereum/go-ethereum/common" + "github.com/prysmaticlabs/go-ssz" + + "github.com/rocket-pool/smartnode/bindings/types" + + "github.com/rocket-pool/smartnode/bindings/tests" +) + +// Deposit settings +const depositAmount = 16000000000 // gwei + +// Deposit data +type depositData struct { + PublicKey []byte `ssz-size:"48"` + WithdrawalCredentials []byte `ssz-size:"32"` + Amount uint64 + Signature []byte `ssz-size:"96"` +} + +// Get the validator pubkey +func GetValidatorPubkey(pubkey int) (types.ValidatorPubkey, error) { + if pubkey == 1 { + return types.HexToValidatorPubkey(tests.ValidatorPubkey) + } else if pubkey == 2 { + return types.HexToValidatorPubkey(tests.ValidatorPubkey2) + } else if pubkey == 3 { + return types.HexToValidatorPubkey(tests.ValidatorPubkey3) + } else { + return types.ValidatorPubkey{}, fmt.Errorf("Invalid pubkey index %d", pubkey) + } +} + +// Get the validator deposit signature +func GetValidatorSignature(pubkey int) (types.ValidatorSignature, error) { + if pubkey == 1 { + return types.HexToValidatorSignature(tests.ValidatorSignature) + } else if pubkey == 2 { + return types.HexToValidatorSignature(tests.ValidatorSignature2) + } else if pubkey == 3 { + return types.HexToValidatorSignature(tests.ValidatorSignature3) + } else { + return types.ValidatorSignature{}, fmt.Errorf("Invalid pubkey index %d", pubkey) + } +} + +// Get the validator deposit depositDataRoot +func GetDepositDataRoot(validatorPubkey types.ValidatorPubkey, withdrawalCredentials common.Hash, validatorSignature types.ValidatorSignature) (common.Hash, error) { + return ssz.HashTreeRoot(depositData{ + PublicKey: validatorPubkey.Bytes(), + WithdrawalCredentials: withdrawalCredentials[:], + Amount: depositAmount, + Signature: validatorSignature.Bytes(), + }) +} diff --git a/bindings/tests/tokens/main_test.go b/bindings/tests/tokens/main_test.go new file mode 100644 index 000000000..afabedf65 --- /dev/null +++ b/bindings/tests/tokens/main_test.go @@ -0,0 +1,68 @@ +package tokens + +import ( + "log" + "os" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethclient" + + "github.com/rocket-pool/smartnode/bindings/rocketpool" + + "github.com/rocket-pool/smartnode/bindings/tests" + "github.com/rocket-pool/smartnode/bindings/tests/testutils/accounts" +) + +var ( + client *ethclient.Client + rp *rocketpool.RocketPool + + ownerAccount *accounts.Account + trustedNodeAccount *accounts.Account + userAccount1 *accounts.Account + userAccount2 *accounts.Account + swcAccount *accounts.Account +) + +func TestMain(m *testing.M) { + var err error + + // Initialize eth client + client, err = ethclient.Dial(tests.Eth1ProviderAddress) + if err != nil { + log.Fatal(err) + } + + // Initialize contract manager + rp, err = rocketpool.NewRocketPool(client, common.HexToAddress(tests.RocketStorageAddress)) + if err != nil { + log.Fatal(err) + } + + // Initialize accounts + ownerAccount, err = accounts.GetAccount(0) + if err != nil { + log.Fatal(err) + } + trustedNodeAccount, err = accounts.GetAccount(1) + if err != nil { + log.Fatal(err) + } + userAccount1, err = accounts.GetAccount(7) + if err != nil { + log.Fatal(err) + } + userAccount2, err = accounts.GetAccount(8) + if err != nil { + log.Fatal(err) + } + swcAccount, err = accounts.GetAccount(9) + if err != nil { + log.Fatal(err) + } + + // Run tests + os.Exit(m.Run()) + +} diff --git a/bindings/tests/tokens/reth_test.go b/bindings/tests/tokens/reth_test.go new file mode 100644 index 000000000..2495b21d1 --- /dev/null +++ b/bindings/tests/tokens/reth_test.go @@ -0,0 +1,240 @@ +package tokens + +import ( + "testing" + + "github.com/ethereum/go-ethereum/common" + + "github.com/rocket-pool/smartnode/bindings/network" + "github.com/rocket-pool/smartnode/bindings/tokens" + "github.com/rocket-pool/smartnode/bindings/utils/eth" + + "github.com/rocket-pool/smartnode/bindings/tests/testutils/evm" + nodeutils "github.com/rocket-pool/smartnode/bindings/tests/testutils/node" + rethutils "github.com/rocket-pool/smartnode/bindings/tests/testutils/tokens/reth" +) + +// GetRETHContractETHBalance test under minipool.TestWithdrawValidatorBalance +// GetRETHTotalCollateral test under minipool.TestWithdrawValidatorBalance +// GetRETHCollateralRate test under minipool.TestWithdrawValidatorBalance + +func TestRETHBalances(t *testing.T) { + + // State snapshotting + if err := evm.TakeSnapshot(); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := evm.RevertSnapshot(); err != nil { + t.Fatal(err) + } + }) + + // Mint rETH + rethAmount := eth.EthToWei(100) + if err := rethutils.MintRETH(rp, userAccount1, rethAmount); err != nil { + t.Fatal(err) + } + + // Get & check rETH total supply + if rethTotalSupply, err := tokens.GetRETHTotalSupply(rp, nil); err != nil { + t.Error(err) + } else if rethTotalSupply.Cmp(rethAmount) != 0 { + t.Errorf("Incorrect rETH total supply %s", rethTotalSupply.String()) + } + + // Get & check rETH account balance + if rethBalance, err := tokens.GetRETHBalance(rp, userAccount1.Address, nil); err != nil { + t.Error(err) + } else if rethBalance.Cmp(rethAmount) != 0 { + t.Errorf("Incorrect rETH account balance %s", rethBalance.String()) + } + +} + +func TestTransferRETH(t *testing.T) { + + // State snapshotting + if err := evm.TakeSnapshot(); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := evm.RevertSnapshot(); err != nil { + t.Fatal(err) + } + }) + + // Mint rETH + rethAmount := eth.EthToWei(100) + if err := rethutils.MintRETH(rp, userAccount1, rethAmount); err != nil { + t.Fatal(err) + } + + // Mine pre-requisite 5760 blocks before being able to transfer + if err := evm.MineBlocks(5760); err != nil { + t.Fatal(err) + } + + // Transfer rETH + toAddress := common.HexToAddress("0x1111111111111111111111111111111111111111") + sendAmount := eth.EthToWei(50) + if _, err := tokens.TransferRETH(rp, toAddress, sendAmount, userAccount1.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Get & check rETH account balance + if rethBalance, err := tokens.GetRETHBalance(rp, toAddress, nil); err != nil { + t.Error(err) + } else if rethBalance.Cmp(sendAmount) != 0 { + t.Errorf("Incorrect rETH account balance %s", rethBalance.String()) + } + +} + +func TestTransferFromRETH(t *testing.T) { + + // State snapshotting + if err := evm.TakeSnapshot(); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := evm.RevertSnapshot(); err != nil { + t.Fatal(err) + } + }) + + // Mint rETH + rethAmount := eth.EthToWei(100) + if err := rethutils.MintRETH(rp, userAccount1, rethAmount); err != nil { + t.Fatal(err) + } + + // Approve rETH spender + sendAmount := eth.EthToWei(50) + if _, err := tokens.ApproveRETH(rp, userAccount2.Address, sendAmount, userAccount1.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Get & check spender allowance + if allowance, err := tokens.GetRETHAllowance(rp, userAccount1.Address, userAccount2.Address, nil); err != nil { + t.Error(err) + } else if allowance.Cmp(sendAmount) != 0 { + t.Errorf("Incorrect rETH spender allowance %s", allowance.String()) + } + + // Mine pre-requisite 5760 blocks before being able to transfer + if err := evm.MineBlocks(5760); err != nil { + t.Fatal(err) + } + + // Transfer rETH from account + toAddress := common.HexToAddress("0x1111111111111111111111111111111111111111") + if _, err := tokens.TransferFromRETH(rp, userAccount1.Address, toAddress, sendAmount, userAccount2.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Get & check rETH account balance + if rethBalance, err := tokens.GetRETHBalance(rp, toAddress, nil); err != nil { + t.Error(err) + } else if rethBalance.Cmp(sendAmount) != 0 { + t.Errorf("Incorrect rETH account balance %s", rethBalance.String()) + } + +} + +func TestRETHExchangeRate(t *testing.T) { + + // State snapshotting + if err := evm.TakeSnapshot(); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := evm.RevertSnapshot(); err != nil { + t.Fatal(err) + } + }) + + // Register trusted node + if err := nodeutils.RegisterTrustedNode(rp, ownerAccount, trustedNodeAccount); err != nil { + t.Fatal(err) + } + + // Submit network balances + if _, err := network.SubmitBalances(rp, 1, eth.EthToWei(100), eth.EthToWei(100), eth.EthToWei(50), trustedNodeAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Get & check ETH value of rETH amount + rethAmount := eth.EthToWei(1) + if ethValue, err := tokens.GetETHValueOfRETH(rp, rethAmount, nil); err != nil { + t.Error(err) + } else if ethValue.Cmp(eth.EthToWei(2)) != 0 { + t.Errorf("Incorrect ETH value %s of rETH amount %s", ethValue.String(), rethAmount.String()) + } + + // Get & check rETH value of ETH amount + ethAmount := eth.EthToWei(2) + if rethValue, err := tokens.GetRETHValueOfETH(rp, ethAmount, nil); err != nil { + t.Error(err) + } else if rethValue.Cmp(eth.EthToWei(1)) != 0 { + t.Errorf("Incorrect rETH value %s of ETH amount %s", rethValue.String(), ethAmount.String()) + } + + // Get & check ETH : rETH exchange rate + if exchangeRate, err := tokens.GetRETHExchangeRate(rp, nil); err != nil { + t.Error(err) + } else if exchangeRate != 2 { + t.Errorf("Incorrect ETH : rETH exchange rate %f : 1", exchangeRate) + } + +} + +func TestBurnRETH(t *testing.T) { + + // State snapshotting + if err := evm.TakeSnapshot(); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := evm.RevertSnapshot(); err != nil { + t.Fatal(err) + } + }) + + // Mint rETH + rethAmount := eth.EthToWei(100) + if err := rethutils.MintRETH(rp, userAccount1, rethAmount); err != nil { + t.Fatal(err) + } + + // Get initial balances + balances1, err := tokens.GetBalances(rp, userAccount1.Address, nil) + if err != nil { + t.Fatal(err) + } + + // Mine pre-requisite 5760 blocks before being able to burn + if err := evm.MineBlocks(5760); err != nil { + t.Fatal(err) + } + + // Burn rETH + burnAmount := eth.EthToWei(50) + if _, err := tokens.BurnRETH(rp, burnAmount, userAccount1.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Get & check updated balances + balances2, err := tokens.GetBalances(rp, userAccount1.Address, nil) + if err != nil { + t.Fatal(err) + } else { + if balances2.RETH.Cmp(balances1.RETH) != -1 { + t.Error("rETH balance did not decrease after burning rETH") + } + if balances2.ETH.Cmp(balances1.ETH) != 1 { + t.Error("ETH balance did not increase after burning rETH") + } + } + +} diff --git a/bindings/tests/tokens/rpl_fixed_test.go b/bindings/tests/tokens/rpl_fixed_test.go new file mode 100644 index 000000000..67e669a87 --- /dev/null +++ b/bindings/tests/tokens/rpl_fixed_test.go @@ -0,0 +1,127 @@ +package tokens + +import ( + "testing" + + "github.com/ethereum/go-ethereum/common" + + "github.com/rocket-pool/smartnode/bindings/tokens" + "github.com/rocket-pool/smartnode/bindings/utils/eth" + + "github.com/rocket-pool/smartnode/bindings/tests/testutils/evm" + rplutils "github.com/rocket-pool/smartnode/bindings/tests/testutils/tokens/rpl" +) + +func TestFixedSupplyRPLBalances(t *testing.T) { + + // State snapshotting + if err := evm.TakeSnapshot(); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := evm.RevertSnapshot(); err != nil { + t.Fatal(err) + } + }) + + // Mint fixed-supply RPL + fixedRplAmount := eth.EthToWei(100) + if err := rplutils.MintFixedSupplyRPL(rp, ownerAccount, userAccount1, fixedRplAmount); err != nil { + t.Fatal(err) + } + + // Get & check fixed-supply RPL total supply + if fixedRplTotalSupply, err := tokens.GetFixedSupplyRPLTotalSupply(rp, nil); err != nil { + t.Error(err) + } else if fixedRplTotalSupply.Cmp(fixedRplAmount) != 0 { + t.Errorf("Incorrect fixed-supply RPL total supply %s", fixedRplTotalSupply.String()) + } + + // Get & check fixed-supply RPL account balance + if fixedRplBalance, err := tokens.GetFixedSupplyRPLBalance(rp, userAccount1.Address, nil); err != nil { + t.Error(err) + } else if fixedRplBalance.Cmp(fixedRplAmount) != 0 { + t.Errorf("Incorrect fixed-supply RPL account balance %s", fixedRplBalance.String()) + } + +} + +func TestTransferFixedSupplyRPL(t *testing.T) { + + // State snapshotting + if err := evm.TakeSnapshot(); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := evm.RevertSnapshot(); err != nil { + t.Fatal(err) + } + }) + + // Mint fixed-supply RPL + fixedRplAmount := eth.EthToWei(100) + if err := rplutils.MintFixedSupplyRPL(rp, ownerAccount, userAccount1, fixedRplAmount); err != nil { + t.Fatal(err) + } + + // Transfer fixed-supply RPL + toAddress := common.HexToAddress("0x1111111111111111111111111111111111111111") + sendAmount := eth.EthToWei(50) + if _, err := tokens.TransferFixedSupplyRPL(rp, toAddress, sendAmount, userAccount1.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Get & check fixed-supply RPL account balance + if fixedRplBalance, err := tokens.GetFixedSupplyRPLBalance(rp, toAddress, nil); err != nil { + t.Error(err) + } else if fixedRplBalance.Cmp(sendAmount) != 0 { + t.Errorf("Incorrect fixed-supply RPL account balance %s", fixedRplBalance.String()) + } + +} + +func TestTransferFromFixedSupplyRPL(t *testing.T) { + + // State snapshotting + if err := evm.TakeSnapshot(); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := evm.RevertSnapshot(); err != nil { + t.Fatal(err) + } + }) + + // Mint fixed-supply RPL + fixedRplAmount := eth.EthToWei(100) + if err := rplutils.MintFixedSupplyRPL(rp, ownerAccount, userAccount1, fixedRplAmount); err != nil { + t.Fatal(err) + } + + // Approve fixed-supply RPL spender + sendAmount := eth.EthToWei(50) + if _, err := tokens.ApproveFixedSupplyRPL(rp, userAccount2.Address, sendAmount, userAccount1.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Get & check spender allowance + if allowance, err := tokens.GetFixedSupplyRPLAllowance(rp, userAccount1.Address, userAccount2.Address, nil); err != nil { + t.Error(err) + } else if allowance.Cmp(sendAmount) != 0 { + t.Errorf("Incorrect fixed-supply RPL spender allowance %s", allowance.String()) + } + + // Transfer fixed-supply RPL from account + toAddress := common.HexToAddress("0x1111111111111111111111111111111111111111") + if _, err := tokens.TransferFromFixedSupplyRPL(rp, userAccount1.Address, toAddress, sendAmount, userAccount2.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Get & check fixed-supply RPL account balance + if fixedRplBalance, err := tokens.GetFixedSupplyRPLBalance(rp, toAddress, nil); err != nil { + t.Error(err) + } else if fixedRplBalance.Cmp(sendAmount) != 0 { + t.Errorf("Incorrect fixed-supply RPL account balance %s", fixedRplBalance.String()) + } + +} diff --git a/bindings/tests/tokens/rpl_test.go b/bindings/tests/tokens/rpl_test.go new file mode 100644 index 000000000..5515a239e --- /dev/null +++ b/bindings/tests/tokens/rpl_test.go @@ -0,0 +1,218 @@ +package tokens + +import ( + "testing" + "time" + + "github.com/ethereum/go-ethereum/common" + + "github.com/rocket-pool/smartnode/bindings/settings/protocol" + "github.com/rocket-pool/smartnode/bindings/tokens" + "github.com/rocket-pool/smartnode/bindings/utils/eth" + + "github.com/rocket-pool/smartnode/bindings/tests/testutils/evm" + rplutils "github.com/rocket-pool/smartnode/bindings/tests/testutils/tokens/rpl" +) + +func TestRPLBalances(t *testing.T) { + + // State snapshotting + if err := evm.TakeSnapshot(); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := evm.RevertSnapshot(); err != nil { + t.Fatal(err) + } + }) + + // Mint RPL + rplAmount := eth.EthToWei(100) + if err := rplutils.MintRPL(rp, ownerAccount, userAccount1, rplAmount); err != nil { + t.Fatal(err) + } + + // Get & check RPL account balance + if rplBalance, err := tokens.GetRPLBalance(rp, userAccount1.Address, nil); err != nil { + t.Error(err) + } else if rplBalance.Cmp(rplAmount) != 0 { + t.Errorf("Incorrect RPL account balance %s", rplBalance.String()) + } + + // Get & check RPL total supply + initialTotalSupply := eth.EthToWei(18000000) + if rplTotalSupply, err := tokens.GetRPLTotalSupply(rp, nil); err != nil { + t.Error(err) + } else if rplTotalSupply.Cmp(initialTotalSupply) != 0 { + t.Errorf("Incorrect RPL total supply %s", rplTotalSupply.String()) + } + +} + +func TestTransferRPL(t *testing.T) { + + // State snapshotting + if err := evm.TakeSnapshot(); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := evm.RevertSnapshot(); err != nil { + t.Fatal(err) + } + }) + + // Mint RPL + rplAmount := eth.EthToWei(100) + if err := rplutils.MintRPL(rp, ownerAccount, userAccount1, rplAmount); err != nil { + t.Fatal(err) + } + + // Transfer RPL + toAddress := common.HexToAddress("0x1111111111111111111111111111111111111111") + sendAmount := eth.EthToWei(50) + if _, err := tokens.TransferRPL(rp, toAddress, sendAmount, userAccount1.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Get & check RPL account balance + if rplBalance, err := tokens.GetRPLBalance(rp, toAddress, nil); err != nil { + t.Error(err) + } else if rplBalance.Cmp(sendAmount) != 0 { + t.Errorf("Incorrect RPL account balance %s", rplBalance.String()) + } + +} + +func TestTransferFromRPL(t *testing.T) { + + // State snapshotting + if err := evm.TakeSnapshot(); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := evm.RevertSnapshot(); err != nil { + t.Fatal(err) + } + }) + + // Mint RPL + rplAmount := eth.EthToWei(100) + if err := rplutils.MintRPL(rp, ownerAccount, userAccount1, rplAmount); err != nil { + t.Fatal(err) + } + + // Approve RPL spender + sendAmount := eth.EthToWei(50) + if _, err := tokens.ApproveRPL(rp, userAccount2.Address, sendAmount, userAccount1.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Get & check spender allowance + if allowance, err := tokens.GetRPLAllowance(rp, userAccount1.Address, userAccount2.Address, nil); err != nil { + t.Error(err) + } else if allowance.Cmp(sendAmount) != 0 { + t.Errorf("Incorrect RPL spender allowance %s", allowance.String()) + } + + // Transfer RPL from account + toAddress := common.HexToAddress("0x1111111111111111111111111111111111111111") + if _, err := tokens.TransferFromRPL(rp, userAccount1.Address, toAddress, sendAmount, userAccount2.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Get & check RPL account balance + if rplBalance, err := tokens.GetRPLBalance(rp, toAddress, nil); err != nil { + t.Error(err) + } else if rplBalance.Cmp(sendAmount) != 0 { + t.Errorf("Incorrect RPL account balance %s", rplBalance.String()) + } + +} + +func TestMintInflationRPL(t *testing.T) { + + // State snapshotting + if err := evm.TakeSnapshot(); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := evm.RevertSnapshot(); err != nil { + t.Fatal(err) + } + }) + + // Constants + oneDay := 24 * 60 * 60 + + // Start RPL inflation + if _, err := protocol.BootstrapInflationStartTime(rp, uint64(time.Now().Unix()+3600), ownerAccount.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Increase time until rewards are available + if err := evm.IncreaseTime(3600 + oneDay); err != nil { + t.Fatal(err) + } + + // Get initial total supply + rplTotalSupply1, err := tokens.GetRPLTotalSupply(rp, nil) + if err != nil { + t.Fatal(err) + } + + // Mint RPL from inflation + if _, err := tokens.MintInflationRPL(rp, userAccount1.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Get & check updated total supply + rplTotalSupply2, err := tokens.GetRPLTotalSupply(rp, nil) + if err != nil { + t.Fatal(err) + } + if rplTotalSupply2.Cmp(rplTotalSupply1) != 1 { + t.Errorf("Incorrect updated RPL total supply %s", rplTotalSupply2.String()) + } + +} + +func TestSwapFixedSupplyRPLForRPL(t *testing.T) { + + // State snapshotting + if err := evm.TakeSnapshot(); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := evm.RevertSnapshot(); err != nil { + t.Fatal(err) + } + }) + + // Mint fixed-supply RPL + rplAmount := eth.EthToWei(100) + if err := rplutils.MintFixedSupplyRPL(rp, ownerAccount, userAccount1, rplAmount); err != nil { + t.Fatal(err) + } + + // Approve fixed-supply RPL spend + rocketTokenRPLAddress, err := rp.GetAddress("rocketTokenRPL") + if err != nil { + t.Fatal(err) + } + if _, err := tokens.ApproveFixedSupplyRPL(rp, *rocketTokenRPLAddress, rplAmount, userAccount1.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Swap fixed-supply RP for RPL + if _, err := tokens.SwapFixedSupplyRPLForRPL(rp, rplAmount, userAccount1.GetTransactor()); err != nil { + t.Fatal(err) + } + + // Get & check RPL account balance + if rplBalance, err := tokens.GetRPLBalance(rp, userAccount1.Address, nil); err != nil { + t.Error(err) + } else if rplBalance.Cmp(rplAmount) != 0 { + t.Errorf("Incorrect RPL account balance %s", rplBalance.String()) + } + +} diff --git a/bindings/tests/tokens/tokens_test.go b/bindings/tests/tokens/tokens_test.go new file mode 100644 index 000000000..26d29b105 --- /dev/null +++ b/bindings/tests/tokens/tokens_test.go @@ -0,0 +1,63 @@ +package tokens + +import ( + "math/big" + "testing" + + "github.com/rocket-pool/smartnode/bindings/tokens" + "github.com/rocket-pool/smartnode/bindings/utils/eth" + + "github.com/rocket-pool/smartnode/bindings/tests/testutils/evm" + rethutils "github.com/rocket-pool/smartnode/bindings/tests/testutils/tokens/reth" + rplutils "github.com/rocket-pool/smartnode/bindings/tests/testutils/tokens/rpl" +) + +func TestTokenBalances(t *testing.T) { + + // State snapshotting + if err := evm.TakeSnapshot(); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := evm.RevertSnapshot(); err != nil { + t.Fatal(err) + } + }) + + // Mint rETH + rethAmount := eth.EthToWei(102) + if err := rethutils.MintRETH(rp, userAccount1, rethAmount); err != nil { + t.Fatal(err) + } + + // Mint RPL + rplAmount := eth.EthToWei(103) + if err := rplutils.MintRPL(rp, ownerAccount, userAccount1, rplAmount); err != nil { + t.Fatal(err) + } + + // Mint fixed-supply RPL + fixedRplAmount := eth.EthToWei(104) + if err := rplutils.MintFixedSupplyRPL(rp, ownerAccount, userAccount1, fixedRplAmount); err != nil { + t.Fatal(err) + } + + // Get & check token balances + if balances, err := tokens.GetBalances(rp, userAccount1.Address, nil); err != nil { + t.Error(err) + } else { + if balances.ETH.Cmp(big.NewInt(0)) != 1 { + t.Errorf("Incorrect ETH balance %s", balances.ETH.String()) + } + if balances.RETH.Cmp(rethAmount) != 0 { + t.Errorf("Incorrect rETH balance %s", balances.RETH.String()) + } + if balances.RPL.Cmp(rplAmount) != 0 { + t.Errorf("Incorrect RPL balance %s", balances.RPL.String()) + } + if balances.FixedSupplyRPL.Cmp(fixedRplAmount) != 0 { + t.Errorf("Incorrect fixed-supply RPL balance %s", balances.FixedSupplyRPL.String()) + } + } + +} diff --git a/bindings/tests/utils/eth/transactions_test.go b/bindings/tests/utils/eth/transactions_test.go new file mode 100644 index 000000000..5ae7cb454 --- /dev/null +++ b/bindings/tests/utils/eth/transactions_test.go @@ -0,0 +1,65 @@ +package eth + +import ( + "context" + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethclient" + + "github.com/rocket-pool/smartnode/bindings/utils/eth" + + "github.com/rocket-pool/smartnode/bindings/tests" + "github.com/rocket-pool/smartnode/bindings/tests/testutils/accounts" + "github.com/rocket-pool/smartnode/bindings/tests/testutils/evm" + "github.com/rocket-pool/smartnode/bindings/utils" +) + +func TestSendTransaction(t *testing.T) { + + // State snapshotting + if err := evm.TakeSnapshot(); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := evm.RevertSnapshot(); err != nil { + t.Fatal(err) + } + }) + + // Initialize eth client + client, err := ethclient.Dial(tests.Eth1ProviderAddress) + if err != nil { + t.Fatal(err) + } + + // Initialize accounts + userAccount, err := accounts.GetAccount(9) + if err != nil { + t.Fatal(err) + } + + // Transaction parameters + toAddress := common.HexToAddress("0x1111111111111111111111111111111111111111") + sendAmount := eth.EthToWei(50) + + // Send transaction + opts := userAccount.GetTransactor() + opts.Value = sendAmount + hash, err := eth.SendTransaction(client, toAddress, big.NewInt(1337), opts) // Ganache's default chain ID is 1337 + if err != nil { + t.Fatal(err) + } + if _, err := utils.WaitForTransaction(client, hash); err != nil { + t.Fatal(err) + } + + // Get & check to address balance + if balance, err := client.BalanceAt(context.Background(), toAddress, nil); err != nil { + t.Error(err) + } else if balance.Cmp(sendAmount) != 0 { + t.Errorf("Incorrect to address balance %s", balance.String()) + } + +} diff --git a/bindings/tests/utils/eth/units_test.go b/bindings/tests/utils/eth/units_test.go new file mode 100644 index 000000000..47a76a5c7 --- /dev/null +++ b/bindings/tests/utils/eth/units_test.go @@ -0,0 +1,38 @@ +package eth + +import ( + "math/big" + "testing" + + "github.com/rocket-pool/smartnode/bindings/utils/eth" +) + +func TestConversion(t *testing.T) { + + // Equivalent unit amounts + weiAmount := new(big.Int) + weiAmount.SetString("999999999999999000000", 0) + var gweiAmount float64 = 999999999999.999000000 + var ethAmount float64 = 999.999999999999000000 + + // Convert wei to eth + if toEthAmount := eth.WeiToEth(weiAmount); toEthAmount != ethAmount { + t.Errorf("Incorrect eth amount %f", toEthAmount) + } + + // Convert eth to wei + if toWeiAmount := eth.EthToWei(ethAmount); toWeiAmount.Cmp(weiAmount) != 0 { + t.Errorf("Incorrect wei amount %s", toWeiAmount.String()) + } + + // Convert wei to gigawei + if toGweiAmount := eth.WeiToGwei(weiAmount); toGweiAmount != gweiAmount { + t.Errorf("Incorrect gwei amount %f", toGweiAmount) + } + + // Convert eth to gwei + if toWeiAmount := eth.GweiToWei(gweiAmount); toWeiAmount.Cmp(weiAmount) != 0 { + t.Errorf("Incorrect wei amount %s", toWeiAmount.String()) + } + +} diff --git a/bindings/tests/utils/stage4_bootstrap.go b/bindings/tests/utils/stage4_bootstrap.go new file mode 100644 index 000000000..2f64c415f --- /dev/null +++ b/bindings/tests/utils/stage4_bootstrap.go @@ -0,0 +1,30 @@ +package utils + +import ( + "time" + + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/settings/protocol" + "github.com/rocket-pool/smartnode/bindings/tests/testutils/accounts" + "github.com/rocket-pool/smartnode/bindings/utils/eth" +) + +// Bootstrap all of the parameters to mimic Stage 4 so the unit tests work correctly +func Stage4Bootstrap(rp *rocketpool.RocketPool, ownerAccount *accounts.Account) { + + opts := ownerAccount.GetTransactor() + + protocol.BootstrapDepositEnabled(rp, true, opts) + protocol.BootstrapAssignDepositsEnabled(rp, true, opts) + protocol.BootstrapMaximumDepositPoolSize(rp, eth.EthToWei(1000), opts) + protocol.BootstrapNodeRegistrationEnabled(rp, true, opts) + protocol.BootstrapNodeDepositEnabled(rp, true, opts) + protocol.BootstrapMinipoolSubmitWithdrawableEnabled(rp, true, opts) + protocol.BootstrapMinimumNodeFee(rp, 0.05, opts) + protocol.BootstrapTargetNodeFee(rp, 0.1, opts) + protocol.BootstrapMaximumNodeFee(rp, 0.2, opts) + protocol.BootstrapNodeFeeDemandRange(rp, eth.EthToWei(1000), opts) + protocol.BootstrapInflationStartTime(rp, + uint64(time.Now().Unix()+(60*60*24*14)), opts) + +} diff --git a/bindings/tokens/reth.go b/bindings/tokens/reth.go new file mode 100644 index 000000000..eeafad031 --- /dev/null +++ b/bindings/tokens/reth.go @@ -0,0 +1,211 @@ +package tokens + +import ( + "fmt" + "math/big" + "sync" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/utils/eth" +) + +// +// Core ERC-20 functions +// + +// Get rETH total supply +func GetRETHTotalSupply(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*big.Int, error) { + rocketTokenRETH, err := getRocketTokenRETH(rp, opts) + if err != nil { + return nil, err + } + return totalSupply(rocketTokenRETH, "rETH", opts) +} + +// Get rETH balance +func GetRETHBalance(rp *rocketpool.RocketPool, address common.Address, opts *bind.CallOpts) (*big.Int, error) { + rocketTokenRETH, err := getRocketTokenRETH(rp, opts) + if err != nil { + return nil, err + } + return balanceOf(rocketTokenRETH, "rETH", address, opts) +} + +// Get rETH allowance +func GetRETHAllowance(rp *rocketpool.RocketPool, owner, spender common.Address, opts *bind.CallOpts) (*big.Int, error) { + rocketTokenRETH, err := getRocketTokenRETH(rp, opts) + if err != nil { + return nil, err + } + return allowance(rocketTokenRETH, "rETH", owner, spender, opts) +} + +// Estimate the gas of TransferRETH +func EstimateTransferRETHGas(rp *rocketpool.RocketPool, to common.Address, amount *big.Int, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketTokenRETH, err := getRocketTokenRETH(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return estimateTransferGas(rocketTokenRETH, "rETH", to, amount, opts) +} + +// Transfer rETH +func TransferRETH(rp *rocketpool.RocketPool, to common.Address, amount *big.Int, opts *bind.TransactOpts) (common.Hash, error) { + rocketTokenRETH, err := getRocketTokenRETH(rp, nil) + if err != nil { + return common.Hash{}, err + } + return transfer(rocketTokenRETH, "rETH", to, amount, opts) +} + +// Estimate the gas of ApproveRETH +func EstimateApproveRETHGas(rp *rocketpool.RocketPool, spender common.Address, amount *big.Int, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketTokenRETH, err := getRocketTokenRETH(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return estimateApproveGas(rocketTokenRETH, "rETH", spender, amount, opts) +} + +// Approve a rETH spender +func ApproveRETH(rp *rocketpool.RocketPool, spender common.Address, amount *big.Int, opts *bind.TransactOpts) (common.Hash, error) { + rocketTokenRETH, err := getRocketTokenRETH(rp, nil) + if err != nil { + return common.Hash{}, err + } + return approve(rocketTokenRETH, "rETH", spender, amount, opts) +} + +// Estimate the gas of TransferFromRETH +func EstimateTransferFromRETHGas(rp *rocketpool.RocketPool, from, to common.Address, amount *big.Int, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketTokenRETH, err := getRocketTokenRETH(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return estimateTransferFromGas(rocketTokenRETH, "rETH", from, to, amount, opts) +} + +// Transfer rETH from a sender +func TransferFromRETH(rp *rocketpool.RocketPool, from, to common.Address, amount *big.Int, opts *bind.TransactOpts) (common.Hash, error) { + rocketTokenRETH, err := getRocketTokenRETH(rp, nil) + if err != nil { + return common.Hash{}, err + } + return transferFrom(rocketTokenRETH, "rETH", from, to, amount, opts) +} + +// +// rETH functions +// + +// Get the rETH contract ETH balance +func GetRETHContractETHBalance(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*big.Int, error) { + rocketTokenRETH, err := getRocketTokenRETH(rp, opts) + if err != nil { + return nil, err + } + return contractETHBalance(rp, rocketTokenRETH, opts) +} + +// Get the ETH value of an amount of rETH +func GetETHValueOfRETH(rp *rocketpool.RocketPool, rethAmount *big.Int, opts *bind.CallOpts) (*big.Int, error) { + rocketTokenRETH, err := getRocketTokenRETH(rp, opts) + if err != nil { + return nil, err + } + ethValue := new(*big.Int) + if err := rocketTokenRETH.Call(opts, ethValue, "getEthValue", rethAmount); err != nil { + return nil, fmt.Errorf("error getting ETH value of rETH amount: %w", err) + } + return *ethValue, nil +} + +// Get the rETH value of an amount of ETH +func GetRETHValueOfETH(rp *rocketpool.RocketPool, ethAmount *big.Int, opts *bind.CallOpts) (*big.Int, error) { + rocketTokenRETH, err := getRocketTokenRETH(rp, opts) + if err != nil { + return nil, err + } + rethValue := new(*big.Int) + if err := rocketTokenRETH.Call(opts, rethValue, "getRethValue", ethAmount); err != nil { + return nil, fmt.Errorf("error getting rETH value of ETH amount: %w", err) + } + return *rethValue, nil +} + +// Get the current ETH : rETH exchange rate +func GetRETHExchangeRate(rp *rocketpool.RocketPool, opts *bind.CallOpts) (float64, error) { + rocketTokenRETH, err := getRocketTokenRETH(rp, opts) + if err != nil { + return 0, err + } + exchangeRate := new(*big.Int) + if err := rocketTokenRETH.Call(opts, exchangeRate, "getExchangeRate"); err != nil { + return 0, fmt.Errorf("error getting rETH exchange rate: %w", err) + } + return eth.WeiToEth(*exchangeRate), nil +} + +// Get the total amount of ETH collateral available for rETH trades +func GetRETHTotalCollateral(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*big.Int, error) { + rocketTokenRETH, err := getRocketTokenRETH(rp, opts) + if err != nil { + return nil, err + } + totalCollateral := new(*big.Int) + if err := rocketTokenRETH.Call(opts, totalCollateral, "getTotalCollateral"); err != nil { + return nil, fmt.Errorf("error getting rETH total collateral: %w", err) + } + return *totalCollateral, nil +} + +// Get the rETH collateralization rate +func GetRETHCollateralRate(rp *rocketpool.RocketPool, opts *bind.CallOpts) (float64, error) { + rocketTokenRETH, err := getRocketTokenRETH(rp, opts) + if err != nil { + return 0, err + } + collateralRate := new(*big.Int) + if err := rocketTokenRETH.Call(opts, collateralRate, "getCollateralRate"); err != nil { + return 0, fmt.Errorf("error getting rETH collateral rate: %w", err) + } + return eth.WeiToEth(*collateralRate), nil +} + +// Estimate the gas of BurnRETH +func EstimateBurnRETHGas(rp *rocketpool.RocketPool, amount *big.Int, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketTokenRETH, err := getRocketTokenRETH(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketTokenRETH.GetTransactionGasInfo(opts, "burn", amount) +} + +// Burn rETH for ETH +func BurnRETH(rp *rocketpool.RocketPool, amount *big.Int, opts *bind.TransactOpts) (common.Hash, error) { + rocketTokenRETH, err := getRocketTokenRETH(rp, nil) + if err != nil { + return common.Hash{}, err + } + tx, err := rocketTokenRETH.Transact(opts, "burn", amount) + if err != nil { + return common.Hash{}, fmt.Errorf("error burning rETH: %w", err) + } + return tx.Hash(), nil +} + +// +// Contracts +// + +// Get contracts +var rocketTokenRETHLock sync.Mutex + +func getRocketTokenRETH(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*rocketpool.Contract, error) { + rocketTokenRETHLock.Lock() + defer rocketTokenRETHLock.Unlock() + return rp.GetContract("rocketTokenRETH", opts) +} diff --git a/bindings/tokens/rpl-fixed.go b/bindings/tokens/rpl-fixed.go new file mode 100644 index 000000000..6c7776e39 --- /dev/null +++ b/bindings/tokens/rpl-fixed.go @@ -0,0 +1,109 @@ +package tokens + +import ( + "math/big" + "sync" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + + "github.com/rocket-pool/smartnode/bindings/rocketpool" +) + +// +// Core ERC-20 functions +// + +// Get fixed-supply RPL total supply +func GetFixedSupplyRPLTotalSupply(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*big.Int, error) { + rocketTokenFixedSupplyRPL, err := getRocketTokenRPLFixedSupply(rp, opts) + if err != nil { + return nil, err + } + return totalSupply(rocketTokenFixedSupplyRPL, "fixed-supply RPL", opts) +} + +// Get fixed-supply RPL balance +func GetFixedSupplyRPLBalance(rp *rocketpool.RocketPool, address common.Address, opts *bind.CallOpts) (*big.Int, error) { + rocketTokenFixedSupplyRPL, err := getRocketTokenRPLFixedSupply(rp, opts) + if err != nil { + return nil, err + } + return balanceOf(rocketTokenFixedSupplyRPL, "fixed-supply RPL", address, opts) +} + +// Get fixed-supply RPL allowance +func GetFixedSupplyRPLAllowance(rp *rocketpool.RocketPool, owner, spender common.Address, opts *bind.CallOpts) (*big.Int, error) { + rocketTokenFixedSupplyRPL, err := getRocketTokenRPLFixedSupply(rp, opts) + if err != nil { + return nil, err + } + return allowance(rocketTokenFixedSupplyRPL, "fixed-supply RPL", owner, spender, opts) +} + +// Estimate the gas of TransferFixedSupplyRPL +func EstimateTransferFixedSupplyRPLGas(rp *rocketpool.RocketPool, to common.Address, amount *big.Int, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketTokenFixedSupplyRPL, err := getRocketTokenRPLFixedSupply(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return estimateTransferGas(rocketTokenFixedSupplyRPL, "fixed-supply RPL", to, amount, opts) +} + +// Transfer fixed-supply RPL +func TransferFixedSupplyRPL(rp *rocketpool.RocketPool, to common.Address, amount *big.Int, opts *bind.TransactOpts) (common.Hash, error) { + rocketTokenFixedSupplyRPL, err := getRocketTokenRPLFixedSupply(rp, nil) + if err != nil { + return common.Hash{}, err + } + return transfer(rocketTokenFixedSupplyRPL, "fixed-supply RPL", to, amount, opts) +} + +// Estimate the gas of ApproveFixedSupplyRPL +func EstimateApproveFixedSupplyRPLGas(rp *rocketpool.RocketPool, spender common.Address, amount *big.Int, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketTokenFixedSupplyRPL, err := getRocketTokenRPLFixedSupply(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return estimateApproveGas(rocketTokenFixedSupplyRPL, "fixed-supply RPL", spender, amount, opts) +} + +// Approve an fixed-supply RPL spender +func ApproveFixedSupplyRPL(rp *rocketpool.RocketPool, spender common.Address, amount *big.Int, opts *bind.TransactOpts) (common.Hash, error) { + rocketTokenFixedSupplyRPL, err := getRocketTokenRPLFixedSupply(rp, nil) + if err != nil { + return common.Hash{}, err + } + return approve(rocketTokenFixedSupplyRPL, "fixed-supply RPL", spender, amount, opts) +} + +// Estimate the gas of TransferFromFixedSupplyRPL +func EstimateTransferFromFixedSupplyRPLGas(rp *rocketpool.RocketPool, from, to common.Address, amount *big.Int, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketTokenFixedSupplyRPL, err := getRocketTokenRPLFixedSupply(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return estimateTransferFromGas(rocketTokenFixedSupplyRPL, "fixed-supply RPL", from, to, amount, opts) +} + +// Transfer fixed-supply RPL from a sender +func TransferFromFixedSupplyRPL(rp *rocketpool.RocketPool, from, to common.Address, amount *big.Int, opts *bind.TransactOpts) (common.Hash, error) { + rocketTokenFixedSupplyRPL, err := getRocketTokenRPLFixedSupply(rp, nil) + if err != nil { + return common.Hash{}, err + } + return transferFrom(rocketTokenFixedSupplyRPL, "fixed-supply RPL", from, to, amount, opts) +} + +// +// Contracts +// + +// Get contracts +var rocketTokenFixedSupplyRPLLock sync.Mutex + +func getRocketTokenRPLFixedSupply(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*rocketpool.Contract, error) { + rocketTokenFixedSupplyRPLLock.Lock() + defer rocketTokenFixedSupplyRPLLock.Unlock() + return rp.GetContract("rocketTokenRPLFixedSupply", opts) +} diff --git a/bindings/tokens/rpl.go b/bindings/tokens/rpl.go new file mode 100644 index 000000000..7d24e46fd --- /dev/null +++ b/bindings/tokens/rpl.go @@ -0,0 +1,185 @@ +package tokens + +import ( + "fmt" + "math/big" + "sync" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + + "github.com/rocket-pool/smartnode/bindings/rocketpool" +) + +// +// Core ERC-20 functions +// + +// Get RPL total supply +func GetRPLTotalSupply(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*big.Int, error) { + rocketTokenRPL, err := getRocketTokenRPL(rp, opts) + if err != nil { + return nil, err + } + return totalSupply(rocketTokenRPL, "RPL", opts) +} + +// Get RPL balance +func GetRPLBalance(rp *rocketpool.RocketPool, address common.Address, opts *bind.CallOpts) (*big.Int, error) { + rocketTokenRPL, err := getRocketTokenRPL(rp, opts) + if err != nil { + return nil, err + } + return balanceOf(rocketTokenRPL, "RPL", address, opts) +} + +// Get RPL allowance +func GetRPLAllowance(rp *rocketpool.RocketPool, owner, spender common.Address, opts *bind.CallOpts) (*big.Int, error) { + rocketTokenRPL, err := getRocketTokenRPL(rp, opts) + if err != nil { + return nil, err + } + return allowance(rocketTokenRPL, "RPL", owner, spender, opts) +} + +// Estimate the gas of TransferRPL +func EstimateTransferRPLGas(rp *rocketpool.RocketPool, to common.Address, amount *big.Int, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketTokenRPL, err := getRocketTokenRPL(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return estimateTransferGas(rocketTokenRPL, "RPL", to, amount, opts) +} + +// Transfer RPL +func TransferRPL(rp *rocketpool.RocketPool, to common.Address, amount *big.Int, opts *bind.TransactOpts) (common.Hash, error) { + rocketTokenRPL, err := getRocketTokenRPL(rp, nil) + if err != nil { + return common.Hash{}, err + } + return transfer(rocketTokenRPL, "RPL", to, amount, opts) +} + +// Estimate the gas of ApproveRPL +func EstimateApproveRPLGas(rp *rocketpool.RocketPool, spender common.Address, amount *big.Int, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketTokenRPL, err := getRocketTokenRPL(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return estimateApproveGas(rocketTokenRPL, "RPL", spender, amount, opts) +} + +// Approve an RPL spender +func ApproveRPL(rp *rocketpool.RocketPool, spender common.Address, amount *big.Int, opts *bind.TransactOpts) (common.Hash, error) { + rocketTokenRPL, err := getRocketTokenRPL(rp, nil) + if err != nil { + return common.Hash{}, err + } + return approve(rocketTokenRPL, "RPL", spender, amount, opts) +} + +// Estimate the gas of TransferFromRPL +func EstimateTransferFromRPLGas(rp *rocketpool.RocketPool, from, to common.Address, amount *big.Int, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketTokenRPL, err := getRocketTokenRPL(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return estimateTransferFromGas(rocketTokenRPL, "RPL", from, to, amount, opts) +} + +// Transfer RPL from a sender +func TransferFromRPL(rp *rocketpool.RocketPool, from, to common.Address, amount *big.Int, opts *bind.TransactOpts) (common.Hash, error) { + rocketTokenRPL, err := getRocketTokenRPL(rp, nil) + if err != nil { + return common.Hash{}, err + } + return transferFrom(rocketTokenRPL, "RPL", from, to, amount, opts) +} + +// +// RPL functions +// + +// Estimate the gas of MintInflationRPL +func EstimateMintInflationRPLGas(rp *rocketpool.RocketPool, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketTokenRPL, err := getRocketTokenRPL(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketTokenRPL.GetTransactionGasInfo(opts, "inflationMintTokens") +} + +// Mint new RPL tokens from inflation +func MintInflationRPL(rp *rocketpool.RocketPool, opts *bind.TransactOpts) (common.Hash, error) { + rocketTokenRPL, err := getRocketTokenRPL(rp, nil) + if err != nil { + return common.Hash{}, err + } + tx, err := rocketTokenRPL.Transact(opts, "inflationMintTokens") + if err != nil { + return common.Hash{}, fmt.Errorf("error minting RPL tokens from inflation: %w", err) + } + return tx.Hash(), nil +} + +// Estimate the gas of SwapFixedSupplyRPLForRPL +func EstimateSwapFixedSupplyRPLForRPLGas(rp *rocketpool.RocketPool, amount *big.Int, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketTokenRPL, err := getRocketTokenRPL(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketTokenRPL.GetTransactionGasInfo(opts, "swapTokens", amount) +} + +// Swap fixed-supply RPL for new RPL tokens +func SwapFixedSupplyRPLForRPL(rp *rocketpool.RocketPool, amount *big.Int, opts *bind.TransactOpts) (common.Hash, error) { + rocketTokenRPL, err := getRocketTokenRPL(rp, nil) + if err != nil { + return common.Hash{}, err + } + tx, err := rocketTokenRPL.Transact(opts, "swapTokens", amount) + if err != nil { + return common.Hash{}, fmt.Errorf("error swapping fixed-supply RPL for new RPL: %w", err) + } + return tx.Hash(), nil +} + +// Get the RPL inflation interval rate +func GetRPLInflationIntervalRate(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*big.Int, error) { + rocketTokenRPL, err := getRocketTokenRPL(rp, opts) + if err != nil { + return nil, err + } + rate := new(*big.Int) + if err := rocketTokenRPL.Call(opts, rate, "getInflationIntervalRate"); err != nil { + return nil, fmt.Errorf("error getting RPL inflation interval rate: %w", err) + } + return *rate, nil +} + +// Get the time that inflation started for this interval +func GetRPLInflationIntervalStartTime(rp *rocketpool.RocketPool, opts *bind.CallOpts) (time.Time, error) { + rocketTokenRPL, err := getRocketTokenRPL(rp, opts) + if err != nil { + return time.Time{}, err + } + value := new(*big.Int) + if err := rocketTokenRPL.Call(opts, value, "getInflationIntervalStartTime"); err != nil { + return time.Time{}, fmt.Errorf("Could not get RPL inflation interval start time: %w", err) + } + return time.Unix((*value).Int64(), 0), nil +} + +// +// Contracts +// + +// Get contracts +var rocketTokenRPLLock sync.Mutex + +func getRocketTokenRPL(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*rocketpool.Contract, error) { + rocketTokenRPLLock.Lock() + defer rocketTokenRPLLock.Unlock() + return rp.GetContract("rocketTokenRPL", opts) +} diff --git a/bindings/tokens/tokens.go b/bindings/tokens/tokens.go new file mode 100644 index 000000000..36bd5f4e9 --- /dev/null +++ b/bindings/tokens/tokens.go @@ -0,0 +1,152 @@ +package tokens + +import ( + "context" + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "golang.org/x/sync/errgroup" + + "github.com/rocket-pool/smartnode/bindings/rocketpool" +) + +// Token balances +type Balances struct { + ETH *big.Int `json:"eth"` + RETH *big.Int `json:"reth"` + RPL *big.Int `json:"rpl"` + FixedSupplyRPL *big.Int `json:"fixedSupplyRpl"` +} + +// Get token balances of an address +func GetBalances(rp *rocketpool.RocketPool, address common.Address, opts *bind.CallOpts) (Balances, error) { + + // Get call options block number + var blockNumber *big.Int + if opts != nil { + blockNumber = opts.BlockNumber + } + + // Data + var wg errgroup.Group + var ethBalance *big.Int + var rethBalance *big.Int + var rplBalance *big.Int + var fixedSupplyRplBalance *big.Int + + // Load data + wg.Go(func() error { + var err error + ethBalance, err = rp.Client.BalanceAt(context.Background(), address, blockNumber) + return err + }) + wg.Go(func() error { + var err error + rethBalance, err = GetRETHBalance(rp, address, opts) + return err + }) + wg.Go(func() error { + var err error + rplBalance, err = GetRPLBalance(rp, address, opts) + return err + }) + wg.Go(func() error { + var err error + fixedSupplyRplBalance, err = GetFixedSupplyRPLBalance(rp, address, opts) + return err + }) + + // Wait for data + if err := wg.Wait(); err != nil { + return Balances{}, err + } + + // Return + return Balances{ + ETH: ethBalance, + RETH: rethBalance, + RPL: rplBalance, + FixedSupplyRPL: fixedSupplyRplBalance, + }, nil + +} + +// Get a token contract's ETH balance +func contractETHBalance(rp *rocketpool.RocketPool, tokenContract *rocketpool.Contract, opts *bind.CallOpts) (*big.Int, error) { + var blockNumber *big.Int + if opts != nil { + blockNumber = opts.BlockNumber + } + return rp.Client.BalanceAt(context.Background(), *(tokenContract.Address), blockNumber) +} + +// Get a token's total supply +func totalSupply(tokenContract *rocketpool.Contract, tokenName string, opts *bind.CallOpts) (*big.Int, error) { + totalSupply := new(*big.Int) + if err := tokenContract.Call(opts, totalSupply, "totalSupply"); err != nil { + return nil, fmt.Errorf("error getting %s total supply: %w", tokenName, err) + } + return *totalSupply, nil +} + +// Get a token balance +func balanceOf(tokenContract *rocketpool.Contract, tokenName string, address common.Address, opts *bind.CallOpts) (*big.Int, error) { + balance := new(*big.Int) + if err := tokenContract.Call(opts, balance, "balanceOf", address); err != nil { + return nil, fmt.Errorf("error getting %s balance of %s: %w", tokenName, address.Hex(), err) + } + return *balance, nil +} + +// Get a spender's allowance for an address +func allowance(tokenContract *rocketpool.Contract, tokenName string, owner, spender common.Address, opts *bind.CallOpts) (*big.Int, error) { + allowance := new(*big.Int) + if err := tokenContract.Call(opts, allowance, "allowance", owner, spender); err != nil { + return nil, fmt.Errorf("error getting %s allowance of %s for %s: %w", tokenName, spender.Hex(), owner.Hex(), err) + } + return *allowance, nil +} + +// Estimate the gas of transfer +func estimateTransferGas(tokenContract *rocketpool.Contract, tokenName string, to common.Address, amount *big.Int, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return tokenContract.GetTransactionGasInfo(opts, "transfer", to, amount) +} + +// Transfer tokens to an address +func transfer(tokenContract *rocketpool.Contract, tokenName string, to common.Address, amount *big.Int, opts *bind.TransactOpts) (common.Hash, error) { + tx, err := tokenContract.Transact(opts, "transfer", to, amount) + if err != nil { + return common.Hash{}, fmt.Errorf("error transferring %s to %s: %w", tokenName, to.Hex(), err) + } + return tx.Hash(), nil +} + +// Estimate the gas of approve +func estimateApproveGas(tokenContract *rocketpool.Contract, tokenName string, spender common.Address, amount *big.Int, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return tokenContract.GetTransactionGasInfo(opts, "approve", spender, amount) +} + +// Approve a token allowance for a spender +func approve(tokenContract *rocketpool.Contract, tokenName string, spender common.Address, amount *big.Int, opts *bind.TransactOpts) (common.Hash, error) { + tx, err := tokenContract.Transact(opts, "approve", spender, amount) + if err != nil { + return common.Hash{}, fmt.Errorf("error approving %s allowance for %s: %w", tokenName, spender.Hex(), err) + } + return tx.Hash(), nil +} + +// Estimate the gas of transferFrom +func estimateTransferFromGas(tokenContract *rocketpool.Contract, tokenName string, from, to common.Address, amount *big.Int, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return tokenContract.GetTransactionGasInfo(opts, "transferFrom", from, to, amount) +} + +// Transfer tokens from a sender to an address +func transferFrom(tokenContract *rocketpool.Contract, tokenName string, from, to common.Address, amount *big.Int, opts *bind.TransactOpts) (common.Hash, error) { + tx, err := tokenContract.Transact(opts, "transferFrom", from, to, amount) + if err != nil { + return common.Hash{}, fmt.Errorf("error transferring %s from %s to %s: %w", tokenName, from.Hex(), to.Hex(), err) + } + return tx.Hash(), nil +} diff --git a/bindings/types/beacon.go b/bindings/types/beacon.go new file mode 100644 index 000000000..168537735 --- /dev/null +++ b/bindings/types/beacon.go @@ -0,0 +1,105 @@ +package types + +import ( + "fmt" + + "encoding/hex" + + "github.com/rocket-pool/smartnode/bindings/utils/json" +) + +// Validator pubkey +const ValidatorPubkeyLength = 48 // bytes +type ValidatorPubkey [ValidatorPubkeyLength]byte + +// Bytes conversion +func (v ValidatorPubkey) Bytes() []byte { + return v[:] +} +func BytesToValidatorPubkey(value []byte) ValidatorPubkey { + var pubkey ValidatorPubkey + copy(pubkey[:], value) + return pubkey +} + +// String conversion +func (v ValidatorPubkey) Hex() string { + return hex.EncodeToString(v.Bytes()) +} +func (v ValidatorPubkey) String() string { + return v.Hex() +} +func HexToValidatorPubkey(value string) (ValidatorPubkey, error) { + pubkey := make([]byte, ValidatorPubkeyLength) + if len(value) != hex.EncodedLen(ValidatorPubkeyLength) { + return ValidatorPubkey{}, fmt.Errorf("Invalid validator public key hex string %s: invalid length %d", value, len(value)) + } + if _, err := hex.Decode(pubkey, []byte(value)); err != nil { + return ValidatorPubkey{}, err + } + return BytesToValidatorPubkey(pubkey), nil +} + +// JSON encoding +func (v ValidatorPubkey) MarshalJSON() ([]byte, error) { + return json.Marshal(v.Hex()) +} +func (v *ValidatorPubkey) UnmarshalJSON(data []byte) error { + var dataStr string + if err := json.Unmarshal(data, &dataStr); err != nil { + return err + } + pubkey, err := HexToValidatorPubkey(dataStr) + if err == nil { + *v = pubkey + } + return err +} + +// Validator signature +const ValidatorSignatureLength = 96 // bytes +type ValidatorSignature [ValidatorSignatureLength]byte + +// Bytes conversion +func (v ValidatorSignature) Bytes() []byte { + return v[:] +} +func BytesToValidatorSignature(value []byte) ValidatorSignature { + var signature ValidatorSignature + copy(signature[:], value) + return signature +} + +// String conversion +func (v ValidatorSignature) Hex() string { + return hex.EncodeToString(v.Bytes()) +} +func (v ValidatorSignature) String() string { + return v.Hex() +} +func HexToValidatorSignature(value string) (ValidatorSignature, error) { + signature := make([]byte, ValidatorSignatureLength) + if len(value) != hex.EncodedLen(ValidatorSignatureLength) { + return ValidatorSignature{}, fmt.Errorf("Invalid validator signature hex string %s: invalid length %d", value, len(value)) + } + if _, err := hex.Decode(signature, []byte(value)); err != nil { + return ValidatorSignature{}, err + } + return BytesToValidatorSignature(signature), nil +} + +// JSON encoding +func (v ValidatorSignature) MarshalJSON() ([]byte, error) { + return json.Marshal(v.Hex()) +} +func (v *ValidatorSignature) UnmarshalJSON(data []byte) error { + var dataStr string + if err := json.Unmarshal(data, &dataStr); err != nil { + return err + } + signature, err := HexToValidatorSignature(dataStr) + if err == nil { + *v = signature + } + return err +} diff --git a/bindings/types/dao.go b/bindings/types/dao.go new file mode 100644 index 000000000..013ba2b08 --- /dev/null +++ b/bindings/types/dao.go @@ -0,0 +1,123 @@ +package types + +import ( + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/rocket-pool/smartnode/bindings/utils/json" +) + +// DAO proposal states +type ProposalState uint8 + +const ( + Pending ProposalState = iota + Active + Cancelled + Defeated + Succeeded + Expired + Executed +) + +var ProposalStates = []string{"Pending", "Active", "Cancelled", "Defeated", "Succeeded", "Expired", "Executed"} + +// pDAO proposal states +type ProtocolDaoProposalState uint8 + +const ( + ProtocolDaoProposalState_Pending ProtocolDaoProposalState = iota + ProtocolDaoProposalState_ActivePhase1 + ProtocolDaoProposalState_ActivePhase2 + ProtocolDaoProposalState_Destroyed + ProtocolDaoProposalState_Vetoed + ProtocolDaoProposalState_QuorumNotMet + ProtocolDaoProposalState_Defeated + ProtocolDaoProposalState_Succeeded + ProtocolDaoProposalState_Expired + ProtocolDaoProposalState_Executed +) + +var ProtocolDaoProposalStates = []string{"Pending", "Active (Phase 1)", "Active (Phase 2)", "Destroyed", "Vetoed", "Quorum not Met", "Defeated", "Succeeded", "Expired", "Executed"} + +// pDAO voting direction +type VoteDirection uint8 + +const ( + VoteDirection_NoVote VoteDirection = iota + VoteDirection_Abstain + VoteDirection_For + VoteDirection_Against + VoteDirection_AgainstWithVeto +) + +var VoteDirections = []string{"Not Voted", "Abstain", "In Favor", "Against", "Against with Veto"} + +// DAO setting types +type ProposalSettingType uint8 + +const ( + ProposalSettingType_Uint256 ProposalSettingType = iota + ProposalSettingType_Bool + ProposalSettingType_Address +) + +// Challenge states +type ChallengeState uint8 + +const ( + ChallengeState_Unchallenged ChallengeState = iota + ChallengeState_Challenged + ChallengeState_Responded + ChallengeState_Paid +) + +// Info about a node's voting power +type NodeVotingInfo struct { + NodeAddress common.Address `json:"nodeAddress"` + VotingPower *big.Int `json:"votingPower"` + Delegate common.Address `json:"delegate"` +} + +// A node of the voting Merkle Tree (not a Rocket Pool node) +type VotingTreeNode struct { + Sum *big.Int `json:"sum"` + Hash common.Hash `json:"hash"` +} + +// String conversion +func (s ProposalState) String() string { + if int(s) >= len(ProposalStates) { + return "" + } + return ProposalStates[s] +} +func StringToProposalState(value string) (ProposalState, error) { + for state, str := range ProposalStates { + if value == str { + return ProposalState(state), nil + } + } + return 0, fmt.Errorf("Invalid proposal state '%s'", value) +} + +// JSON encoding +func (s ProposalState) MarshalJSON() ([]byte, error) { + str := s.String() + if str == "" { + return []byte{}, fmt.Errorf("Invalid proposal state '%d'", s) + } + return json.Marshal(str) +} +func (s *ProposalState) UnmarshalJSON(data []byte) error { + var dataStr string + if err := json.Unmarshal(data, &dataStr); err != nil { + return err + } + state, err := StringToProposalState(dataStr) + if err == nil { + *s = state + } + return err +} diff --git a/bindings/types/minipool.go b/bindings/types/minipool.go new file mode 100644 index 000000000..23d64103a --- /dev/null +++ b/bindings/types/minipool.go @@ -0,0 +1,105 @@ +package types + +import ( + "fmt" + + "github.com/rocket-pool/smartnode/bindings/utils/json" +) + +// Minipool statuses +type MinipoolStatus uint8 + +const ( + Initialized MinipoolStatus = iota + Prelaunch + Staking + Withdrawable + Dissolved +) + +var MinipoolStatuses = []string{"Initialized", "Prelaunch", "Staking", "Withdrawable", "Dissolved"} + +// String conversion +func (s MinipoolStatus) String() string { + if int(s) >= len(MinipoolStatuses) { + return "" + } + return MinipoolStatuses[s] +} +func StringToMinipoolStatus(value string) (MinipoolStatus, error) { + for status, str := range MinipoolStatuses { + if value == str { + return MinipoolStatus(status), nil + } + } + return 0, fmt.Errorf("Invalid minipool status '%s'", value) +} + +// JSON encoding +func (s MinipoolStatus) MarshalJSON() ([]byte, error) { + str := s.String() + if str == "" { + return []byte{}, fmt.Errorf("Invalid minipool status '%d'", s) + } + return json.Marshal(str) +} +func (s *MinipoolStatus) UnmarshalJSON(data []byte) error { + var dataStr string + if err := json.Unmarshal(data, &dataStr); err != nil { + return err + } + status, err := StringToMinipoolStatus(dataStr) + if err == nil { + *s = status + } + return err +} + +// Minipool deposit types +type MinipoolDeposit uint8 + +const ( + None MinipoolDeposit = iota + Full + Half + Empty + Variable +) + +var MinipoolDepositTypes = []string{"None", "Full", "Half", "Empty", "Variable"} + +// String conversion +func (d MinipoolDeposit) String() string { + if int(d) >= len(MinipoolDepositTypes) { + return "" + } + return MinipoolDepositTypes[d] +} +func StringToMinipoolDeposit(value string) (MinipoolDeposit, error) { + for depositType, str := range MinipoolDepositTypes { + if value == str { + return MinipoolDeposit(depositType), nil + } + } + return 0, fmt.Errorf("Invalid minipool deposit type '%s'", value) +} + +// JSON encoding +func (d MinipoolDeposit) MarshalJSON() ([]byte, error) { + str := d.String() + if str == "" { + return []byte{}, fmt.Errorf("Invalid minipool deposit type '%d'", d) + } + return json.Marshal(str) +} +func (d *MinipoolDeposit) UnmarshalJSON(data []byte) error { + var dataStr string + if err := json.Unmarshal(data, &dataStr); err != nil { + return err + } + depositType, err := StringToMinipoolDeposit(dataStr) + if err == nil { + *d = depositType + } + return err +} diff --git a/bindings/utils/address_generation.go b/bindings/utils/address_generation.go new file mode 100644 index 000000000..5720b7880 --- /dev/null +++ b/bindings/utils/address_generation.go @@ -0,0 +1,17 @@ +package utils + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" +) + +// Combine a node's address and a salt to retreive a new salt compatible with depositing +func GetNodeSalt(nodeAddress common.Address, salt *big.Int) common.Hash { + // Create a new salt by hashing the original and the node address + saltBytes := [32]byte{} + salt.FillBytes(saltBytes[:]) + saltHash := crypto.Keccak256Hash(nodeAddress.Bytes(), saltBytes[:]) + return saltHash +} diff --git a/bindings/utils/deposit_retrieval.go b/bindings/utils/deposit_retrieval.go new file mode 100644 index 000000000..d8fcbd5b1 --- /dev/null +++ b/bindings/utils/deposit_retrieval.go @@ -0,0 +1,123 @@ +package utils + +import ( + "bytes" + "encoding/binary" + "math/big" + "sort" + "sync" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + rptypes "github.com/rocket-pool/smartnode/bindings/types" + "github.com/rocket-pool/smartnode/bindings/utils/eth" +) + +// BeaconDepositEvent represents a DepositEvent event raised by the BeaconDeposit contract. +type BeaconDepositEvent struct { + Pubkey []byte `abi:"pubkey"` + WithdrawalCredentials []byte `abi:"withdrawal_credentials"` + Amount []byte `abi:"amount"` + Signature []byte `abi:"signature"` + Index []byte `abi:"index"` + Raw types.Log // Blockchain specific contextual infos +} + +// Formatted Beacon deposit event data +type DepositData struct { + Pubkey rptypes.ValidatorPubkey `json:"pubkey"` + WithdrawalCredentials common.Hash `json:"withdrawalCredentials"` + Amount uint64 `json:"amount"` + Signature rptypes.ValidatorSignature `json:"signature"` + TxHash common.Hash `json:"txHash"` + BlockNumber uint64 `json:"blockNumber"` + TxIndex uint `json:"txIndex"` +} + +// Gets all of the deposit contract's deposit events for the provided pubkeys +func GetDeposits(rp *rocketpool.RocketPool, pubkeys map[rptypes.ValidatorPubkey]bool, startBlock *big.Int, intervalSize *big.Int, opts *bind.CallOpts) (map[rptypes.ValidatorPubkey][]DepositData, error) { + + // Get the deposit contract wrapper + casperDeposit, err := getCasperDeposit(rp, opts) + if err != nil { + return nil, err + } + + // Create the initial map and pubkey lookup + depositMap := make(map[rptypes.ValidatorPubkey][]DepositData, len(pubkeys)) + + // Get the deposit events + addressFilter := []common.Address{*casperDeposit.Address} + topicFilter := [][]common.Hash{{casperDeposit.ABI.Events["DepositEvent"].ID}} + logs, err := eth.GetLogs(rp, addressFilter, topicFilter, intervalSize, startBlock, nil, nil) + if err != nil { + return nil, err + } + + // Process each event + for _, log := range logs { + depositEvent := new(BeaconDepositEvent) + err = casperDeposit.Contract.UnpackLog(depositEvent, "DepositEvent", log) + if err != nil { + return nil, err + } + + // Check if this is a deposit for one of the pubkeys we're looking for + pubkey := rptypes.BytesToValidatorPubkey(depositEvent.Pubkey) + _, exists := pubkeys[pubkey] + if exists { + // Convert the deposit amount from little-endian binary to a uint64 + var amount uint64 + buf := bytes.NewReader(depositEvent.Amount) + err = binary.Read(buf, binary.LittleEndian, &amount) + if err != nil { + return nil, err + } + + // Create the deposit data wrapper and add it to this pubkey's collection + depositData := DepositData{ + Pubkey: pubkey, + WithdrawalCredentials: common.BytesToHash(depositEvent.WithdrawalCredentials), + Amount: amount, + Signature: rptypes.BytesToValidatorSignature(depositEvent.Signature), + TxHash: log.TxHash, + BlockNumber: log.BlockNumber, + TxIndex: log.TxIndex, + } + depositMap[pubkey] = append(depositMap[pubkey], depositData) + } + } + + // Sort deposits by time + for _, deposits := range depositMap { + if len(deposits) > 1 { + sortDepositData(deposits) + } + } + + return depositMap, nil +} + +// Sorts a slice of deposit data entries - lower blocks come first, and if multiple transactions occur +// in the same block, lower transaction indices come first +func sortDepositData(data []DepositData) { + sort.Slice(data, func(i int, j int) bool { + first := data[i] + second := data[j] + if first.BlockNumber == second.BlockNumber { + return first.TxIndex < second.TxIndex + } + return first.BlockNumber < second.BlockNumber + }) +} + +// Get contracts +var casperDepositLock sync.Mutex + +func getCasperDeposit(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*rocketpool.Contract, error) { + casperDepositLock.Lock() + defer casperDepositLock.Unlock() + return rp.GetContract("casperDeposit", opts) +} diff --git a/bindings/utils/eth/erc20.go b/bindings/utils/eth/erc20.go new file mode 100644 index 000000000..e5b09a7ae --- /dev/null +++ b/bindings/utils/eth/erc20.go @@ -0,0 +1,205 @@ +package eth + +import ( + "fmt" + "math/big" + "strings" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/rocket-pool/smartnode/bindings/rocketpool" +) + +const ( + Erc20AbiString string = `[ + { + "constant": true, + "inputs": [], + "name": "name", + "outputs": [ + { + "name": "", + "type": "string" + } + ], + "payable": false, + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "decimals", + "outputs": [ + { + "name": "", + "type": "uint8" + } + ], + "payable": false, + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_owner", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "name": "balance", + "type": "uint256" + } + ], + "payable": false, + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "symbol", + "outputs": [ + { + "name": "", + "type": "string" + } + ], + "payable": false, + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_to", + "type": "address" + }, + { + "name": "_value", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "name": "success", + "type": "bool" + } + ], + "payable": false, + "type": "function" + } + ]` +) + +// Global container for the parsed ABI above +var erc20Abi *abi.ABI + +type Erc20Contract struct { + Name string + Symbol string + Decimals uint8 + contract *rocketpool.Contract +} + +// Creates a contract wrapper for the ERC20 at the given address +func NewErc20Contract(address common.Address, client rocketpool.ExecutionClient, opts *bind.CallOpts) (*Erc20Contract, error) { + // Parse the ABI + if erc20Abi == nil { + abiParsed, err := abi.JSON(strings.NewReader(Erc20AbiString)) + if err != nil { + return nil, fmt.Errorf("error parsing ERC20 ABI: %w", err) + } + erc20Abi = &abiParsed + } + + // Create contract + contract := &rocketpool.Contract{ + Contract: bind.NewBoundContract(address, *erc20Abi, client, client, client), + Address: &address, + ABI: erc20Abi, + Client: client, + } + + // Create the wrapper + wrapper := &Erc20Contract{ + contract: contract, + } + + // Get the details + name, err := wrapper.GetName(opts) + if err != nil { + return nil, err + } + wrapper.Name = name + symbol, err := wrapper.GetSymbol(opts) + if err != nil { + return nil, err + } + wrapper.Symbol = symbol + decimals, err := wrapper.GetDecimals(opts) + if err != nil { + return nil, err + } + wrapper.Decimals = decimals + + return wrapper, nil +} + +// Get the token name +func (c *Erc20Contract) GetName(opts *bind.CallOpts) (string, error) { + name := new(string) + err := c.contract.Call(opts, name, "name") + if err != nil { + return "", fmt.Errorf("could not get ERC20 name: %w", err) + } + return *name, nil +} + +// Get the token symbol +func (c *Erc20Contract) GetSymbol(opts *bind.CallOpts) (string, error) { + symbol := new(string) + err := c.contract.Call(opts, symbol, "symbol") + if err != nil { + return "", fmt.Errorf("could not get ERC20 symbol: %w", err) + } + return *symbol, nil +} + +// Get the token decimals +func (c *Erc20Contract) GetDecimals(opts *bind.CallOpts) (uint8, error) { + decimals := new(uint8) + err := c.contract.Call(opts, decimals, "decimals") + if err != nil { + return 0, fmt.Errorf("could not get ERC20 decimals: %w", err) + } + return *decimals, nil +} + +// Get the token balance for an address +func (c *Erc20Contract) BalanceOf(address common.Address, opts *bind.CallOpts) (*big.Int, error) { + balance := new(*big.Int) + err := c.contract.Call(opts, balance, "balanceOf", address) + if err != nil { + return nil, fmt.Errorf("could not get ERC20 balance for address %s: %w", address.Hex(), err) + } + return *balance, nil +} + +// Estimate the gas for transferring an ERC20 to another address +func (c *Erc20Contract) EstimateTransferGas(to common.Address, amount *big.Int, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + return c.contract.GetTransactionGasInfo(opts, "transfer", to, amount) +} + +// Transfer an ERC20 to another address +func (c *Erc20Contract) Transfer(to common.Address, amount *big.Int, opts *bind.TransactOpts) (*types.Transaction, error) { + tx, err := c.contract.Transact(opts, "transfer", to, amount) + if err != nil { + return nil, fmt.Errorf("could not transfer ERC20 to %s: %w", to.Hex(), err) + } + return tx, nil +} diff --git a/bindings/utils/eth/logs.go b/bindings/utils/eth/logs.go new file mode 100644 index 000000000..9f0e1b179 --- /dev/null +++ b/bindings/utils/eth/logs.go @@ -0,0 +1,124 @@ +package eth + +import ( + "context" + "math/big" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/storage" +) + +type FilterQuery struct { + BlockHash *common.Hash + FromBlock *big.Int + ToBlock *big.Int + Topics [][]common.Hash +} + +func FilterContractLogs(rp *rocketpool.RocketPool, contractName string, q FilterQuery, intervalSize *big.Int, opts *bind.CallOpts) ([]types.Log, error) { + rocketDaoNodeTrustedUpgrade, err := rp.GetContract("rocketDAONodeTrustedUpgrade", opts) + if err != nil { + return nil, err + } + // Get all the addresses this contract has ever been deployed at + addresses := make([]common.Address, 0) + // Construct a filter to query ContractUpgraded event + addressFilter := []common.Address{*rocketDaoNodeTrustedUpgrade.Address} + topicFilter := [][]common.Hash{{rocketDaoNodeTrustedUpgrade.ABI.Events["ContractUpgraded"].ID}, {crypto.Keccak256Hash([]byte(contractName))}} + logs, err := GetLogs(rp, addressFilter, topicFilter, intervalSize, nil, nil, nil) + if err != nil { + return nil, err + } + // Iterate the logs and store every past contract address + for _, log := range logs { + addresses = append(addresses, common.HexToAddress(log.Topics[2].Hex())) + } + // Append current address + currentAddress, err := rp.GetAddress(contractName, opts) + if err != nil { + return nil, err + } + addresses = append(addresses, *currentAddress) + // Perform the desired getLogs call and return results + return GetLogs(rp, addresses, q.Topics, intervalSize, q.FromBlock, q.ToBlock, q.BlockHash) +} + +// Gets the logs for a particular log request, breaking the calls into batches if necessary +func GetLogs(rp *rocketpool.RocketPool, addressFilter []common.Address, topicFilter [][]common.Hash, intervalSize, fromBlock, toBlock *big.Int, blockHash *common.Hash) ([]types.Log, error) { + var logs []types.Log + + // Get the block that Rocket Pool was deployed on as the lower bound if one wasn't specified + if fromBlock == nil { + var err error + fromBlock, err = storage.GetDeployBlock(rp) + if err != nil { + return nil, err + } + } + + if intervalSize == nil { + // Handle unlimited intervals with a single call + logs, err := rp.Client.FilterLogs(context.Background(), ethereum.FilterQuery{ + Addresses: addressFilter, + Topics: topicFilter, + FromBlock: fromBlock, + ToBlock: toBlock, + BlockHash: blockHash, + }) + if err != nil { + return nil, err + } + return logs, nil + } else { + // Get the latest block + if toBlock == nil { + latestBlock, err := rp.Client.BlockNumber(context.Background()) + if err != nil { + return nil, err + } + toBlock = big.NewInt(0) + toBlock.SetUint64(latestBlock) + } + + // Set the start and end, clamping on the latest block + intervalSize := big.NewInt(0).Sub(intervalSize, big.NewInt(1)) + start := big.NewInt(0).Set(fromBlock) + end := big.NewInt(0).Add(start, intervalSize) + if end.Cmp(toBlock) == 1 { + end.Set(toBlock) + } + for { + // Get the logs using the current interval + newLogs, err := rp.Client.FilterLogs(context.Background(), ethereum.FilterQuery{ + Addresses: addressFilter, + Topics: topicFilter, + FromBlock: start, + ToBlock: end, + BlockHash: blockHash, + }) + if err != nil { + return nil, err + } + + // Append the logs to the total list + logs = append(logs, newLogs...) + + // Return once we've finished iterating + if end.Cmp(toBlock) == 0 { + return logs, nil + } + + // Update to the next interval (end+1 : that + interval - 1) + start.Add(end, big.NewInt(1)) + end.Add(start, intervalSize) + if end.Cmp(toBlock) == 1 { + end.Set(toBlock) + } + } + } +} diff --git a/bindings/utils/eth/transactions.go b/bindings/utils/eth/transactions.go new file mode 100644 index 000000000..be8a1e365 --- /dev/null +++ b/bindings/utils/eth/transactions.go @@ -0,0 +1,125 @@ +package eth + +import ( + "context" + "math/big" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/rocket-pool/smartnode/bindings/rocketpool" +) + +// Estimate the gas of SendTransaction +func EstimateSendTransactionGas(client rocketpool.ExecutionClient, toAddress common.Address, data []byte, useSafeGasLimit bool, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + + // User-defined settings + response := rocketpool.GasInfo{} + + // Set default value + value := opts.Value + if value == nil { + value = big.NewInt(0) + } + + // Set default data + if data == nil { + data = []byte{} + } + + // Estimate gas limit + gasLimit, err := client.EstimateGas(context.Background(), ethereum.CallMsg{ + From: opts.From, + To: &toAddress, + GasPrice: big.NewInt(0), // set to 0 for simulation + Data: data, + Value: value, + }) + if err != nil { + return rocketpool.GasInfo{}, err + } + response.EstGasLimit = gasLimit + + if useSafeGasLimit { + response.SafeGasLimit = uint64(float64(gasLimit) * rocketpool.GasLimitMultiplier) + } else { + response.SafeGasLimit = gasLimit + } + + return response, err +} + +// Send a transaction to an address +// useSafeGasLimit will amplify the estimated gas limit to by 50% for safety (no effect if the gas limit in opts is already set). +func SendTransaction(client rocketpool.ExecutionClient, toAddress common.Address, chainID *big.Int, data []byte, useSafeGasLimit bool, opts *bind.TransactOpts) (common.Hash, error) { + var err error + + // Get from address nonce + var nonce uint64 + if opts.Nonce == nil { + nonce, err = client.PendingNonceAt(context.Background(), opts.From) + if err != nil { + return common.Hash{}, err + } + } else { + nonce = opts.Nonce.Uint64() + } + + // Set default value + value := opts.Value + if value == nil { + value = big.NewInt(0) + } + + // Set default data + if data == nil { + data = []byte{} + } + + // Estimate gas limit + gasLimit := opts.GasLimit + if gasLimit == 0 { + gasLimit, err = client.EstimateGas(context.Background(), ethereum.CallMsg{ + From: opts.From, + To: &toAddress, + GasPrice: big.NewInt(0), // use 0 gwei for simulation + Data: data, + Value: value, + }) + if err != nil { + return common.Hash{}, err + } + + if useSafeGasLimit { + gasLimit = uint64(float64(gasLimit) * rocketpool.GasLimitMultiplier) + } + } + + // Initialize transaction + tx := types.NewTx(&types.DynamicFeeTx{ + ChainID: chainID, + Nonce: nonce, + GasTipCap: opts.GasTipCap, + GasFeeCap: opts.GasFeeCap, + Gas: gasLimit, + To: &toAddress, + Value: value, + Data: data, + AccessList: []types.AccessTuple{}, + }) + + // Sign transaction + signedTx, err := opts.Signer(opts.From, tx) + if err != nil { + return common.Hash{}, err + } + + // Send transaction + if err = client.SendTransaction(context.Background(), signedTx); err != nil { + return common.Hash{}, err + } + + return signedTx.Hash(), nil + +} diff --git a/bindings/utils/eth/units.go b/bindings/utils/eth/units.go new file mode 100644 index 000000000..1278f83ee --- /dev/null +++ b/bindings/utils/eth/units.go @@ -0,0 +1,82 @@ +package eth + +import ( + "math" + "math/big" + "strconv" +) + +// Conversion factors +const ( + WeiPerEth float64 = 1e18 + WeiPerGwei float64 = 1e9 +) + +// Convert wei to eth +func WeiToEth(wei *big.Int) float64 { + if wei == nil { + return 0 + } + var weiFloat big.Float + var eth big.Float + weiFloat.SetInt(wei) + eth.Quo(&weiFloat, big.NewFloat(WeiPerEth)) + eth64, _ := eth.Float64() + return eth64 +} + +// Convert eth to wei +func EthToWei(eth float64) *big.Int { + var ethFloat big.Float + var weiFloat big.Float + var wei big.Int + ethFloat.SetString(strconv.FormatFloat(eth, 'f', -1, 64)) + weiFloat.Mul(ðFloat, big.NewFloat(WeiPerEth)) + weiFloat.Int(&wei) + return &wei +} + +// Convert wei to gigawei +func WeiToGwei(wei *big.Int) float64 { + var weiFloat big.Float + var gwei big.Float + weiFloat.SetInt(wei) + gwei.Quo(&weiFloat, big.NewFloat(WeiPerGwei)) + gwei64, _ := gwei.Float64() + return gwei64 +} + +// Convert gigawei to wei +func GweiToWei(gwei float64) *big.Int { + var gweiFloat big.Float + var weiFloat big.Float + var wei big.Int + gweiFloat.SetString(strconv.FormatFloat(gwei, 'f', -1, 64)) + weiFloat.Mul(&gweiFloat, big.NewFloat(WeiPerGwei)) + weiFloat.Int(&wei) + return &wei +} + +// Converts float amount to big.Int considering a token's decimals +func EthToWeiWithDecimals(amountRaw float64, decimals uint8) *big.Int { + var ethFloat big.Float + var weiFloat big.Float + var wei big.Int + ethFloat.SetString(strconv.FormatFloat(amountRaw, 'f', -1, 64)) + weiFloat.Mul(ðFloat, big.NewFloat(math.Pow(10, float64(decimals)))) + weiFloat.Int(&wei) + return &wei +} + +// Converts big.Int to float64 considering a token's decimals +func WeiToEthWithDecimals(amount *big.Int, decimals uint8) float64 { + if amount == nil { + return 0 + } + var weiFloat big.Float + var eth big.Float + weiFloat.SetInt(amount) + eth.Quo(&weiFloat, big.NewFloat(math.Pow(10, float64(decimals)))) + eth64, _ := eth.Float64() + return eth64 +} diff --git a/bindings/utils/json/json.go b/bindings/utils/json/json.go new file mode 100644 index 000000000..ec15a007b --- /dev/null +++ b/bindings/utils/json/json.go @@ -0,0 +1,19 @@ +package json + +import ( + "encoding/json" + "fmt" +) + +func Marshal(v interface{}) ([]byte, error) { + return json.Marshal(v) +} + +func Unmarshal(data []byte, v interface{}) error { + err := json.Unmarshal(data, v) + if err != nil { + return fmt.Errorf("%w\nUnable to Unmarshal JSON string %s", err, string(data)) + } + + return nil +} diff --git a/bindings/utils/multicall/abi.go b/bindings/utils/multicall/abi.go new file mode 100644 index 000000000..5bbaf033d --- /dev/null +++ b/bindings/utils/multicall/abi.go @@ -0,0 +1,21 @@ +/* +This code was derived from the following sources: + +- https://github.com/depocket/multicall-go +- https://github.com/wbobeirne/eth-balance-checker +*/ + +package multicall + +import ( + "github.com/ethereum/go-ethereum/common" +) + +type MultiCall struct { + Target common.Address + CallData []byte +} + +var MulticallABI string = "[{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"callData\",\"type\":\"bytes\"}],\"internalType\":\"struct Multicall2.Call[]\",\"name\":\"calls\",\"type\":\"tuple[]\"}],\"name\":\"aggregate\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"blockNumber\",\"type\":\"uint256\"},{\"internalType\":\"bytes[]\",\"name\":\"returnData\",\"type\":\"bytes[]\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"callData\",\"type\":\"bytes\"}],\"internalType\":\"struct Multicall2.Call[]\",\"name\":\"calls\",\"type\":\"tuple[]\"}],\"name\":\"blockAndAggregate\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"blockNumber\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"returnData\",\"type\":\"bytes\"}],\"internalType\":\"struct Multicall2.Result[]\",\"name\":\"returnData\",\"type\":\"tuple[]\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"blockNumber\",\"type\":\"uint256\"}],\"name\":\"getBlockHash\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getBlockNumber\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"blockNumber\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getCurrentBlockCoinbase\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"coinbase\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getCurrentBlockDifficulty\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"difficulty\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getCurrentBlockGasLimit\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"gaslimit\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getCurrentBlockTimestamp\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"timestamp\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"}],\"name\":\"getEthBalance\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"balance\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getLastBlockHash\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bool\",\"name\":\"requireSuccess\",\"type\":\"bool\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"callData\",\"type\":\"bytes\"}],\"internalType\":\"struct Multicall2.Call[]\",\"name\":\"calls\",\"type\":\"tuple[]\"}],\"name\":\"tryAggregate\",\"outputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"returnData\",\"type\":\"bytes\"}],\"internalType\":\"struct Multicall2.Result[]\",\"name\":\"returnData\",\"type\":\"tuple[]\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bool\",\"name\":\"requireSuccess\",\"type\":\"bool\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"callData\",\"type\":\"bytes\"}],\"internalType\":\"struct Multicall2.Call[]\",\"name\":\"calls\",\"type\":\"tuple[]\"}],\"name\":\"tryBlockAndAggregate\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"blockNumber\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"returnData\",\"type\":\"bytes\"}],\"internalType\":\"struct Multicall2.Result[]\",\"name\":\"returnData\",\"type\":\"tuple[]\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]" + +var BalancesABI string = "[{\"constant\":true,\"inputs\":[{\"name\":\"user\",\"type\":\"address\"},{\"name\":\"token\",\"type\":\"address\"}],\"name\":\"tokenBalance\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"users\",\"type\":\"address[]\"},{\"name\":\"tokens\",\"type\":\"address[]\"}],\"name\":\"balances\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256[]\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"payable\":true,\"stateMutability\":\"payable\",\"type\":\"fallback\"}]" diff --git a/bindings/utils/multicall/balances.go b/bindings/utils/multicall/balances.go new file mode 100644 index 000000000..ad221ba5d --- /dev/null +++ b/bindings/utils/multicall/balances.go @@ -0,0 +1,97 @@ +package multicall + +import ( + "context" + "fmt" + "math/big" + "strings" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "golang.org/x/sync/errgroup" +) + +const ( + balanceBatchSize int = 1000 + threadLimit int = 6 +) + +type BalanceBatcher struct { + Client rocketpool.ExecutionClient + ABI abi.ABI + ContractAddress common.Address +} + +func NewBalanceBatcher(client rocketpool.ExecutionClient, address common.Address) (*BalanceBatcher, error) { + abi, err := abi.JSON(strings.NewReader(BalancesABI)) + if err != nil { + return nil, err + } + + return &BalanceBatcher{ + Client: client, + ContractAddress: address, + ABI: abi, + }, nil +} + +func (b *BalanceBatcher) GetEthBalances(addresses []common.Address, opts *bind.CallOpts) ([]*big.Int, error) { + + // Sync + count := len(addresses) + var wg errgroup.Group + wg.SetLimit(threadLimit) + balances := make([]*big.Int, count) + + // Run the getters in batches + for i := 0; i < count; i += balanceBatchSize { + i := i + max := i + balanceBatchSize + if max > count { + max = count + } + + wg.Go(func() error { + subAddresses := addresses[i:max] + tokens := []common.Address{ + {}, // Empty token for ETH balance + } + callData, err := b.ABI.Pack("balances", subAddresses, tokens) + if err != nil { + return fmt.Errorf("error creating calldata for balances: %w", err) + } + + response, err := b.Client.CallContract(context.Background(), ethereum.CallMsg{To: &b.ContractAddress, Data: callData}, opts.BlockNumber) + if err != nil { + return fmt.Errorf("error calling balances: %w", err) + } + + var subBalances []*big.Int + err = b.ABI.UnpackIntoInterface(&subBalances, "balances", response) + if err != nil { + return fmt.Errorf("error unpacking balances response: %w", err) + } + + if len(subBalances) != len(subAddresses) { + return fmt.Errorf("received %d balances which mismatches query batch size %d", len(subBalances), len(subAddresses)) + } + for j, balance := range subBalances { + if balance == nil { + return fmt.Errorf("received nil balance for address %s", subAddresses[j].String()) + } + balances[i+j] = balance + } + + return nil + }) + } + + if err := wg.Wait(); err != nil { + return nil, fmt.Errorf("error getting balances: %w", err) + } + + return balances, nil +} diff --git a/bindings/utils/multicall/multicaller.go b/bindings/utils/multicall/multicaller.go new file mode 100644 index 000000000..42e7b5ff2 --- /dev/null +++ b/bindings/utils/multicall/multicaller.go @@ -0,0 +1,133 @@ +/* +* This code was derived from https://github.com/depocket/multicall-go + */ + +package multicall + +import ( + "context" + "fmt" + "strings" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/rocket-pool/smartnode/bindings/rocketpool" +) + +type Call struct { + Method string `json:"method"` + Target common.Address `json:"target"` + CallData []byte `json:"call_data"` + Contract *rocketpool.Contract + output interface{} +} + +type CallResponse struct { + Method string + Status bool + ReturnDataRaw []byte `json:"returnData"` +} + +type Result struct { + Success bool `json:"success"` + Output interface{} +} + +func (call Call) GetMultiCall() MultiCall { + return MultiCall{Target: call.Target, CallData: call.CallData} +} + +type MultiCaller struct { + Client rocketpool.ExecutionClient + ABI abi.ABI + ContractAddress common.Address + calls []Call +} + +func NewMultiCaller(client rocketpool.ExecutionClient, multicallerAddress common.Address) (*MultiCaller, error) { + mcAbi, err := abi.JSON(strings.NewReader(MulticallABI)) + if err != nil { + return nil, err + } + + return &MultiCaller{ + Client: client, + ABI: mcAbi, + ContractAddress: multicallerAddress, + calls: []Call{}, + }, nil +} + +func (caller *MultiCaller) AddCall(contract *rocketpool.Contract, output interface{}, method string, args ...interface{}) error { + callData, err := contract.ABI.Pack(method, args...) + if err != nil { + return fmt.Errorf("error adding call [%s]: %w", method, err) + } + call := Call{ + Method: method, + Target: *contract.Address, + CallData: callData, + Contract: contract, + output: output, + } + caller.calls = append(caller.calls, call) + return nil +} + +func (caller *MultiCaller) Execute(requireSuccess bool, opts *bind.CallOpts) ([]CallResponse, error) { + var multiCalls = make([]MultiCall, 0, len(caller.calls)) + for _, call := range caller.calls { + multiCalls = append(multiCalls, call.GetMultiCall()) + } + callData, err := caller.ABI.Pack("tryAggregate", requireSuccess, multiCalls) + if err != nil { + return nil, err + } + + resp, err := caller.Client.CallContract(context.Background(), ethereum.CallMsg{To: &caller.ContractAddress, Data: callData}, opts.BlockNumber) + if err != nil { + return nil, err + } + + responses, err := caller.ABI.Unpack("tryAggregate", resp) + + if err != nil { + return nil, err + } + + results := make([]CallResponse, len(caller.calls)) + for i, response := range responses[0].([]struct { + Success bool `json:"success"` + ReturnData []byte `json:"returnData"` + }) { + results[i].Method = caller.calls[i].Method + results[i].ReturnDataRaw = response.ReturnData + results[i].Status = response.Success + } + return results, nil +} + +func (caller *MultiCaller) FlexibleCall(requireSuccess bool, opts *bind.CallOpts) ([]Result, error) { + res := make([]Result, len(caller.calls)) + results, err := caller.Execute(requireSuccess, opts) + if err != nil { + caller.calls = []Call{} + return nil, err + } + for i, call := range caller.calls { + callSuccess := results[i].Status + if callSuccess { + err := call.Contract.ABI.UnpackIntoInterface(call.output, call.Method, results[i].ReturnDataRaw) + if err != nil { + caller.calls = []Call{} + return nil, err + } + } + res[i].Success = callSuccess + res[i].Output = call.output + } + caller.calls = []Call{} + return res, err +} diff --git a/bindings/utils/state/common.go b/bindings/utils/state/common.go new file mode 100644 index 000000000..5c4f83501 --- /dev/null +++ b/bindings/utils/state/common.go @@ -0,0 +1,23 @@ +package state + +import ( + "math/big" + "time" +) + +const ( + threadLimit int = 10 +) + +// Global constants +var zero = big.NewInt(0) + +// Converts a time on the chain (as Unix time in seconds) to a time.Time struct +func convertToTime(value *big.Int) time.Time { + return time.Unix(value.Int64(), 0) +} + +// Converts a duration on the chain (as a number of seconds) to a time.Duration struct +func convertToDuration(value *big.Int) time.Duration { + return time.Duration(value.Uint64()) * time.Second +} diff --git a/bindings/utils/state/contracts.go b/bindings/utils/state/contracts.go new file mode 100644 index 000000000..18bb708e1 --- /dev/null +++ b/bindings/utils/state/contracts.go @@ -0,0 +1,281 @@ +package state + +import ( + "context" + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/hashicorp/go-version" + "github.com/rocket-pool/smartnode/bindings/deposit" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/utils/multicall" +) + +// Container for network contracts +type NetworkContracts struct { + // Non-RP Utility + BalanceBatcher *multicall.BalanceBatcher + Multicaller *multicall.MultiCaller + ElBlockNumber *big.Int + + // Network version + Version *version.Version + + // Redstone + RocketDAONodeTrusted *rocketpool.Contract + RocketDAONodeTrustedSettingsMinipool *rocketpool.Contract + RocketDAOProtocolSettingsMinipool *rocketpool.Contract + RocketDAOProtocolSettingsNetwork *rocketpool.Contract + RocketDAOProtocolSettingsNode *rocketpool.Contract + RocketDepositPool *rocketpool.Contract + RocketMinipoolManager *rocketpool.Contract + RocketMinipoolQueue *rocketpool.Contract + RocketNetworkBalances *rocketpool.Contract + RocketNetworkFees *rocketpool.Contract + RocketNetworkPrices *rocketpool.Contract + RocketNodeDeposit *rocketpool.Contract + RocketNodeDistributorFactory *rocketpool.Contract + RocketNodeManager *rocketpool.Contract + RocketNodeStaking *rocketpool.Contract + RocketRewardsPool *rocketpool.Contract + RocketSmoothingPool *rocketpool.Contract + RocketStorage *rocketpool.Contract + RocketTokenRETH *rocketpool.Contract + RocketTokenRPL *rocketpool.Contract + RocketTokenRPLFixedSupply *rocketpool.Contract + + // Atlas + RocketMinipoolBondReducer *rocketpool.Contract + + // Houston + RocketDAOProtocolProposal *rocketpool.Contract + RocketDAOProtocolVerifier *rocketpool.Contract + + // Saturn + RocketMegapoolFactory *rocketpool.Contract + RocketMegapoolManager *rocketpool.Contract +} + +type contractArtifacts struct { + name string + address common.Address + abiEncoded string + contract **rocketpool.Contract +} + +// Get a new network contracts container +func NewNetworkContracts(rp *rocketpool.RocketPool, isSaturnDeployed bool, multicallerAddress common.Address, balanceBatcherAddress common.Address, opts *bind.CallOpts) (*NetworkContracts, error) { + // Get the latest block number if it's not provided + if opts == nil { + latestElBlock, err := rp.Client.BlockNumber(context.Background()) + if err != nil { + return nil, fmt.Errorf("error getting latest block number: %w", err) + } + opts = &bind.CallOpts{ + BlockNumber: big.NewInt(0).SetUint64(latestElBlock), + } + } + + // Create the contract binding + contracts := &NetworkContracts{ + RocketStorage: rp.RocketStorageContract, + ElBlockNumber: opts.BlockNumber, + } + + // Create the multicaller + var err error + contracts.Multicaller, err = multicall.NewMultiCaller(rp.Client, multicallerAddress) + if err != nil { + return nil, err + } + + // Create the balance batcher + contracts.BalanceBatcher, err = multicall.NewBalanceBatcher(rp.Client, balanceBatcherAddress) + if err != nil { + return nil, err + } + + // Create the contract wrappers for Redstone + wrappers := []contractArtifacts{ + { + name: "rocketDAONodeTrusted", + contract: &contracts.RocketDAONodeTrusted, + }, { + name: "rocketDAONodeTrustedSettingsMinipool", + contract: &contracts.RocketDAONodeTrustedSettingsMinipool, + }, { + name: "rocketDAOProtocolSettingsMinipool", + contract: &contracts.RocketDAOProtocolSettingsMinipool, + }, { + name: "rocketDAOProtocolSettingsNetwork", + contract: &contracts.RocketDAOProtocolSettingsNetwork, + }, { + name: "rocketDAOProtocolSettingsNode", + contract: &contracts.RocketDAOProtocolSettingsNode, + }, { + name: "rocketDepositPool", + contract: &contracts.RocketDepositPool, + }, { + name: "rocketMinipoolManager", + contract: &contracts.RocketMinipoolManager, + }, { + name: "rocketMinipoolQueue", + contract: &contracts.RocketMinipoolQueue, + }, { + name: "rocketNetworkBalances", + contract: &contracts.RocketNetworkBalances, + }, { + name: "rocketNetworkFees", + contract: &contracts.RocketNetworkFees, + }, { + name: "rocketNetworkPrices", + contract: &contracts.RocketNetworkPrices, + }, { + name: "rocketNodeDeposit", + contract: &contracts.RocketNodeDeposit, + }, { + name: "rocketNodeDistributorFactory", + contract: &contracts.RocketNodeDistributorFactory, + }, { + name: "rocketNodeManager", + contract: &contracts.RocketNodeManager, + }, { + name: "rocketNodeStaking", + contract: &contracts.RocketNodeStaking, + }, { + name: "rocketRewardsPool", + contract: &contracts.RocketRewardsPool, + }, { + name: "rocketSmoothingPool", + contract: &contracts.RocketSmoothingPool, + }, { + name: "rocketTokenRETH", + contract: &contracts.RocketTokenRETH, + }, { + name: "rocketTokenRPL", + contract: &contracts.RocketTokenRPL, + }, { + name: "rocketTokenRPLFixedSupply", + contract: &contracts.RocketTokenRPLFixedSupply, + }, + } + + // Atlas wrappers + wrappers = append(wrappers, contractArtifacts{ + name: "rocketMinipoolBondReducer", + contract: &contracts.RocketMinipoolBondReducer, + }) + + // Houston wrappers + wrappers = append(wrappers, contractArtifacts{ + name: "rocketDAOProtocolProposal", + contract: &contracts.RocketDAOProtocolProposal, + }, contractArtifacts{ + name: "rocketDAOProtocolVerifier", + contract: &contracts.RocketDAOProtocolVerifier, + }) + + // Saturn wrappers + if isSaturnDeployed { + wrappers = append(wrappers, contractArtifacts{ + name: "rocketMegapoolFactory", + contract: &contracts.RocketMegapoolFactory, + }, contractArtifacts{ + name: "rocketMegapoolManager", + contract: &contracts.RocketMegapoolManager, + }) + } + + // Add the address and ABI getters to multicall + for i, wrapper := range wrappers { + // Add the address getter + contracts.Multicaller.AddCall(contracts.RocketStorage, &wrappers[i].address, "getAddress", [32]byte(crypto.Keccak256Hash([]byte("contract.address"), []byte(wrapper.name)))) + + // Add the ABI getter + contracts.Multicaller.AddCall(contracts.RocketStorage, &wrappers[i].abiEncoded, "getString", [32]byte(crypto.Keccak256Hash([]byte("contract.abi"), []byte(wrapper.name)))) + } + + // Run the multi-getter + _, err = contracts.Multicaller.FlexibleCall(true, opts) + if err != nil { + return nil, fmt.Errorf("error executing multicall for contract retrieval: %w", err) + } + + // Postprocess the contracts + for i, wrapper := range wrappers { + // Decode the ABI + abi, err := rocketpool.DecodeAbi(wrapper.abiEncoded) + if err != nil { + return nil, fmt.Errorf("error decoding ABI for %s: %w", wrapper.name, err) + } + + // Create the contract binding + contract := &rocketpool.Contract{ + Contract: bind.NewBoundContract(wrapper.address, *abi, rp.Client, rp.Client, rp.Client), + Address: &wrappers[i].address, + ABI: abi, + Client: rp.Client, + } + + // Set the contract in the main wrapper object + *wrappers[i].contract = contract + } + + err = contracts.getCurrentVersion(rp) + if err != nil { + return nil, fmt.Errorf("error getting network contract version: %w", err) + } + + return contracts, nil +} + +// Returns whether or not Saturn has been deployed +func (c *NetworkContracts) isSaturnDeployed() bool { + constraint, _ := version.NewConstraint(">= 1.4.0") + return constraint.Check(c.Version) +} + +// Get the current version of the network +func (c *NetworkContracts) getCurrentVersion(rp *rocketpool.RocketPool) error { + opts := &bind.CallOpts{ + BlockNumber: c.ElBlockNumber, + } + + depositPoolVersion, err := deposit.GetRocketDepositPoolVersion(rp, opts) + if err != nil { + return fmt.Errorf("error checking deposit pool version: %w", err) + } + + // Check for v1.4 (Saturn 1) + if depositPoolVersion > 3 { + c.Version, err = version.NewSemver("1.4.0") + return err + } + + // Check for v1.2 + nodeStakingVersion, err := rocketpool.GetContractVersion(rp, *c.RocketNodeStaking.Address, opts) + if err != nil { + return fmt.Errorf("error checking node staking version: %w", err) + } + if nodeStakingVersion > 3 { + c.Version, err = version.NewSemver("1.2.0") + return err + } + + // Check for v1.1 + nodeMgrVersion, err := rocketpool.GetContractVersion(rp, *c.RocketNodeManager.Address, opts) + if err != nil { + return fmt.Errorf("error checking node manager version: %w", err) + } + if nodeMgrVersion > 1 { + c.Version, err = version.NewSemver("1.1.0") + return err + } + + // v1.0 + c.Version, err = version.NewSemver("1.0.0") + return err +} diff --git a/bindings/utils/state/megapool.go b/bindings/utils/state/megapool.go new file mode 100644 index 000000000..fa76a0243 --- /dev/null +++ b/bindings/utils/state/megapool.go @@ -0,0 +1,202 @@ +package state + +import ( + "context" + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/rocket-pool/smartnode/bindings/megapool" + "github.com/rocket-pool/smartnode/bindings/network" + "github.com/rocket-pool/smartnode/bindings/node" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "golang.org/x/sync/errgroup" +) + +const ( + megapoolValidatorsBatchSize int = 1000 +) + +type NativeMegapoolDetails struct { + Address common.Address `json:"address"` + DelegateAddress common.Address `json:"delegate"` + EffectiveDelegateAddress common.Address `json:"effectiveDelegateAddress"` + Deployed bool `json:"deployed"` + ValidatorCount uint32 `json:"validatorCount"` + ActiveValidatorCount uint32 `json:"activeValidatorCount"` + NodeDebt *big.Int `json:"nodeDebt"` + RefundValue *big.Int `json:"refundValue"` + DelegateExpiry uint64 `json:"delegateExpiry"` + DelegateExpired bool `json:"delegateExpired"` + NodeExpressTicketCount uint64 `json:"nodeExpressTicketCount"` + UseLatestDelegate bool `json:"useLatestDelegate"` + AssignedValue *big.Int `json:"assignedValue"` + NodeBond *big.Int `json:"nodeBond"` + UserCapital *big.Int `json:"userCapital"` + NodeShare *big.Int `json:"nodeShare"` + BondRequirement *big.Int `json:"bondRequirement"` + EthBalance *big.Int `json:"ethBalance"` + LastDistributionBlock uint64 `json:"lastDistributionBlock"` +} + +// Get all megapool validators using the multicaller +func GetAllMegapoolValidators(rp *rocketpool.RocketPool, contracts *NetworkContracts) ([]megapool.ValidatorInfoFromGlobalIndex, error) { + opts := &bind.CallOpts{ + BlockNumber: contracts.ElBlockNumber, + } + + // Get megapool validators count + megapoolValidatorsCount, err := megapool.GetValidatorCount(rp, opts) + if err != nil { + return []megapool.ValidatorInfoFromGlobalIndex{}, err + } + + // Sync + var wg errgroup.Group + wg.SetLimit(threadLimit) + validators := make([]megapool.ValidatorInfoFromGlobalIndex, megapoolValidatorsCount) + + // Run the getters in batches + count := int(megapoolValidatorsCount) + for i := 0; i < count; i += megapoolValidatorsBatchSize { + i := i + max := i + megapoolValidatorsBatchSize + if max > count { + max = count + } + + for j := i; j < max; j++ { + j := j // Create a new variable `j` scoped to the loop iteration + wg.Go(func() error { + validators[j], err = megapool.GetValidatorInfo(rp, uint32(j), opts) + if err != nil { + return fmt.Errorf("error executing GetValidatorInfo with global index %d", j) + } + return nil + }) + } + } + + if err := wg.Wait(); err != nil { + return nil, fmt.Errorf("error getting all megapool validators: %w", err) + } + + return validators, nil +} + +func GetNodeMegapoolDetails(rp *rocketpool.RocketPool, nodeAccount common.Address) (NativeMegapoolDetails, error) { + + megapoolAddress, err := megapool.GetMegapoolExpectedAddress(rp, nodeAccount, nil) + if err != nil { + return NativeMegapoolDetails{}, err + } + + // Sync + var wg errgroup.Group + details := NativeMegapoolDetails{Address: megapoolAddress} + + // Return if megapool isn't deployed + details.Deployed, err = megapool.GetMegapoolDeployed(rp, nodeAccount, nil) + if err != nil { + return NativeMegapoolDetails{}, err + } + if !details.Deployed { + return details, nil + } + + // Load the megapool contract + mega, err := megapool.NewMegaPoolV1(rp, megapoolAddress, nil) + if err != nil { + return NativeMegapoolDetails{}, err + } + + details.EffectiveDelegateAddress, err = mega.GetEffectiveDelegate(nil) + if err != nil { + return NativeMegapoolDetails{}, err + } + details.DelegateAddress, err = mega.GetDelegate(nil) + if err != nil { + return NativeMegapoolDetails{}, err + } + + // Return if delegate is expired + details.DelegateExpired, err = mega.GetDelegateExpired(rp, nil) + if err != nil { + return NativeMegapoolDetails{}, err + } + if details.DelegateExpired { + return details, nil + } + + details.LastDistributionBlock, err = mega.GetLastDistributionBlock(nil) + if err != nil { + return NativeMegapoolDetails{}, err + } + wg.Go(func() error { + var err error + details.NodeShare, err = network.GetCurrentNodeShare(rp, nil) + return err + }) + wg.Go(func() error { + var err error + details.NodeDebt, err = mega.GetDebt(nil) + return err + }) + wg.Go(func() error { + var err error + details.RefundValue, err = mega.GetRefundValue(nil) + return err + }) + wg.Go(func() error { + var err error + details.ValidatorCount, err = mega.GetValidatorCount(nil) + return err + }) + wg.Go(func() error { + var err error + details.ActiveValidatorCount, err = mega.GetActiveValidatorCount(nil) + return err + }) + wg.Go(func() error { + var err error + details.UseLatestDelegate, err = mega.GetUseLatestDelegate(nil) + return err + }) + wg.Go(func() error { + var err error + details.DelegateExpiry, err = megapool.GetMegapoolDelegateExpiry(rp, details.DelegateAddress, nil) + return err + }) + wg.Go(func() error { + var err error + details.AssignedValue, err = mega.GetAssignedValue(nil) + return err + }) + wg.Go(func() error { + var err error + details.NodeBond, err = mega.GetNodeBond(nil) + return err + }) + wg.Go(func() error { + var err error + details.UserCapital, err = mega.GetUserCapital(nil) + return err + }) + wg.Go(func() error { + var err error + details.EthBalance, err = rp.Client.BalanceAt(context.Background(), details.Address, nil) + return err + }) + + // Wait for data + if err := wg.Wait(); err != nil { + return details, err + } + + details.BondRequirement, err = node.GetBondRequirement(rp, big.NewInt(int64(details.ActiveValidatorCount)), nil) + if err != nil { + return details, err + } + return details, nil +} diff --git a/bindings/utils/state/minipool.go b/bindings/utils/state/minipool.go new file mode 100644 index 000000000..1491d986d --- /dev/null +++ b/bindings/utils/state/minipool.go @@ -0,0 +1,602 @@ +package state + +import ( + "fmt" + "math/big" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/rocket-pool/smartnode/bindings/minipool" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/types" + "github.com/rocket-pool/smartnode/bindings/utils/multicall" + "golang.org/x/sync/errgroup" +) + +const ( + minipoolBatchSize int = 100 + minipoolCompleteShareBatchSize int = 500 + minipoolAddressBatchSize int = 1000 + minipoolVersionBatchSize int = 500 +) + +// Complete details for a minipool +type NativeMinipoolDetails struct { + // Redstone + Exists bool `json:"exists"` + MinipoolAddress common.Address `json:"minipool_address"` + Pubkey types.ValidatorPubkey `json:"pubkey"` + StatusRaw uint8 `json:"status_raw"` + StatusBlock *big.Int `json:"status_block"` + StatusTime *big.Int `json:"status_time"` + Finalised bool `json:"finalised"` + DepositTypeRaw uint8 `json:"deposit_type_raw"` + NodeFee *big.Int `json:"node_fee"` + NodeDepositBalance *big.Int `json:"node_deposit_balance"` + NodeDepositAssigned bool `json:"node_deposit_assigned"` + UserDepositBalance *big.Int `json:"user_deposit_balance"` + UserDepositAssigned bool `json:"user_deposit_assigned"` + UserDepositAssignedTime *big.Int `json:"user_deposit_assigned_time"` + UseLatestDelegate bool `json:"use_latest_delegate"` + Delegate common.Address `json:"delegate"` + PreviousDelegate common.Address `json:"previous_delegate"` + EffectiveDelegate common.Address `json:"effective_delegate"` + PenaltyCount *big.Int `json:"penalty_count"` + PenaltyRate *big.Int `json:"penalty_rate"` + NodeAddress common.Address `json:"node_address"` + Version uint8 `json:"version"` + Balance *big.Int `json:"balance"` + DistributableBalance *big.Int `json:"distributable_balance"` + NodeShareOfBalance *big.Int `json:"node_share_of_balance"` // Result of calculateNodeShare(contract balance) + UserShareOfBalance *big.Int `json:"user_share_of_balance"` // Result of calculateUserShare(contract balance) + NodeRefundBalance *big.Int `json:"node_refund_balance"` + WithdrawalCredentials common.Hash `json:"withdrawal_credentials"` + Status types.MinipoolStatus `json:"status"` + DepositType types.MinipoolDeposit `json:"deposit_type"` + + // Must call CalculateCompleteMinipoolShares to get these + NodeShareOfBalanceIncludingBeacon *big.Int `json:"node_share_of_balance_including_beacon"` + UserShareOfBalanceIncludingBeacon *big.Int `json:"user_share_of_balance_including_beacon"` + NodeShareOfBeaconBalance *big.Int `json:"node_share_of_beacon_balance"` + UserShareOfBeaconBalance *big.Int `json:"user_share_of_beacon_balance"` + + // Atlas + UserDistributed bool + Slashed bool + IsVacant bool + LastBondReductionTime *big.Int + LastBondReductionPrevValue *big.Int + LastBondReductionPrevNodeFee *big.Int + ReduceBondTime *big.Int + ReduceBondCancelled bool + ReduceBondValue *big.Int + PreMigrationBalance *big.Int +} + +var sixteenEth = big.NewInt(0).Mul(big.NewInt(16), oneEth) + +func (details *NativeMinipoolDetails) IsEligibleForBonuses(eligibleEnd time.Time) bool { + // A minipool is eligible for bonuses if it was active and had a bond of less than 16 ETH during the interval + if details.Status != types.Staking { + return false + } + if details.NodeDepositBalance.Cmp(sixteenEth) >= 0 { + return false + } + + lastBondReductionTimestamp := details.LastBondReductionTime.Int64() + if lastBondReductionTimestamp == 0 { + // eligible if the bond was always under 16 eth + return true + } + lastBondReductionTime := time.Unix(lastBondReductionTimestamp, 0) + // eligible if the bond was reduced before or during the interval + return lastBondReductionTime.Before(eligibleEnd) +} + +// Gets the details for a minipool using the efficient multicall contract +func GetNativeMinipoolDetails(rp *rocketpool.RocketPool, contracts *NetworkContracts, minipoolAddress common.Address) (NativeMinipoolDetails, error) { + opts := &bind.CallOpts{ + BlockNumber: contracts.ElBlockNumber, + } + + details := NativeMinipoolDetails{} + details.MinipoolAddress = minipoolAddress + + version, err := rocketpool.GetContractVersion(rp, minipoolAddress, opts) + if err != nil { + return NativeMinipoolDetails{}, fmt.Errorf("error getting minipool version: %w", err) + } + details.Version = version + addMinipoolDetailsCalls(rp, contracts, contracts.Multicaller, &details, opts) + + _, err = contracts.Multicaller.FlexibleCall(true, opts) + if err != nil { + return NativeMinipoolDetails{}, fmt.Errorf("error executing multicall: %w", err) + } + + fixupMinipoolDetails(&details) + + return details, nil +} + +// Gets the minpool details for a node using the efficient multicall contract +func GetNodeNativeMinipoolDetails(rp *rocketpool.RocketPool, contracts *NetworkContracts, nodeAddress common.Address) ([]NativeMinipoolDetails, error) { + opts := &bind.CallOpts{ + BlockNumber: contracts.ElBlockNumber, + } + + // Get the list of minipool addresses for this node + addresses, err := getNodeMinipoolAddressesFast(rp, contracts, nodeAddress, opts) + if err != nil { + return nil, fmt.Errorf("error getting minipool addresses: %w", err) + } + + // Get the list of minipool versions + versions, err := getMinipoolVersionsFast(rp, contracts, addresses, opts) + if err != nil { + return nil, fmt.Errorf("error getting minipool versions: %w", err) + } + + // Get the minipool details + return getBulkMinipoolDetails(rp, contracts, addresses, versions, opts) +} + +// Gets all minpool details using the efficient multicall contract +func GetAllNativeMinipoolDetails(rp *rocketpool.RocketPool, contracts *NetworkContracts) ([]NativeMinipoolDetails, error) { + opts := &bind.CallOpts{ + BlockNumber: contracts.ElBlockNumber, + } + + // Get the list of all minipool addresses + addresses, err := getAllMinipoolAddressesFast(rp, contracts, opts) + if err != nil { + return nil, fmt.Errorf("error getting minipool addresses: %w", err) + } + + // Get the list of minipool versions + versions, err := getMinipoolVersionsFast(rp, contracts, addresses, opts) + if err != nil { + return nil, fmt.Errorf("error getting minipool versions: %w", err) + } + + // Get the minipool details + return getBulkMinipoolDetails(rp, contracts, addresses, versions, opts) +} + +// Calculate the node and user shares of the total minipool balance, including the portion on the Beacon chain +func CalculateCompleteMinipoolShares(rp *rocketpool.RocketPool, contracts *NetworkContracts, minipoolDetails []*NativeMinipoolDetails, beaconBalances []*big.Int) error { + opts := &bind.CallOpts{ + BlockNumber: contracts.ElBlockNumber, + } + + var wg errgroup.Group + wg.SetLimit(threadLimit) + count := len(minipoolDetails) + for i := 0; i < count; i += minipoolCompleteShareBatchSize { + i := i + max := i + minipoolCompleteShareBatchSize + if max > count { + max = count + } + + wg.Go(func() error { + var err error + mc, err := multicall.NewMultiCaller(rp.Client, contracts.Multicaller.ContractAddress) + if err != nil { + return err + } + for j := i; j < max; j++ { + + // Make the minipool contract + details := minipoolDetails[j] + mp, err := minipool.NewMinipoolFromVersion(rp, details.MinipoolAddress, details.Version, opts) + if err != nil { + return err + } + mpContract := mp.GetContract() + + // Calculate the Beacon shares + beaconBalance := big.NewInt(0).Set(beaconBalances[j]) + if beaconBalance.Cmp(zero) > 0 { + mc.AddCall(mpContract, &details.NodeShareOfBeaconBalance, "calculateNodeShare", beaconBalance) + mc.AddCall(mpContract, &details.UserShareOfBeaconBalance, "calculateUserShare", beaconBalance) + } else { + details.NodeShareOfBeaconBalance = big.NewInt(0) + details.UserShareOfBeaconBalance = big.NewInt(0) + } + + // Calculate the total balance + totalBalance := big.NewInt(0).Set(beaconBalances[j]) // Total balance = beacon balance + totalBalance.Add(totalBalance, details.Balance) // Add contract balance + totalBalance.Sub(totalBalance, details.NodeRefundBalance) // Remove node refund + + // Calculate the node and user shares + if totalBalance.Cmp(zero) > 0 { + mc.AddCall(mpContract, &details.NodeShareOfBalanceIncludingBeacon, "calculateNodeShare", totalBalance) + mc.AddCall(mpContract, &details.UserShareOfBalanceIncludingBeacon, "calculateUserShare", totalBalance) + } else { + details.NodeShareOfBalanceIncludingBeacon = big.NewInt(0) + details.UserShareOfBalanceIncludingBeacon = big.NewInt(0) + } + } + _, err = mc.FlexibleCall(true, opts) + if err != nil { + return fmt.Errorf("error executing multicall: %w", err) + } + + return nil + }) + } + + if err := wg.Wait(); err != nil { + return fmt.Errorf("error calculating minipool shares: %w", err) + } + + return nil +} + +var oneEth = big.NewInt(1e18) + +// Get the bond and node fee of a minipool for the specified time +func (details *NativeMinipoolDetails) GetMinipoolBondAndNodeFee(blockTime time.Time) (*big.Int, *big.Int) { + currentBond := details.NodeDepositBalance + currentFee := details.NodeFee + previousBond := details.LastBondReductionPrevValue + previousFee := details.LastBondReductionPrevNodeFee + + var reductionTimeBig *big.Int = details.LastBondReductionTime + if reductionTimeBig.Cmp(common.Big0) == 0 { + // Never reduced + return currentBond, currentFee + } + + reductionTime := time.Unix(reductionTimeBig.Int64(), 0) + if reductionTime.Sub(blockTime) > 0 { + // This block occurred before the reduction + if previousFee.Cmp(common.Big0) == 0 { + // Catch for minipools that were created before this call existed + return previousBond, currentFee + } + return previousBond, previousFee + } + + return currentBond, currentFee +} + +// Get all minipool addresses using the multicaller +func getNodeMinipoolAddressesFast(rp *rocketpool.RocketPool, contracts *NetworkContracts, nodeAddress common.Address, opts *bind.CallOpts) ([]common.Address, error) { + // Get minipool count + minipoolCount, err := minipool.GetNodeMinipoolCount(rp, nodeAddress, opts) + if err != nil { + return []common.Address{}, err + } + + // Sync + var wg errgroup.Group + wg.SetLimit(threadLimit) + addresses := make([]common.Address, minipoolCount) + + // Run the getters in batches + count := int(minipoolCount) + for i := 0; i < count; i += minipoolAddressBatchSize { + i := i + max := i + minipoolAddressBatchSize + if max > count { + max = count + } + + wg.Go(func() error { + var err error + mc, err := multicall.NewMultiCaller(rp.Client, contracts.Multicaller.ContractAddress) + if err != nil { + return err + } + for j := i; j < max; j++ { + mc.AddCall(contracts.RocketMinipoolManager, &addresses[j], "getNodeMinipoolAt", nodeAddress, big.NewInt(int64(j))) + } + _, err = mc.FlexibleCall(true, opts) + if err != nil { + return fmt.Errorf("error executing multicall: %w", err) + } + return nil + }) + } + + if err := wg.Wait(); err != nil { + return nil, fmt.Errorf("error getting minipool addresses for node %s: %w", nodeAddress.Hex(), err) + } + + return addresses, nil +} + +// Get all minipool addresses using the multicaller +func getAllMinipoolAddressesFast(rp *rocketpool.RocketPool, contracts *NetworkContracts, opts *bind.CallOpts) ([]common.Address, error) { + // Get minipool count + minipoolCount, err := minipool.GetMinipoolCount(rp, opts) + if err != nil { + return []common.Address{}, err + } + + // Sync + var wg errgroup.Group + wg.SetLimit(threadLimit) + addresses := make([]common.Address, minipoolCount) + + // Run the getters in batches + count := int(minipoolCount) + for i := 0; i < count; i += minipoolAddressBatchSize { + i := i + max := i + minipoolAddressBatchSize + if max > count { + max = count + } + + wg.Go(func() error { + var err error + mc, err := multicall.NewMultiCaller(rp.Client, contracts.Multicaller.ContractAddress) + if err != nil { + return err + } + for j := i; j < max; j++ { + mc.AddCall(contracts.RocketMinipoolManager, &addresses[j], "getMinipoolAt", big.NewInt(int64(j))) + } + _, err = mc.FlexibleCall(true, opts) + if err != nil { + return fmt.Errorf("error executing multicall: %w", err) + } + return nil + }) + } + + if err := wg.Wait(); err != nil { + return nil, fmt.Errorf("error getting all minipool addresses: %w", err) + } + + return addresses, nil +} + +// Get minipool versions using the multicaller +func getMinipoolVersionsFast(rp *rocketpool.RocketPool, contracts *NetworkContracts, addresses []common.Address, opts *bind.CallOpts) ([]uint8, error) { + // Sync + var wg errgroup.Group + wg.SetLimit(threadLimit) + + // Run the getters in batches + count := len(addresses) + versions := make([]uint8, count) + for i := 0; i < count; i += minipoolVersionBatchSize { + i := i + max := i + minipoolVersionBatchSize + if max > count { + max = count + } + + wg.Go(func() error { + var err error + mc, err := multicall.NewMultiCaller(rp.Client, contracts.Multicaller.ContractAddress) + if err != nil { + return err + } + for j := i; j < max; j++ { + contract, err := rocketpool.GetRocketVersionContractForAddress(rp, addresses[j]) + if err != nil { + return fmt.Errorf("error creating version contract for minipool %s: %w", addresses[j].Hex(), err) + } + mc.AddCall(contract, &versions[j], "version") + } + results, err := mc.FlexibleCall(false, opts) // Allow calls to fail - necessary for Prater + for j, result := range results { + if !result.Success { + versions[j+i] = 1 // Anything that failed the version check didn't have the method yet so it must be v1 + } + } + if err != nil { + return fmt.Errorf("error executing multicall: %w", err) + } + return nil + }) + } + + if err := wg.Wait(); err != nil { + return nil, fmt.Errorf("error getting minipool versions: %w", err) + } + + return versions, nil +} + +// Get multiple minipool details at once +func getBulkMinipoolDetails(rp *rocketpool.RocketPool, contracts *NetworkContracts, addresses []common.Address, versions []uint8, opts *bind.CallOpts) ([]NativeMinipoolDetails, error) { + minipoolDetails := make([]NativeMinipoolDetails, len(addresses)) + + // Get the balances of the minipools + balances, err := contracts.BalanceBatcher.GetEthBalances(addresses, opts) + if err != nil { + return nil, fmt.Errorf("error getting minipool balances: %w", err) + } + for i := range minipoolDetails { + minipoolDetails[i].Balance = balances[i] + } + + // Round 1: most of the details + var wg errgroup.Group + wg.SetLimit(threadLimit) + count := len(addresses) + for i := 0; i < count; i += minipoolBatchSize { + i := i + max := i + minipoolBatchSize + if max > count { + max = count + } + + wg.Go(func() error { + var err error + mc, err := multicall.NewMultiCaller(rp.Client, contracts.Multicaller.ContractAddress) + if err != nil { + return err + } + for j := i; j < max; j++ { + + address := addresses[j] + details := &minipoolDetails[j] + details.MinipoolAddress = address + details.Version = versions[j] + + addMinipoolDetailsCalls(rp, contracts, mc, details, opts) + } + _, err = mc.FlexibleCall(true, opts) + if err != nil { + return fmt.Errorf("error executing multicall: %w", err) + } + + return nil + }) + } + + if err := wg.Wait(); err != nil { + return nil, fmt.Errorf("error getting minipool details r1: %w", err) + } + + // Round 2: NodeShare and UserShare once the refund amount has been populated + var wg2 errgroup.Group + wg2.SetLimit(threadLimit) + for i := 0; i < count; i += minipoolBatchSize { + i := i + max := i + minipoolBatchSize + if max > count { + max = count + } + + wg2.Go(func() error { + var err error + mc, err := multicall.NewMultiCaller(rp.Client, contracts.Multicaller.ContractAddress) + if err != nil { + return err + } + for j := i; j < max; j++ { + details := &minipoolDetails[j] + details.Version = versions[j] + addMinipoolShareCalls(rp, mc, details, opts) + } + _, err = mc.FlexibleCall(true, opts) + if err != nil { + return fmt.Errorf("error executing multicall: %w", err) + } + + return nil + }) + } + + if err := wg2.Wait(); err != nil { + return nil, fmt.Errorf("error getting minipool details r2: %w", err) + } + + // Postprocess the minipools + for i := range minipoolDetails { + fixupMinipoolDetails(&minipoolDetails[i]) + } + + return minipoolDetails, nil +} + +// Add all of the calls for the minipool details to the multicaller +func addMinipoolDetailsCalls(rp *rocketpool.RocketPool, contracts *NetworkContracts, mc *multicall.MultiCaller, details *NativeMinipoolDetails, opts *bind.CallOpts) error { + // Create the minipool contract binding + address := details.MinipoolAddress + mp, err := minipool.NewMinipoolFromVersion(rp, address, details.Version, opts) + if err != nil { + return err + } + mpContract := mp.GetContract() + + details.Version = mp.GetVersion() + mc.AddCall(contracts.RocketMinipoolManager, &details.Exists, "getMinipoolExists", address) + mc.AddCall(contracts.RocketMinipoolManager, &details.Pubkey, "getMinipoolPubkey", address) + mc.AddCall(contracts.RocketMinipoolManager, &details.WithdrawalCredentials, "getMinipoolWithdrawalCredentials", address) + mc.AddCall(contracts.RocketMinipoolManager, &details.Slashed, "getMinipoolRPLSlashed", address) + mc.AddCall(mpContract, &details.StatusRaw, "getStatus") + mc.AddCall(mpContract, &details.StatusBlock, "getStatusBlock") + mc.AddCall(mpContract, &details.StatusTime, "getStatusTime") + mc.AddCall(mpContract, &details.Finalised, "getFinalised") + mc.AddCall(mpContract, &details.NodeFee, "getNodeFee") + mc.AddCall(mpContract, &details.NodeDepositBalance, "getNodeDepositBalance") + mc.AddCall(mpContract, &details.NodeDepositAssigned, "getNodeDepositAssigned") + mc.AddCall(mpContract, &details.UserDepositBalance, "getUserDepositBalance") + mc.AddCall(mpContract, &details.UserDepositAssigned, "getUserDepositAssigned") + mc.AddCall(mpContract, &details.UserDepositAssignedTime, "getUserDepositAssignedTime") + mc.AddCall(mpContract, &details.UseLatestDelegate, "getUseLatestDelegate") + mc.AddCall(mpContract, &details.Delegate, "getDelegate") + mc.AddCall(mpContract, &details.PreviousDelegate, "getPreviousDelegate") + mc.AddCall(mpContract, &details.EffectiveDelegate, "getEffectiveDelegate") + mc.AddCall(mpContract, &details.NodeAddress, "getNodeAddress") + mc.AddCall(mpContract, &details.NodeRefundBalance, "getNodeRefundBalance") + + if details.Version < 3 { + // These fields are all v3+ only + details.UserDistributed = false + details.LastBondReductionTime = big.NewInt(0) + details.LastBondReductionPrevValue = big.NewInt(0) + details.LastBondReductionPrevNodeFee = big.NewInt(0) + details.IsVacant = false + details.ReduceBondTime = big.NewInt(0) + details.ReduceBondCancelled = false + details.ReduceBondValue = big.NewInt(0) + details.PreMigrationBalance = big.NewInt(0) + } else { + mc.AddCall(mpContract, &details.UserDistributed, "getUserDistributed") + mc.AddCall(mpContract, &details.IsVacant, "getVacant") + mc.AddCall(mpContract, &details.PreMigrationBalance, "getPreMigrationBalance") + + // If minipool v3 exists, RocketMinipoolBondReducer exists so this is safe + mc.AddCall(contracts.RocketMinipoolBondReducer, &details.ReduceBondTime, "getReduceBondTime", address) + mc.AddCall(contracts.RocketMinipoolBondReducer, &details.ReduceBondCancelled, "getReduceBondCancelled", address) + mc.AddCall(contracts.RocketMinipoolBondReducer, &details.LastBondReductionTime, "getLastBondReductionTime", address) + mc.AddCall(contracts.RocketMinipoolBondReducer, &details.LastBondReductionPrevValue, "getLastBondReductionPrevValue", address) + mc.AddCall(contracts.RocketMinipoolBondReducer, &details.LastBondReductionPrevNodeFee, "getLastBondReductionPrevNodeFee", address) + mc.AddCall(contracts.RocketMinipoolBondReducer, &details.ReduceBondValue, "getReduceBondValue", address) + } + + penaltyCountKey := crypto.Keccak256Hash([]byte("network.penalties.penalty"), address.Bytes()) + mc.AddCall(contracts.RocketStorage, &details.PenaltyCount, "getUint", penaltyCountKey) + + penaltyRatekey := crypto.Keccak256Hash([]byte("minipool.penalty.rate"), address.Bytes()) + mc.AddCall(contracts.RocketStorage, &details.PenaltyRate, "getUint", penaltyRatekey) + + // Query the minipool manager using the delegate-invariant function + mc.AddCall(contracts.RocketMinipoolManager, &details.DepositTypeRaw, "getMinipoolDepositType", address) + + return nil +} + +// Add the calls for the minipool node and user share to the multicaller +func addMinipoolShareCalls(rp *rocketpool.RocketPool, mc *multicall.MultiCaller, details *NativeMinipoolDetails, opts *bind.CallOpts) error { + // Create the minipool contract binding + address := details.MinipoolAddress + mp, err := minipool.NewMinipoolFromVersion(rp, address, details.Version, opts) + if err != nil { + return err + } + mpContract := mp.GetContract() + + details.DistributableBalance = big.NewInt(0).Sub(details.Balance, details.NodeRefundBalance) + if details.DistributableBalance.Cmp(zero) >= 0 { + mc.AddCall(mpContract, &details.NodeShareOfBalance, "calculateNodeShare", details.DistributableBalance) + mc.AddCall(mpContract, &details.UserShareOfBalance, "calculateUserShare", details.DistributableBalance) + } else { + details.NodeShareOfBalance = big.NewInt(0) + details.UserShareOfBalance = big.NewInt(0) + } + + return nil +} + +// Fixes a minipool details struct with supplemental logic +func fixupMinipoolDetails(details *NativeMinipoolDetails) error { + + details.Status = types.MinipoolStatus(details.StatusRaw) + details.DepositType = types.MinipoolDeposit(details.DepositTypeRaw) + + return nil +} diff --git a/bindings/utils/state/network.go b/bindings/utils/state/network.go new file mode 100644 index 000000000..d49c3abff --- /dev/null +++ b/bindings/utils/state/network.go @@ -0,0 +1,243 @@ +package state + +import ( + "fmt" + "math/big" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/rocket-pool/smartnode/bindings/minipool" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/utils/eth" + "github.com/rocket-pool/smartnode/bindings/utils/multicall" + "golang.org/x/sync/errgroup" +) + +const ( + networkEffectiveStakeBatchSize int = 250 +) + +type NetworkDetails struct { + // Redstone + RplPrice *big.Int `json:"rpl_price"` + MinCollateralFraction *big.Int `json:"min_collateral_fraction"` + MaxCollateralFraction *big.Int `json:"max_collateral_fraction"` + IntervalDuration time.Duration `json:"interval_duration"` + IntervalStart time.Time `json:"interval_start"` + NodeOperatorRewardsPercent *big.Int `json:"node_operator_rewards_percent"` + TrustedNodeOperatorRewardsPercent *big.Int `json:"trusted_node_operator_rewards_percent"` + ProtocolDaoRewardsPercent *big.Int `json:"protocol_dao_rewards_percent"` + PendingRPLRewards *big.Int `json:"pending_rpl_rewards"` + RewardIndex uint64 `json:"reward_index"` + ScrubPeriod time.Duration `json:"scrub_period"` + SmoothingPoolAddress common.Address `json:"smoothing_pool_address"` + DepositPoolBalance *big.Int `json:"deposit_pool_balance"` + DepositPoolExcess *big.Int `json:"deposit_pool_excess"` + QueueCapacity minipool.QueueCapacity `json:"queue_capacity"` + QueueLength *big.Int `json:"queue_length"` + RPLInflationIntervalRate *big.Int `json:"rpl_inflation_interval_rate"` + RPLTotalSupply *big.Int `json:"rpl_total_supply"` + PricesBlock uint64 `json:"prices_block"` + LatestReportablePricesBlock uint64 `json:"latest_reportable_prices_block"` + ETHUtilizationRate float64 `json:"eth_utilization_rate"` + StakingETHBalance *big.Int `json:"staking_eth_balance"` + RETHExchangeRate float64 `json:"reth_exchange_rate"` + TotalETHBalance *big.Int `json:"total_eth_balance"` + RETHBalance *big.Int `json:"reth_balance"` + TotalRETHSupply *big.Int `json:"total_reth_supply"` + TotalRPLStake *big.Int `json:"total_rpl_stake"` + SmoothingPoolBalance *big.Int `json:"smoothing_pool_balance"` + NodeFee float64 `json:"node_fee"` + BalancesBlock uint64 `json:"balances_block"` + LatestReportableBalancesBlock uint64 `json:"latest_reportable_balances_block"` + SubmitBalancesEnabled bool `json:"submit_balances_enabled"` + SubmitPricesEnabled bool `json:"submit_prices_enabled"` + MinipoolLaunchTimeout *big.Int `json:"minipool_launch_timeout"` + + // Atlas + PromotionScrubPeriod time.Duration `json:"promotion_scrub_period"` + BondReductionWindowStart time.Duration `json:"bond_reduction_window_start"` + BondReductionWindowLength time.Duration `json:"bond_reduction_window_length"` + DepositPoolUserBalance *big.Int `json:"deposit_pool_user_balance"` + + // Houston + PricesSubmissionFrequency uint64 `json:"prices_submission_frequency"` + BalancesSubmissionFrequency uint64 `json:"balances_submission_frequency"` +} + +// Create a snapshot of all of the network's details +func NewNetworkDetails(rp *rocketpool.RocketPool, contracts *NetworkContracts) (*NetworkDetails, error) { + opts := &bind.CallOpts{ + BlockNumber: contracts.ElBlockNumber, + } + + details := &NetworkDetails{} + + // Local vars for things that need to be converted + var rewardIndex *big.Int + var intervalStart *big.Int + var intervalDuration *big.Int + var scrubPeriodSeconds *big.Int + var totalQueueCapacity *big.Int + var effectiveQueueCapacity *big.Int + var totalQueueLength *big.Int + var pricesBlock *big.Int + var pricesSubmissionFrequency *big.Int + var ethUtilizationRate *big.Int + var rETHExchangeRate *big.Int + var nodeFee *big.Int + var balancesBlock *big.Int + var balancesSubmissionFrequency *big.Int + var minipoolLaunchTimeout *big.Int + var promotionScrubPeriodSeconds *big.Int + var windowStartRaw *big.Int + var windowLengthRaw *big.Int + + // Multicall getters + contracts.Multicaller.AddCall(contracts.RocketNetworkPrices, &details.RplPrice, "getRPLPrice") + contracts.Multicaller.AddCall(contracts.RocketDAOProtocolSettingsNode, &details.MinCollateralFraction, "getMinimumPerMinipoolStake") + contracts.Multicaller.AddCall(contracts.RocketDAOProtocolSettingsNode, &details.MaxCollateralFraction, "getMaximumPerMinipoolStake") + contracts.Multicaller.AddCall(contracts.RocketRewardsPool, &rewardIndex, "getRewardIndex") + contracts.Multicaller.AddCall(contracts.RocketRewardsPool, &intervalStart, "getClaimIntervalTimeStart") + contracts.Multicaller.AddCall(contracts.RocketRewardsPool, &intervalDuration, "getClaimIntervalTime") + contracts.Multicaller.AddCall(contracts.RocketRewardsPool, &details.NodeOperatorRewardsPercent, "getClaimingContractPerc", "rocketClaimNode") + contracts.Multicaller.AddCall(contracts.RocketRewardsPool, &details.TrustedNodeOperatorRewardsPercent, "getClaimingContractPerc", "rocketClaimTrustedNode") + contracts.Multicaller.AddCall(contracts.RocketRewardsPool, &details.ProtocolDaoRewardsPercent, "getClaimingContractPerc", "rocketClaimDAO") + contracts.Multicaller.AddCall(contracts.RocketRewardsPool, &details.PendingRPLRewards, "getPendingRPLRewards") + contracts.Multicaller.AddCall(contracts.RocketDAONodeTrustedSettingsMinipool, &scrubPeriodSeconds, "getScrubPeriod") + contracts.Multicaller.AddCall(contracts.RocketDepositPool, &details.DepositPoolBalance, "getBalance") + contracts.Multicaller.AddCall(contracts.RocketDepositPool, &details.DepositPoolExcess, "getExcessBalance") + contracts.Multicaller.AddCall(contracts.RocketMinipoolQueue, &totalQueueCapacity, "getTotalCapacity") + contracts.Multicaller.AddCall(contracts.RocketMinipoolQueue, &effectiveQueueCapacity, "getEffectiveCapacity") + contracts.Multicaller.AddCall(contracts.RocketMinipoolQueue, &totalQueueLength, "getTotalLength") + contracts.Multicaller.AddCall(contracts.RocketTokenRPL, &details.RPLInflationIntervalRate, "getInflationIntervalRate") + contracts.Multicaller.AddCall(contracts.RocketTokenRPL, &details.RPLTotalSupply, "totalSupply") + contracts.Multicaller.AddCall(contracts.RocketNetworkPrices, &pricesBlock, "getPricesBlock") + contracts.Multicaller.AddCall(contracts.RocketNetworkBalances, ðUtilizationRate, "getETHUtilizationRate") + contracts.Multicaller.AddCall(contracts.RocketNetworkBalances, &details.StakingETHBalance, "getStakingETHBalance") + contracts.Multicaller.AddCall(contracts.RocketTokenRETH, &rETHExchangeRate, "getExchangeRate") + contracts.Multicaller.AddCall(contracts.RocketNetworkBalances, &details.TotalETHBalance, "getTotalETHBalance") + contracts.Multicaller.AddCall(contracts.RocketTokenRETH, &details.TotalRETHSupply, "totalSupply") + contracts.Multicaller.AddCall(contracts.RocketNodeStaking, &details.TotalRPLStake, "getTotalRPLStake") + contracts.Multicaller.AddCall(contracts.RocketNetworkFees, &nodeFee, "getNodeFee") + contracts.Multicaller.AddCall(contracts.RocketNetworkBalances, &balancesBlock, "getBalancesBlock") + contracts.Multicaller.AddCall(contracts.RocketDAOProtocolSettingsNetwork, &details.SubmitBalancesEnabled, "getSubmitBalancesEnabled") + contracts.Multicaller.AddCall(contracts.RocketDAOProtocolSettingsNetwork, &details.SubmitPricesEnabled, "getSubmitPricesEnabled") + contracts.Multicaller.AddCall(contracts.RocketDAOProtocolSettingsMinipool, &minipoolLaunchTimeout, "getLaunchTimeout") + + // Atlas things + contracts.Multicaller.AddCall(contracts.RocketDAONodeTrustedSettingsMinipool, &promotionScrubPeriodSeconds, "getPromotionScrubPeriod") + contracts.Multicaller.AddCall(contracts.RocketDAONodeTrustedSettingsMinipool, &windowStartRaw, "getBondReductionWindowStart") + contracts.Multicaller.AddCall(contracts.RocketDAONodeTrustedSettingsMinipool, &windowLengthRaw, "getBondReductionWindowLength") + contracts.Multicaller.AddCall(contracts.RocketDepositPool, &details.DepositPoolUserBalance, "getUserBalance") + + // Houston + contracts.Multicaller.AddCall(contracts.RocketDAOProtocolSettingsNetwork, &pricesSubmissionFrequency, "getSubmitPricesFrequency") + contracts.Multicaller.AddCall(contracts.RocketDAOProtocolSettingsNetwork, &balancesSubmissionFrequency, "getSubmitBalancesFrequency") + + _, err := contracts.Multicaller.FlexibleCall(true, opts) + if err != nil { + return nil, fmt.Errorf("error executing multicall: %w", err) + } + + // Conversion for raw parameters + details.RewardIndex = rewardIndex.Uint64() + details.IntervalStart = convertToTime(intervalStart) + details.IntervalDuration = convertToDuration(intervalDuration) + details.ScrubPeriod = convertToDuration(scrubPeriodSeconds) + details.SmoothingPoolAddress = *contracts.RocketSmoothingPool.Address + details.QueueCapacity = minipool.QueueCapacity{ + Total: totalQueueCapacity, + Effective: effectiveQueueCapacity, + } + details.QueueLength = totalQueueLength + details.PricesBlock = pricesBlock.Uint64() + + details.PricesSubmissionFrequency = pricesSubmissionFrequency.Uint64() + details.BalancesSubmissionFrequency = balancesSubmissionFrequency.Uint64() + details.ETHUtilizationRate = eth.WeiToEth(ethUtilizationRate) + details.RETHExchangeRate = eth.WeiToEth(rETHExchangeRate) + details.NodeFee = eth.WeiToEth(nodeFee) + details.BalancesBlock = balancesBlock.Uint64() + details.MinipoolLaunchTimeout = minipoolLaunchTimeout + details.PromotionScrubPeriod = convertToDuration(promotionScrubPeriodSeconds) + details.BondReductionWindowStart = convertToDuration(windowStartRaw) + details.BondReductionWindowLength = convertToDuration(windowLengthRaw) + + // Get various balances + addresses := []common.Address{ + *contracts.RocketSmoothingPool.Address, + *contracts.RocketTokenRETH.Address, + } + balances, err := contracts.BalanceBatcher.GetEthBalances(addresses, opts) + if err != nil { + return nil, fmt.Errorf("error getting contract balances: %w", err) + } + details.SmoothingPoolBalance = balances[0] + details.RETHBalance = balances[1] + + return details, nil +} + +// Gets the details for a node using the efficient multicall contract +func GetTotalEffectiveRplStake(rp *rocketpool.RocketPool, contracts *NetworkContracts) (*big.Int, error) { + opts := &bind.CallOpts{ + BlockNumber: contracts.ElBlockNumber, + } + + // Get the list of node addresses + addresses, err := getNodeAddressesFast(rp, contracts, opts) + if err != nil { + return nil, fmt.Errorf("error getting node addresses: %w", err) + } + count := len(addresses) + minimumStakes := make([]*big.Int, count) + effectiveStakes := make([]*big.Int, count) + + // Sync + var wg errgroup.Group + wg.SetLimit(threadLimit) + + // Run the getters in batches + for i := 0; i < count; i += networkEffectiveStakeBatchSize { + i := i + max := i + networkEffectiveStakeBatchSize + if max > count { + max = count + } + + wg.Go(func() error { + var err error + mc, err := multicall.NewMultiCaller(rp.Client, contracts.Multicaller.ContractAddress) + if err != nil { + return err + } + for j := i; j < max; j++ { + address := addresses[j] + mc.AddCall(contracts.RocketNodeStaking, &minimumStakes[j], "getNodeMinimumRPLStake", address) + mc.AddCall(contracts.RocketNodeStaking, &effectiveStakes[j], "getNodeEffectiveRPLStake", address) + } + _, err = mc.FlexibleCall(true, opts) + if err != nil { + return fmt.Errorf("error executing multicall: %w", err) + } + return nil + }) + } + + if err := wg.Wait(); err != nil { + return nil, fmt.Errorf("error getting effective stakes for all nodes: %w", err) + } + + totalEffectiveStake := big.NewInt(0) + for i, effectiveStake := range effectiveStakes { + minimumStake := minimumStakes[i] + // Fix the effective stake + if effectiveStake.Cmp(minimumStake) >= 0 { + totalEffectiveStake.Add(totalEffectiveStake, effectiveStake) + } + } + + return totalEffectiveStake, nil +} diff --git a/bindings/utils/state/node.go b/bindings/utils/state/node.go new file mode 100644 index 000000000..2d5e016d0 --- /dev/null +++ b/bindings/utils/state/node.go @@ -0,0 +1,361 @@ +package state + +import ( + "context" + "fmt" + "math/big" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/rocket-pool/smartnode/bindings/node" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/types" + "github.com/rocket-pool/smartnode/bindings/utils/multicall" + "golang.org/x/sync/errgroup" +) + +const ( + legacyNodeBatchSize int = 100 + nodeAddressBatchSize int = 1000 +) + +// Complete details for a node +type NativeNodeDetails struct { + Exists bool `json:"exists"` + RegistrationTime *big.Int `json:"registration_time"` + TimezoneLocation string `json:"timezone_location"` + FeeDistributorInitialised bool `json:"fee_distributor_initialised"` + FeeDistributorAddress common.Address `json:"fee_distributor_address"` + RewardNetwork *big.Int `json:"reward_network"` + RplStake *big.Int `json:"rpl_stake"` + EffectiveRPLStake *big.Int `json:"effective_rpl_stake"` + MinimumRPLStake *big.Int `json:"minimum_rpl_stake"` + MaximumRPLStake *big.Int `json:"maximum_rpl_stake"` + EthMatched *big.Int `json:"eth_matched"` + EthMatchedLimit *big.Int `json:"eth_matched_limit"` + MinipoolCount *big.Int `json:"minipool_count"` + BalanceETH *big.Int `json:"balance_eth"` + BalanceRETH *big.Int `json:"balance_reth"` + BalanceRPL *big.Int `json:"balance_rpl"` + BalanceOldRPL *big.Int `json:"balance_old_rpl"` + DepositCreditBalance *big.Int `json:"deposit_credit_balance"` + DistributorBalanceUserETH *big.Int `json:"distributor_balance_user_eth"` // Must call CalculateAverageFeeAndDistributorShares to get this + DistributorBalanceNodeETH *big.Int `json:"distributor_balance_node_eth"` // Must call CalculateAverageFeeAndDistributorShares to get this + WithdrawalAddress common.Address `json:"withdrawal_address"` + PendingWithdrawalAddress common.Address `json:"pending_withdrawal_address"` + SmoothingPoolRegistrationState bool `json:"smoothing_pool_registration_state"` + SmoothingPoolRegistrationChanged *big.Int `json:"smoothing_pool_registration_changed"` + NodeAddress common.Address `json:"node_address"` + AverageNodeFee *big.Int `json:"average_node_fee"` // Must call CalculateAverageFeeAndDistributorShares to get this + CollateralisationRatio *big.Int `json:"collateralisation_ratio"` + DistributorBalance *big.Int `json:"distributor_balance"` + MegapoolAddress common.Address `json:"megapool_address"` + MegapoolDeployed bool `json:"megapool_deployed"` +} + +func timeMax(a, b time.Time) time.Time { + if a.After(b) { + return a + } + return b +} + +func timeMin(a, b time.Time) time.Time { + if a.Before(b) { + return a + } + return b +} + +// Returns whether the node is eligible for bonuses, and the start and end times of its eligibility +func (nnd *NativeNodeDetails) IsEligibleForBonuses(eligibleStart time.Time, eligibleEnd time.Time) (bool, time.Time, time.Time) { + // Nodes are not eligible for bonuses if they never opted into the smoothing pool + registeredTime := time.Unix(nnd.SmoothingPoolRegistrationChanged.Int64(), 0) + if registeredTime.Unix() == 0 { + return false, time.Time{}, time.Time{} + } + + // Nodes are eligible for bonuses if they were in the Smoothing Pool for a portion of the interval + if nnd.SmoothingPoolRegistrationState { + return registeredTime.Before(eligibleEnd), timeMax(registeredTime, eligibleStart), eligibleEnd + } + + // Nodes that weren't opted in at the end of the interval are eligible if they opted out during the interval + return registeredTime.Before(eligibleEnd), timeMax(registeredTime, eligibleStart), timeMin(registeredTime, eligibleEnd) +} + +// Gets the details for a node using the efficient multicall contract +func GetNativeNodeDetails(rp *rocketpool.RocketPool, contracts *NetworkContracts, nodeAddress common.Address) (NativeNodeDetails, error) { + opts := &bind.CallOpts{ + BlockNumber: contracts.ElBlockNumber, + } + details := NativeNodeDetails{ + NodeAddress: nodeAddress, + AverageNodeFee: big.NewInt(0), + CollateralisationRatio: big.NewInt(0), + DistributorBalanceUserETH: big.NewInt(0), + DistributorBalanceNodeETH: big.NewInt(0), + } + + addNodeDetailsCalls(contracts, contracts.Multicaller, &details, nodeAddress) + + _, err := contracts.Multicaller.FlexibleCall(true, opts) + if err != nil { + return NativeNodeDetails{}, fmt.Errorf("error executing multicall: %w", err) + } + + // Get the node's ETH balance + details.BalanceETH, err = rp.Client.BalanceAt(context.Background(), nodeAddress, opts.BlockNumber) + if err != nil { + return NativeNodeDetails{}, err + } + + // Get the distributor balance + distributorBalance, err := rp.Client.BalanceAt(context.Background(), details.FeeDistributorAddress, opts.BlockNumber) + if err != nil { + return NativeNodeDetails{}, err + } + + // Do some postprocessing on the node data + details.DistributorBalance = distributorBalance + + // Fix the effective stake + if details.EffectiveRPLStake.Cmp(details.MinimumRPLStake) == -1 { + details.EffectiveRPLStake.SetUint64(0) + } + + return details, nil +} + +// Gets the details for all nodes using the efficient multicall contract +func GetAllNativeNodeDetails(rp *rocketpool.RocketPool, contracts *NetworkContracts) ([]NativeNodeDetails, error) { + opts := &bind.CallOpts{ + BlockNumber: contracts.ElBlockNumber, + } + + // Get the list of node addresses + addresses, err := getNodeAddressesFast(rp, contracts, opts) + if err != nil { + return nil, fmt.Errorf("error getting node addresses: %w", err) + } + count := len(addresses) + nodeDetails := make([]NativeNodeDetails, count) + + // Sync + var wg errgroup.Group + wg.SetLimit(threadLimit) + + // Run the getters in batches + for i := 0; i < count; i += legacyNodeBatchSize { + i := i + max := i + legacyNodeBatchSize + if max > count { + max = count + } + + wg.Go(func() error { + var err error + mc, err := multicall.NewMultiCaller(rp.Client, contracts.Multicaller.ContractAddress) + if err != nil { + return err + } + for j := i; j < max; j++ { + address := addresses[j] + details := &nodeDetails[j] + details.NodeAddress = address + details.AverageNodeFee = big.NewInt(0) + details.DistributorBalanceUserETH = big.NewInt(0) + details.DistributorBalanceNodeETH = big.NewInt(0) + details.CollateralisationRatio = big.NewInt(0) + + addNodeDetailsCalls(contracts, mc, details, address) + } + _, err = mc.FlexibleCall(true, opts) + if err != nil { + return fmt.Errorf("error executing multicall: %w", err) + } + return nil + }) + } + + if err := wg.Wait(); err != nil { + return nil, fmt.Errorf("error getting node details: %w", err) + } + + // Get the balances of the nodes + distributorAddresses := make([]common.Address, count) + balances, err := contracts.BalanceBatcher.GetEthBalances(addresses, opts) + if err != nil { + return nil, fmt.Errorf("error getting node balances: %w", err) + } + for i, details := range nodeDetails { + nodeDetails[i].BalanceETH = balances[i] + distributorAddresses[i] = details.FeeDistributorAddress + } + + // Get the balances of the distributors + balances, err = contracts.BalanceBatcher.GetEthBalances(distributorAddresses, opts) + if err != nil { + return nil, fmt.Errorf("error getting distributor balances: %w", err) + } + + // Do some postprocessing on the node data + for i := range nodeDetails { + details := &nodeDetails[i] + details.DistributorBalance = balances[i] + + // Fix the effective stake + if details.EffectiveRPLStake.Cmp(details.MinimumRPLStake) == -1 { + details.EffectiveRPLStake.SetUint64(0) + } + } + + return nodeDetails, nil +} + +func (node *NativeNodeDetails) WasOptedInAt(t time.Time) bool { + if node.SmoothingPoolRegistrationState { + // If a node is opted in, check if the check time is after the opt-in time + return t.After(time.Unix(node.SmoothingPoolRegistrationChanged.Int64(), 0)) + } + + // If the node isn't opted in and was never opted in, it's not opted in + if node.SmoothingPoolRegistrationChanged.Cmp(big.NewInt(0)) == 0 { + return false + } + + // If a node is opted out, but was opted in, check if the check time is before the opt-out time + return t.Before(time.Unix(node.SmoothingPoolRegistrationChanged.Int64(), 0)) +} + +// Calculate the average node fee and user/node shares of the distributor's balance +func (node *NativeNodeDetails) CalculateAverageFeeAndDistributorShares(minipoolDetails []*NativeMinipoolDetails) error { + + // Calculate the total of all fees for staking minipools that aren't finalized + totalFee := big.NewInt(0) + eligibleMinipools := int64(0) + for _, mpd := range minipoolDetails { + if mpd.Status == types.Staking && !mpd.Finalised { + totalFee.Add(totalFee, mpd.NodeFee) + eligibleMinipools++ + } + } + + // Get the average fee (0 if there aren't any minipools) + if eligibleMinipools > 0 { + node.AverageNodeFee.Div(totalFee, big.NewInt(eligibleMinipools)) + } + + // Get the user and node portions of the distributor balance + distributorBalance := big.NewInt(0).Set(node.DistributorBalance) + if distributorBalance.Cmp(big.NewInt(0)) > 0 { + nodeBalance := big.NewInt(0) + nodeBalance.Mul(distributorBalance, big.NewInt(1e18)) + nodeBalance.Div(nodeBalance, node.CollateralisationRatio) + + userBalance := big.NewInt(0) + userBalance.Sub(distributorBalance, nodeBalance) + + if eligibleMinipools == 0 { + // Split it based solely on the collateralisation ratio if there are no minipools (and hence no average fee) + node.DistributorBalanceNodeETH = big.NewInt(0).Set(nodeBalance) + node.DistributorBalanceUserETH = big.NewInt(0).Sub(distributorBalance, nodeBalance) + } else { + // Amount of ETH given to the NO as a commission + commissionEth := big.NewInt(0) + commissionEth.Mul(userBalance, node.AverageNodeFee) + commissionEth.Div(commissionEth, big.NewInt(1e18)) + + node.DistributorBalanceNodeETH.Add(nodeBalance, commissionEth) // Node gets their portion + commission on user portion + node.DistributorBalanceUserETH.Sub(distributorBalance, node.DistributorBalanceNodeETH) // User gets balance - node share + } + + } else { + // No distributor balance + node.DistributorBalanceNodeETH = big.NewInt(0) + node.DistributorBalanceUserETH = big.NewInt(0) + } + + return nil +} + +// Get all node addresses using the multicaller +func getNodeAddressesFast(rp *rocketpool.RocketPool, contracts *NetworkContracts, opts *bind.CallOpts) ([]common.Address, error) { + // Get minipool count + nodeCount, err := node.GetNodeCount(rp, opts) + if err != nil { + return []common.Address{}, err + } + + // Sync + var wg errgroup.Group + wg.SetLimit(threadLimit) + addresses := make([]common.Address, nodeCount) + + // Run the getters in batches + count := int(nodeCount) + for i := 0; i < count; i += nodeAddressBatchSize { + i := i + max := i + nodeAddressBatchSize + if max > count { + max = count + } + + wg.Go(func() error { + var err error + mc, err := multicall.NewMultiCaller(rp.Client, contracts.Multicaller.ContractAddress) + if err != nil { + return err + } + for j := i; j < max; j++ { + mc.AddCall(contracts.RocketNodeManager, &addresses[j], "getNodeAt", big.NewInt(int64(j))) + } + _, err = mc.FlexibleCall(true, opts) + if err != nil { + return fmt.Errorf("error executing multicall: %w", err) + } + return nil + }) + } + + if err := wg.Wait(); err != nil { + return nil, fmt.Errorf("error getting node addresses: %w", err) + } + + return addresses, nil +} + +// Add all of the calls for the node details to the multicaller +func addNodeDetailsCalls(contracts *NetworkContracts, mc *multicall.MultiCaller, details *NativeNodeDetails, address common.Address) { + mc.AddCall(contracts.RocketNodeManager, &details.Exists, "getNodeExists", address) + mc.AddCall(contracts.RocketNodeManager, &details.RegistrationTime, "getNodeRegistrationTime", address) + mc.AddCall(contracts.RocketNodeManager, &details.TimezoneLocation, "getNodeTimezoneLocation", address) + mc.AddCall(contracts.RocketNodeManager, &details.FeeDistributorInitialised, "getFeeDistributorInitialised", address) + mc.AddCall(contracts.RocketNodeDistributorFactory, &details.FeeDistributorAddress, "getProxyAddress", address) + mc.AddCall(contracts.RocketNodeManager, &details.RewardNetwork, "getRewardNetwork", address) + mc.AddCall(contracts.RocketNodeStaking, &details.RplStake, "getNodeRPLStake", address) + mc.AddCall(contracts.RocketNodeStaking, &details.EffectiveRPLStake, "getNodeEffectiveRPLStake", address) + mc.AddCall(contracts.RocketNodeStaking, &details.MinimumRPLStake, "getNodeMinimumRPLStake", address) + mc.AddCall(contracts.RocketNodeStaking, &details.MaximumRPLStake, "getNodeMaximumRPLStake", address) + mc.AddCall(contracts.RocketNodeStaking, &details.EthMatched, "getNodeETHMatched", address) + mc.AddCall(contracts.RocketNodeStaking, &details.EthMatchedLimit, "getNodeETHMatchedLimit", address) + mc.AddCall(contracts.RocketMinipoolManager, &details.MinipoolCount, "getNodeMinipoolCount", address) + mc.AddCall(contracts.RocketTokenRETH, &details.BalanceRETH, "balanceOf", address) + mc.AddCall(contracts.RocketTokenRPL, &details.BalanceRPL, "balanceOf", address) + mc.AddCall(contracts.RocketTokenRPLFixedSupply, &details.BalanceOldRPL, "balanceOf", address) + mc.AddCall(contracts.RocketStorage, &details.WithdrawalAddress, "getNodeWithdrawalAddress", address) + mc.AddCall(contracts.RocketStorage, &details.PendingWithdrawalAddress, "getNodePendingWithdrawalAddress", address) + mc.AddCall(contracts.RocketNodeManager, &details.SmoothingPoolRegistrationState, "getSmoothingPoolRegistrationState", address) + mc.AddCall(contracts.RocketNodeManager, &details.SmoothingPoolRegistrationChanged, "getSmoothingPoolRegistrationChanged", address) + + // Atlas + mc.AddCall(contracts.RocketNodeDeposit, &details.DepositCreditBalance, "getNodeDepositCredit", address) + mc.AddCall(contracts.RocketNodeStaking, &details.CollateralisationRatio, "getNodeETHCollateralisationRatio", address) + + // Saturn + if contracts.isSaturnDeployed() { + mc.AddCall(contracts.RocketMegapoolFactory, &details.MegapoolAddress, "getExpectedAddress", address) + mc.AddCall(contracts.RocketMegapoolFactory, &details.MegapoolDeployed, "getMegapoolDeployed", address) + } +} diff --git a/bindings/utils/state/odao.go b/bindings/utils/state/odao.go new file mode 100644 index 000000000..9074a6905 --- /dev/null +++ b/bindings/utils/state/odao.go @@ -0,0 +1,188 @@ +package state + +import ( + "fmt" + "math/big" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/rocket-pool/smartnode/bindings/dao/trustednode" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/utils/multicall" + "golang.org/x/sync/errgroup" +) + +const ( + oDaoAddressBatchSize int = 1000 + oDaoDetailsBatchSize int = 50 +) + +type OracleDaoMemberDetails struct { + Address common.Address `json:"address"` + Exists bool `json:"exists"` + ID string `json:"id"` + Url string `json:"url"` + JoinedTime time.Time `json:"joinedTime"` + LastProposalTime time.Time `json:"lastProposalTime"` + RPLBondAmount *big.Int `json:"rplBondAmount"` + ReplacementAddress common.Address `json:"replacementAddress"` + IsChallenged bool `json:"isChallenged"` + joinedTimeRaw *big.Int `json:"-"` + lastProposalTimeRaw *big.Int `json:"-"` +} + +// Gets the details for an Oracle DAO member using the efficient multicall contract +func GetOracleDaoMemberDetails(rp *rocketpool.RocketPool, contracts *NetworkContracts, memberAddress common.Address) (OracleDaoMemberDetails, error) { + opts := &bind.CallOpts{ + BlockNumber: contracts.ElBlockNumber, + } + + details := OracleDaoMemberDetails{} + details.Address = memberAddress + + addOracleDaoMemberDetailsCalls(contracts, contracts.Multicaller, &details) + + _, err := contracts.Multicaller.FlexibleCall(true, opts) + if err != nil { + return OracleDaoMemberDetails{}, fmt.Errorf("error executing multicall: %w", err) + } + + fixupOracleDaoMemberDetails(&details) + + return details, nil +} + +// Gets all Oracle DAO member details using the efficient multicall contract +func GetAllOracleDaoMemberDetails(rp *rocketpool.RocketPool, contracts *NetworkContracts) ([]OracleDaoMemberDetails, error) { + opts := &bind.CallOpts{ + BlockNumber: contracts.ElBlockNumber, + } + + // Get the list of all minipool addresses + addresses, err := getOdaoAddresses(rp, contracts, opts) + if err != nil { + return nil, fmt.Errorf("error getting Oracle DAO addresses: %w", err) + } + + // Get the minipool details + return getOracleDaoDetails(rp, contracts, addresses, opts) +} + +// Get all Oracle DAO addresses +func getOdaoAddresses(rp *rocketpool.RocketPool, contracts *NetworkContracts, opts *bind.CallOpts) ([]common.Address, error) { + // Get minipool count + memberCount, err := trustednode.GetMemberCount(rp, opts) + if err != nil { + return []common.Address{}, err + } + + // Sync + var wg errgroup.Group + wg.SetLimit(threadLimit) + addresses := make([]common.Address, memberCount) + + // Run the getters in batches + count := int(memberCount) + for i := 0; i < count; i += minipoolAddressBatchSize { + i := i + max := i + oDaoAddressBatchSize + if max > count { + max = count + } + + wg.Go(func() error { + var err error + mc, err := multicall.NewMultiCaller(rp.Client, contracts.Multicaller.ContractAddress) + if err != nil { + return err + } + for j := i; j < max; j++ { + mc.AddCall(contracts.RocketDAONodeTrusted, &addresses[j], "getMemberAt", big.NewInt(int64(j))) + } + _, err = mc.FlexibleCall(true, opts) + if err != nil { + return fmt.Errorf("error executing multicall: %w", err) + } + return nil + }) + } + + if err := wg.Wait(); err != nil { + return nil, fmt.Errorf("error getting Oracle DAO addresses: %w", err) + } + + return addresses, nil +} + +// Get the details of the Oracle DAO members +func getOracleDaoDetails(rp *rocketpool.RocketPool, contracts *NetworkContracts, addresses []common.Address, opts *bind.CallOpts) ([]OracleDaoMemberDetails, error) { + memberDetails := make([]OracleDaoMemberDetails, len(addresses)) + + // Get the details in batches + var wg errgroup.Group + wg.SetLimit(threadLimit) + count := len(addresses) + for i := 0; i < count; i += minipoolBatchSize { + i := i + max := i + minipoolBatchSize + if max > count { + max = count + } + + wg.Go(func() error { + var err error + mc, err := multicall.NewMultiCaller(rp.Client, contracts.Multicaller.ContractAddress) + if err != nil { + return err + } + for j := i; j < max; j++ { + + address := addresses[j] + details := &memberDetails[j] + details.Address = address + + addOracleDaoMemberDetailsCalls(contracts, mc, details) + } + _, err = mc.FlexibleCall(true, opts) + if err != nil { + return fmt.Errorf("error executing multicall: %w", err) + } + + return nil + }) + } + + if err := wg.Wait(); err != nil { + return nil, fmt.Errorf("error getting Oracle DAO details: %w", err) + } + + // Postprocessing + for i := range memberDetails { + details := &memberDetails[i] + fixupOracleDaoMemberDetails(details) + } + + return memberDetails, nil +} + +// Add the Oracle DAO details getters to the multicaller +func addOracleDaoMemberDetailsCalls(contracts *NetworkContracts, mc *multicall.MultiCaller, details *OracleDaoMemberDetails) error { + address := details.Address + mc.AddCall(contracts.RocketDAONodeTrusted, &details.Exists, "getMemberIsValid", address) + mc.AddCall(contracts.RocketDAONodeTrusted, &details.ID, "getMemberID", address) + mc.AddCall(contracts.RocketDAONodeTrusted, &details.Url, "getMemberUrl", address) + mc.AddCall(contracts.RocketDAONodeTrusted, &details.joinedTimeRaw, "getMemberJoinedTime", address) + mc.AddCall(contracts.RocketDAONodeTrusted, &details.lastProposalTimeRaw, "getMemberLastProposalTime", address) + mc.AddCall(contracts.RocketDAONodeTrusted, &details.RPLBondAmount, "getMemberRPLBondAmount", address) + mc.AddCall(contracts.RocketDAONodeTrusted, &details.ReplacementAddress, "getMemberReplacedAddress", address) + mc.AddCall(contracts.RocketDAONodeTrusted, &details.IsChallenged, "getMemberIsChallenged", address) + return nil +} + +// Fixes a member details struct with supplemental logic +func fixupOracleDaoMemberDetails(details *OracleDaoMemberDetails) error { + details.JoinedTime = convertToTime(details.joinedTimeRaw) + details.LastProposalTime = convertToTime(details.lastProposalTimeRaw) + return nil +} diff --git a/bindings/utils/state/pdao.go b/bindings/utils/state/pdao.go new file mode 100644 index 000000000..174143a9a --- /dev/null +++ b/bindings/utils/state/pdao.go @@ -0,0 +1,212 @@ +package state + +import ( + "fmt" + "math/big" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/rocket-pool/smartnode/bindings/dao/protocol" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/types" + "github.com/rocket-pool/smartnode/bindings/utils/multicall" + "golang.org/x/sync/errgroup" +) + +const ( + pDaoPropDetailsBatchSize int = 50 +) + +// Proposal details +type protocolDaoProposalDetailsRaw struct { + ID uint64 + DAO string + ProposerAddress common.Address + TargetBlock *big.Int + Message string + CreatedTime *big.Int + ChallengeWindow *big.Int + VotingStartTime *big.Int + Phase1EndTime *big.Int + Phase2EndTime *big.Int + ExpiryTime *big.Int + VotingPowerRequired *big.Int + VotingPowerFor *big.Int + VotingPowerAgainst *big.Int + VotingPowerAbstained *big.Int + VotingPowerToVeto *big.Int + IsDestroyed bool + IsFinalized bool + IsExecuted bool + IsVetoed bool + VetoQuorum *big.Int + Payload []byte + PayloadStr string + State uint8 + ProposalBond *big.Int + ChallengeBond *big.Int + DefeatIndex *big.Int +} + +// Gets a Protocol DAO proposal's details using the efficient multicall contract +func GetProtocolDaoProposalDetails(rp *rocketpool.RocketPool, contracts *NetworkContracts, proposalID uint64) (protocol.ProtocolDaoProposalDetails, error) { + opts := &bind.CallOpts{ + BlockNumber: contracts.ElBlockNumber, + } + + details := protocol.ProtocolDaoProposalDetails{} + rawDetails := protocolDaoProposalDetailsRaw{} + details.ID = proposalID + + addProposalCalls(contracts, contracts.Multicaller, &rawDetails) + + _, err := contracts.Multicaller.FlexibleCall(true, opts) + if err != nil { + return details, fmt.Errorf("error executing multicall: %w", err) + } + + fixupPdaoProposalDetails(rp, &rawDetails, &details, opts) + + return details, nil +} + +// Gets all Protocol DAO proposal details using the efficient multicall contract +func GetAllProtocolDaoProposalDetails(rp *rocketpool.RocketPool, contracts *NetworkContracts) ([]protocol.ProtocolDaoProposalDetails, error) { + opts := &bind.CallOpts{ + BlockNumber: contracts.ElBlockNumber, + } + + // Get the number of proposals available + propCount, err := protocol.GetTotalProposalCount(rp, opts) + if err != nil { + return nil, fmt.Errorf("error getting proposal count: %w", err) + } + + // Make the proposal IDs (1-indexed) and return the details + ids := make([]uint64, propCount) + for i := range ids { + ids[i] = uint64(i + 1) + } + return getProposalDetails(rp, contracts, ids, opts) +} + +// Get the details of all protocol DAO proposals +func getProposalDetails(rp *rocketpool.RocketPool, contracts *NetworkContracts, ids []uint64, opts *bind.CallOpts) ([]protocol.ProtocolDaoProposalDetails, error) { + propDetailsRaw := make([]protocolDaoProposalDetailsRaw, len(ids)) + + // Get the details in batches + var wg errgroup.Group + wg.SetLimit(threadLimit) + count := len(propDetailsRaw) + for i := 0; i < count; i += pDaoPropDetailsBatchSize { + i := i + max := i + pDaoPropDetailsBatchSize + if max > count { + max = count + } + + wg.Go(func() error { + var err error + mc, err := multicall.NewMultiCaller(rp.Client, contracts.Multicaller.ContractAddress) + if err != nil { + return err + } + for j := i; j < max; j++ { + id := ids[j] + details := &propDetailsRaw[j] + details.ID = id + + addProposalCalls(contracts, mc, details) + } + _, err = mc.FlexibleCall(true, opts) + if err != nil { + return fmt.Errorf("error executing multicall: %w", err) + } + + return nil + }) + } + + if err := wg.Wait(); err != nil { + return nil, fmt.Errorf("error getting Protocol DAO proposal details: %w", err) + } + + // Postprocessing + props := make([]protocol.ProtocolDaoProposalDetails, len(ids)) + for i := range propDetailsRaw { + rawDetails := &propDetailsRaw[i] + details := &props[i] + fixupPdaoProposalDetails(rp, rawDetails, details, opts) + } + + return props, nil +} + +// Get the details of a proposal +func addProposalCalls(contracts *NetworkContracts, mc *multicall.MultiCaller, details *protocolDaoProposalDetailsRaw) error { + id := big.NewInt(0).SetUint64(details.ID) + mc.AddCall(contracts.RocketDAOProtocolProposal, &details.ProposerAddress, "getProposer", id) + mc.AddCall(contracts.RocketDAOProtocolProposal, &details.DAO, "getDAO", id) + mc.AddCall(contracts.RocketDAOProtocolProposal, &details.TargetBlock, "getProposalBlock", id) + mc.AddCall(contracts.RocketDAOProtocolProposal, &details.Message, "getMessage", id) + mc.AddCall(contracts.RocketDAOProtocolProposal, &details.VotingStartTime, "getStart", id) + mc.AddCall(contracts.RocketDAOProtocolProposal, &details.Phase1EndTime, "getPhase1End", id) + mc.AddCall(contracts.RocketDAOProtocolProposal, &details.Phase2EndTime, "getPhase2End", id) + mc.AddCall(contracts.RocketDAOProtocolProposal, &details.ExpiryTime, "getExpires", id) + mc.AddCall(contracts.RocketDAOProtocolProposal, &details.CreatedTime, "getCreated", id) + mc.AddCall(contracts.RocketDAOProtocolProposal, &details.VotingPowerRequired, "getVotingPowerRequired", id) + mc.AddCall(contracts.RocketDAOProtocolProposal, &details.VotingPowerFor, "getVotingPowerFor", id) + mc.AddCall(contracts.RocketDAOProtocolProposal, &details.VotingPowerAgainst, "getVotingPowerAgainst", id) + mc.AddCall(contracts.RocketDAOProtocolProposal, &details.VotingPowerAbstained, "getVotingPowerAbstained", id) + mc.AddCall(contracts.RocketDAOProtocolProposal, &details.VotingPowerToVeto, "getVotingPowerVeto", id) + mc.AddCall(contracts.RocketDAOProtocolProposal, &details.IsDestroyed, "getDestroyed", id) + mc.AddCall(contracts.RocketDAOProtocolProposal, &details.IsFinalized, "getFinalised", id) + mc.AddCall(contracts.RocketDAOProtocolProposal, &details.IsExecuted, "getExecuted", id) + mc.AddCall(contracts.RocketDAOProtocolProposal, &details.IsVetoed, "getVetoed", id) + mc.AddCall(contracts.RocketDAOProtocolProposal, &details.VetoQuorum, "getProposalVetoQuorum", id) + mc.AddCall(contracts.RocketDAOProtocolProposal, &details.Payload, "getPayload", id) + mc.AddCall(contracts.RocketDAOProtocolProposal, &details.State, "getState", id) + mc.AddCall(contracts.RocketDAOProtocolVerifier, &details.DefeatIndex, "getDefeatIndex", id) + mc.AddCall(contracts.RocketDAOProtocolVerifier, &details.ProposalBond, "getProposalBond", id) + mc.AddCall(contracts.RocketDAOProtocolVerifier, &details.ChallengeBond, "getChallengeBond", id) + mc.AddCall(contracts.RocketDAOProtocolVerifier, &details.ChallengeWindow, "getChallengePeriod", id) + return nil +} + +// Converts a raw proposal to a well-formatted one +func fixupPdaoProposalDetails(rp *rocketpool.RocketPool, rawDetails *protocolDaoProposalDetailsRaw, details *protocol.ProtocolDaoProposalDetails, opts *bind.CallOpts) error { + details.ID = rawDetails.ID + details.DAO = rawDetails.DAO + details.ProposerAddress = rawDetails.ProposerAddress + details.TargetBlock = uint32(rawDetails.TargetBlock.Uint64()) + details.Message = rawDetails.Message + details.VotingStartTime = time.Unix(rawDetails.VotingStartTime.Int64(), 0) + details.Phase1EndTime = time.Unix(rawDetails.Phase1EndTime.Int64(), 0) + details.Phase2EndTime = time.Unix(rawDetails.Phase2EndTime.Int64(), 0) + details.ExpiryTime = time.Unix(rawDetails.ExpiryTime.Int64(), 0) + details.CreatedTime = time.Unix(rawDetails.CreatedTime.Int64(), 0) + details.VotingPowerRequired = rawDetails.VotingPowerRequired + details.VotingPowerFor = rawDetails.VotingPowerFor + details.VotingPowerAgainst = rawDetails.VotingPowerAgainst + details.VotingPowerAbstained = rawDetails.VotingPowerAbstained + details.VotingPowerToVeto = rawDetails.VotingPowerToVeto + details.IsDestroyed = rawDetails.IsDestroyed + details.IsFinalized = rawDetails.IsFinalized + details.IsExecuted = rawDetails.IsExecuted + details.IsVetoed = rawDetails.IsVetoed + details.VetoQuorum = rawDetails.VetoQuorum + details.Payload = rawDetails.Payload + details.State = types.ProtocolDaoProposalState(rawDetails.State) + details.DefeatIndex = rawDetails.DefeatIndex.Uint64() + details.ProposalBond = rawDetails.ProposalBond + details.ChallengeBond = rawDetails.ChallengeBond + details.ChallengeWindow = time.Second * time.Duration(rawDetails.ChallengeWindow.Uint64()) + + var err error + details.PayloadStr, err = protocol.GetProposalPayloadString(rp, rawDetails.Payload, opts) + if err != nil { + details.PayloadStr = fmt.Sprintf("", err.Error()) + } + return nil +} diff --git a/bindings/utils/strings/sanitize.go b/bindings/utils/strings/sanitize.go new file mode 100644 index 000000000..ac8904f3a --- /dev/null +++ b/bindings/utils/strings/sanitize.go @@ -0,0 +1,16 @@ +package strings + +import ( + "strings" + "unicode" +) + +// Remove non-printable characters from a string +func Sanitize(str string) string { + return strings.Map(func(r rune) rune { + if unicode.IsPrint(r) { + return r + } + return -1 + }, str) +} diff --git a/bindings/utils/version-checker.go b/bindings/utils/version-checker.go new file mode 100644 index 000000000..ae4826948 --- /dev/null +++ b/bindings/utils/version-checker.go @@ -0,0 +1,60 @@ +package utils + +import ( + "fmt" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/hashicorp/go-version" + "github.com/rocket-pool/smartnode/bindings/deposit" + "github.com/rocket-pool/smartnode/bindings/network" + "github.com/rocket-pool/smartnode/bindings/node" + "github.com/rocket-pool/smartnode/bindings/rocketpool" +) + +func GetCurrentVersion(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*version.Version, error) { + depositPoolVersion, err := deposit.GetRocketDepositPoolVersion(rp, opts) + if err != nil { + return nil, fmt.Errorf("error checking deposit pool version: %w", err) + } + + // Check for v1.4 (Saturn 1) + if depositPoolVersion > 3 { + return version.NewSemver("1.4.0") + } + + nodeMgrVersion, err := node.GetNodeManagerVersion(rp, opts) + if err != nil { + return nil, fmt.Errorf("error checking node manager version: %w", err) + } + + // Check for v1.3.1 (Houston Hotfix) + networkVotingVersion, err := network.GetRocketNetworkVotingVersion(rp, opts) + if err != nil { + return nil, fmt.Errorf("error checking network voting version: %w", err) + } + if networkVotingVersion > 1 { + return version.NewSemver("1.3.1") + } + + if nodeMgrVersion > 3 { + return version.NewSemver("1.3.0") + } + + // Check for v1.2 (Atlas) + nodeStakingVersion, err := node.GetNodeStakingVersion(rp, opts) + if err != nil { + return nil, fmt.Errorf("error checking node staking version: %w", err) + } + if nodeStakingVersion > 3 { + return version.NewSemver("1.2.0") + } + + // Check for v1.1 (Redstone) + if nodeMgrVersion > 1 { + return version.NewSemver("1.1.0") + } + + // v1.0 (Classic) + return version.NewSemver("1.0.0") + +} diff --git a/bindings/utils/wait.go b/bindings/utils/wait.go new file mode 100644 index 000000000..7a9649c42 --- /dev/null +++ b/bindings/utils/wait.go @@ -0,0 +1,52 @@ +package utils + +import ( + "context" + "errors" + "fmt" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/rocket-pool/smartnode/bindings/rocketpool" +) + +// Wait for a transaction to get mined +func WaitForTransaction(client rocketpool.ExecutionClient, hash common.Hash) (*types.Receipt, error) { + + var tx *types.Transaction + var err error + + // Get the transaction from its hash, retrying for 30 sec if it wasn't found + for i := 0; i < 30; i++ { + if i == 29 { + return nil, fmt.Errorf("Transaction not found after 30 seconds.") + } + + tx, _, err = client.TransactionByHash(context.Background(), hash) + if err != nil { + if err.Error() == "not found" { + time.Sleep(1 * time.Second) + continue + } + return nil, err + } else { + break + } + } + + // Wait for transaction to be mined + txReceipt, err := bind.WaitMined(context.Background(), client, tx) + if err != nil { + return nil, err + } + + // Check transaction status + if txReceipt.Status == 0 { + return txReceipt, errors.New("Transaction failed with status 0") + } + + // Return + return txReceipt, nil +} diff --git a/docker/rocketpool-dockerfile b/docker/rocketpool-dockerfile index 263e83151..b547756a1 100644 --- a/docker/rocketpool-dockerfile +++ b/docker/rocketpool-dockerfile @@ -13,6 +13,7 @@ RUN apt update && apt install -y \ COPY ./go.work /src/go.work COPY ./go.work.sum /src/go.work.sum COPY ./addons/go.mod /src/addons/go.mod +COPY ./bindings/go.mod /src/bindings/go.mod COPY ./rocketpool/go.mod /src/rocketpool/go.mod COPY ./rocketpool-cli/go.mod /src/rocketpool-cli/go.mod COPY ./shared/go.mod /src/shared/go.mod @@ -28,6 +29,7 @@ ARG VERSION COPY ./go.work /src/go.work COPY ./go.work.sum /src/go.work.sum COPY ./addons/ /src/addons/ +COPY ./bindings/ /src/bindings/ COPY ./rocketpool/ /src/rocketpool/ COPY ./rocketpool-cli/ /src/rocketpool-cli/ COPY ./shared/ /src/shared/ diff --git a/go.work b/go.work index 7d04138ac..8f8246b7c 100644 --- a/go.work +++ b/go.work @@ -2,6 +2,7 @@ go 1.21.8 use ( ./addons + ./bindings ./rocketpool ./rocketpool-cli ./shared @@ -11,3 +12,5 @@ use ( replace github.com/wealdtech/go-merkletree v1.0.1-0.20190605192610-2bb163c2ea2a => github.com/rocket-pool/go-merkletree v1.0.1-0.20220406020931-c262d9b976dd replace github.com/rocket-pool/smartnode => ./ + +replace github.com/rocket-pool/rocketpool-go => ./ diff --git a/go.work.sum b/go.work.sum index 9fe4fd17f..cb5c1f68b 100644 --- a/go.work.sum +++ b/go.work.sum @@ -85,6 +85,7 @@ cloud.google.com/go/compute v1.19.3/go.mod h1:qxvISKp/gYnXkSAD1ppcSOveRAmzxicEv/ cloud.google.com/go/compute v1.20.1/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= +cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc= cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= cloud.google.com/go/contactcenterinsights v1.6.0 h1:jXIpfcH/VYSE1SYcPzO0n1VVb+sAamiLOgCw45JbOQk= cloud.google.com/go/contactcenterinsights v1.6.0/go.mod h1:IIDlT6CLcDoyv79kDv8iWxMSTZhLxSCofVV5W6YFM/w= @@ -315,41 +316,37 @@ github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbi github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 h1:1BDTz0u9nC3//pOCMdNH+CiXJVYJh5UQNCOBG7jbELc= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/CloudyKit/fastprinter v0.0.0-20170127035650-74b38d55f37a/go.mod h1:EFZQ978U7x8IRnstaskI3IysnWY5Ao3QgZUKOXlsAdw= +github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 h1:sR+/8Yb4slttB4vD+b9btVEnWgL3Q00OBTzVT8B9C0c= github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno= github.com/CloudyKit/jet v2.1.3-0.20180809161101-62edd43e4f88+incompatible/go.mod h1:HPYO+50pSWkPoj9Q/eq0aRGByCL6ScRlUmiEX5Zgm+w= +github.com/CloudyKit/jet/v6 v6.2.0 h1:EpcZ6SR9n28BUGtNJSvlBqf90IpjeFr36Tizxhn/oME= github.com/CloudyKit/jet/v6 v6.2.0/go.mod h1:d3ypHeIRNo2+XyqnGA8s+aphtcVpjP5hPwP/Lzo7Ro4= github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= -github.com/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ= github.com/DataDog/zstd v1.5.5/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY= github.com/Joker/jade v1.0.1-0.20190614124447-d475f43051e7/go.mod h1:6E6s8o2AE4KhCrqr6GRJjdC/gNfTdxkIXvuGZZda2VM= +github.com/Joker/jade v1.1.3 h1:Qbeh12Vq6BxURXT1qZBRHsDxeURB8ztcL6f3EXSGeHk= github.com/Joker/jade v1.1.3/go.mod h1:T+2WLyt7VH6Lp0TRxQrUYEs64nRc83wkMQrfeIQKduM= github.com/MariusVanDerWijden/FuzzyVM v0.0.0-20240209103030-ec53fa766bf8 h1:BwEuC3xavrv4HTUDH2fUrKgKooiH3Q/nSVnFGtnzpN0= github.com/MariusVanDerWijden/FuzzyVM v0.0.0-20240209103030-ec53fa766bf8/go.mod h1:L1QpLBqXlboJMOC2hndG95d1eiElzKsBhjzcuy8pxeM= github.com/MariusVanDerWijden/tx-fuzz v1.3.3-0.20240227085032-f70dd7c85c97 h1:QDTh0xHorSykJ4+2VccBADMeRAVUbnHaWrCPIMtN+Vc= github.com/MariusVanDerWijden/tx-fuzz v1.3.3-0.20240227085032-f70dd7c85c97/go.mod h1:xcjGtET6+7KeDHcwLQp3sIfyFALtoTjzZgY8Y+RUozM= -github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= -github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0= +github.com/Shopify/goreferrer v0.0.0-20220729165902-8cddb4f5de06 h1:KkH3I3sJuOLP3TjA/dfr4NAY8bghDwnXiU7cTKxQqo0= github.com/Shopify/goreferrer v0.0.0-20220729165902-8cddb4f5de06/go.mod h1:7erjKLwalezA0k99cWs5L11HWOAPNjdUZ6RxH1BXbbM= github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA= github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= github.com/VictoriaMetrics/fastcache v1.12.1/go.mod h1:tX04vaqcNoQeGLD+ra5pU5sWkuxnzWhEzLwhP9w653o= -github.com/VictoriaMetrics/fastcache v1.12.2 h1:N0y9ASrJ0F6h0QaC3o6uJb3NIZ9VKLjCM7NQbSmF7WI= github.com/VictoriaMetrics/fastcache v1.12.2/go.mod h1:AmC+Nzz1+3G2eCPapF6UcsnkThDcMsQicp4xDukwJYI= -github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA= -github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow= github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4= github.com/aclements/go-moremath v0.0.0-20210112150236-f10218a38794/go.mod h1:7e+I0LQFUI9AXWxOfsQROs9xPhoJtbsyWcjJqDd4KPY= github.com/aead/siphash v1.0.1 h1:FwHfE/T45KPKYuuSAKyyvE+oPWcaQ+CUmFW0bPlM+kg= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= -github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= github.com/alecthomas/kingpin/v2 v2.4.0 h1:f48lwail6p8zpO1bC4TxtqACaGqHYA22qkHjHpqDjYY= github.com/alecthomas/kingpin/v2 v2.4.0/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -361,10 +358,11 @@ github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAu github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= github.com/alessio/shellescape v1.4.1 h1:V7yhSDDn8LP4lc4jS8pFkt0zCnzVJlG5JXy9BVKJUX0= github.com/alessio/shellescape v1.4.1/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30= +github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= -github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/antihax/optional v1.0.0 h1:xK2lYat7ZLaVVcIuj82J8kIro4V6kDe0AUDFboUCwcg= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/apache/arrow/go/v11 v11.0.0/go.mod h1:Eg5OsL5H+e299f7u5ssuXsuHQVEGC4xei5aX110hRiI= @@ -372,7 +370,6 @@ github.com/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2 github.com/aristanetworks/goarista v0.0.0-20200805130819-fd197cf57d96 h1:XJH0YfVFKbq782tlNThzN/Ud5qm/cx6LXOA/P6RkTxc= github.com/aristanetworks/goarista v0.0.0-20200805130819-fd197cf57d96/go.mod h1:QZe5Yh80Hp1b6JxQdpfSEEe8X7hTyTEZSosSrFf/oJE= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= -github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/aws/aws-sdk-go-v2 v1.21.2 h1:+LXZ0sgo8quN9UOKXXzAWRT3FWd4NxeXWOZom9pE7GA= @@ -401,6 +398,7 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.23.2 h1:0BkLfgeDjfZnZ+MhB3ONb01u9pwF github.com/aws/aws-sdk-go-v2/service/sts v1.23.2/go.mod h1:Eows6e1uQEsc4ZaHANmsPRzAKcVDrcmjjWiih2+HUUQ= github.com/aws/smithy-go v1.15.0 h1:PS/durmlzvAFpQHDs4wi4sNNP9ExsqZh6IlfdHXgKK8= github.com/aws/smithy-go v1.15.0/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= +github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g= github.com/bazelbuild/rules_go v0.23.2 h1:Wxu7JjqnF78cKZbsBsARLSXx/jlGaSLCnUV3mTlyHvM= @@ -410,10 +408,8 @@ github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= -github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bits-and-blooms/bitset v1.7.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= -github.com/bits-and-blooms/bitset v1.11.0 h1:RMyy2mBBShArUAhfVRZJ2xyBO58KCBCtZFShw3umo6k= github.com/bits-and-blooms/bitset v1.11.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= @@ -425,14 +421,12 @@ github.com/btcsuite/btcd v0.23.4/go.mod h1:0QJIIN1wwIXF/3G/m87gIwGniDMDQqjVn4SZg github.com/btcsuite/btcd/btcec/v2 v2.1.0/go.mod h1:2VzYrv4Gm4apmbVVsSq5bqf1Ec8v56E48Vt0Y/umPgA= github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU= -github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U= github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= github.com/btcsuite/btcd/btcutil v1.0.0/go.mod h1:Uoxwv0pqYWhD//tfTiipkxNfdhG9UrLwaeswfjfdF0A= github.com/btcsuite/btcd/btcutil v1.1.0/go.mod h1:5OapHB7A2hBBWLm48mmw4MOHNJCcUBTwmWH/0Jn8VHE= github.com/btcsuite/btcd/btcutil v1.1.3 h1:xfbtw8lwpp0G6NwSHb+UE67ryTFHJAiNuipusjXSohQ= github.com/btcsuite/btcd/btcutil v1.1.3/go.mod h1:UR7dsSJzJUfMmFiiLlIrMq1lS9jh9EdCV7FStZSnpi0= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= -github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9VhRV3jjAVU7DJVjMaK+IsvSeZvFo= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= @@ -456,27 +450,26 @@ github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA github.com/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g= github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= -github.com/cespare/cp v1.1.1 h1:nCb6ZLdB7NRaqsm91JtQTAme2SKJzXVsdPIPkyJr1MU= github.com/cespare/cp v1.1.1/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= -github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927/go.mod h1:h/aW8ynjgkuj+NQRlZcDbAbM1ORAbXjXX77sX7T289U= -github.com/cheggaaa/pb v2.0.7+incompatible/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s= -github.com/cheggaaa/pb/v3 v3.0.4/go.mod h1:7rgWxLrAUcFMkvJuv09+DYi7mMUYi8nO9iOWcvGJPfw= -github.com/cheggaaa/pb/v3 v3.0.8 h1:bC8oemdChbke2FHIIGy9mn4DPJ2caZYQnfbRqwmdCoA= +github.com/cheggaaa/pb v2.0.7+incompatible h1:gLKifR1UkZ/kLkda5gC0K6c8g+jU2sINPtBeOiNlMhU= github.com/cheggaaa/pb/v3 v3.0.8/go.mod h1:UICbiLec/XO6Hw6k+BHEtHeQFzzBH4i2/qk/ow1EJTA= +github.com/chromedp/cdproto v0.0.0-20230802225258-3cf4e6d46a89 h1:aPflPkRFkVwbW6dmcVqfgwp1i+UWGFH6VgR1Jim5Ygc= github.com/chromedp/cdproto v0.0.0-20230802225258-3cf4e6d46a89/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs= +github.com/chromedp/chromedp v0.9.2 h1:dKtNz4kApb06KuSXoTQIyUC2TrA0fhGDwNZf3bcgfKw= github.com/chromedp/chromedp v0.9.2/go.mod h1:LkSXJKONWTCHAfQasKFUZI+mxqS4tZqhmtGzzhLsnLs= +github.com/chromedp/sysutil v1.0.0 h1:+ZxhTpfpZlmchB58ih/LBHX52ky7w2VhQVKQMucy3Ic= github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww= github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY= +github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM= github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/readline v1.5.0/go.mod h1:x22KAscuvRqlLoK9CsoYsmxoXZMMFVyOl86cAH8qUic= @@ -485,6 +478,7 @@ github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObk github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04= github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8= github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= @@ -503,28 +497,23 @@ github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 h1:/inchEIKaYC1Akx+H+gqO04wryn5h75LSazbRlnya1k= github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cockroachdb/datadriven v1.0.0/go.mod h1:5Ib8Meh+jk1RlHIXej6Pzevx/NLlNvQB9pmSBZErGA4= +github.com/cockroachdb/datadriven v1.0.2 h1:H9MtNqVoVhvd9nCBwOyDjUEdZCREqbIdCJD93PBm/jA= github.com/cockroachdb/datadriven v1.0.2/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= github.com/cockroachdb/errors v1.6.1/go.mod h1:tm6FTP5G81vwJ5lC0SizQo374JNCOPrHyXGitRJoDqM= github.com/cockroachdb/errors v1.8.1/go.mod h1:qGwQn6JmZ+oMjuLwjWzUNqblqk0xl4CVV3SQbGwK7Ac= -github.com/cockroachdb/errors v1.11.1 h1:xSEW75zKaKCWzR3OfxXUxgrk/NtT4G1MiOv5lWZazG8= github.com/cockroachdb/errors v1.11.1/go.mod h1:8MUxA3Gi6b25tYlFEBGLf+D8aISL+M4MIpiWMSNRfxw= github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= -github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= -github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593 h1:aPEJyR4rPBvDmeyi+l/FS/VtA00IWvjeFvjen1m1l1A= github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593/go.mod h1:6hk1eMY/u5t+Cf18q5lFMUA1Rc+Sm5I6Ra1QuPyxXCo= github.com/cockroachdb/redact v1.0.8/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= -github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30= github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= github.com/cockroachdb/sentry-go v0.6.1-cockroachdb.2 h1:IKgmqgMQlVJIZj19CdocBeSfSaiCbEBZGKODaixqtHM= github.com/cockroachdb/sentry-go v0.6.1-cockroachdb.2/go.mod h1:8BT+cPK6xvFOcRlk0R8eg+OTkcqI6baNH4xAkpiYVvQ= -github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= +github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0 h1:sDMmm+q/3+BukdIpxwO365v/Rbspp2Nt5XntgQRXq8Q= github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= -github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ= github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= -github.com/consensys/gnark-crypto v0.12.1 h1:lHH39WuuFgVHONRl3J0LRBtuYdQTumFSDtJF7HpyG8M= github.com/consensys/gnark-crypto v0.12.1/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY= github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM= github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw= @@ -539,13 +528,11 @@ github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM= github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/crackcomm/go-gitignore v0.0.0-20170627025303-887ab5e44cc3 h1:HVTnpeuvF6Owjd5mniCL8DEXo7uYXdQEmOP4FJbV5tg= github.com/crackcomm/go-gitignore v0.0.0-20170627025303-887ab5e44cc3/go.mod h1:p1d6YEZWvFzEh4KLyvBcVSnrfNDDvK2zfK/4x2v/4pE= github.com/crate-crypto/go-ipa v0.0.0-20230601170251-1830d0757c80 h1:DuBDHVjgGMPki7bAyh91+3cF1Vh34sAEdH8JQgbc2R0= github.com/crate-crypto/go-ipa v0.0.0-20230601170251-1830d0757c80/go.mod h1:gzbVz57IDJgQ9rLQwfSk696JGWof8ftznEL9GoAv3NI= -github.com/crate-crypto/go-kzg-4844 v0.7.0 h1:C0vgZRk4q4EZ/JgPfzuSoxdCq3C3mOZMBShovmncxvA= github.com/crate-crypto/go-kzg-4844 v0.7.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= @@ -557,22 +544,18 @@ github.com/d4l3k/messagediff v1.2.1 h1:ZcAIMYsUg0EAp9X+tt8/enBE/Q8Yd5kzPynLyKptt github.com/d4l3k/messagediff v1.2.1/go.mod h1:Oozbb1TVXFac9FtSIxHBMnBCq2qeH/2KkEQxENCrlLo= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c h1:pFUpOrbxDR6AkioZ1ySsx5yxlDQZ8stG2b88gTPxgJU= github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c/go.mod h1:6UhI8N9EjYm1c2odKpFpAYeR8dsBeM7PtzQhRgxRr9U= github.com/deckarep/golang-set v1.8.0 h1:sk9/l/KqpunDwP7pSjUg0keiOOLEnOBHzykLrsPppp4= github.com/deckarep/golang-set v1.8.0/go.mod h1:5nI87KwE7wgsBU1F4GKAw2Qod7p5kyS383rP6+o6qqo= github.com/deckarep/golang-set/v2 v2.1.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= -github.com/deckarep/golang-set/v2 v2.5.0 h1:hn6cEZtQ0h3J8kFrHR/NrzyOoTnjgW1+FmNJzQ7y/sA= github.com/deckarep/golang-set/v2 v2.5.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= -github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= github.com/decred/dcrd/lru v1.0.0 h1:Kbsb1SFDsIlaupWPwsPp+dkxiBY1frcS07PCPgotKz8= github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= @@ -583,6 +566,7 @@ github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6ps github.com/dgraph-io/badger v1.6.2 h1:mNw0qs90GVgGGWylh0umH5iag1j6n/PeJtNvL6KY/x8= github.com/dgraph-io/badger v1.6.2/go.mod h1:JW2yswe3V058sS0kZ2h/AXeDSqFjxnZcRrVH//y2UQE= github.com/dgraph-io/badger/v2 v2.2007.4/go.mod h1:vSw/ax2qojzbN6eXHIx6KPKtCSHJN/Uz0X0VPruTIhk= +github.com/dgraph-io/ristretto v0.0.4-0.20210318174700-74754f61e018/go.mod h1:MIonLggsKgZLUSt414ExgwNtlOL5MuEoAJP514mwGe8= github.com/dgraph-io/ristretto v0.1.0 h1:Jv3CGQHp9OjuMBSne1485aDpUkTKEcUqF+jm/LuerPI= github.com/dgraph-io/ristretto v0.1.0/go.mod h1:fux0lOrBhrVCJd3lcTHsIJhq1T2rokOu6v9Vcb3Q9ug= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= @@ -607,17 +591,15 @@ github.com/dop251/goja v0.0.0-20230806174421-c933cf95e127/go.mod h1:QMWlm50DNe14 github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y= github.com/dop251/goja_nodejs v0.0.0-20211022123610-8dd9abb0616d/go.mod h1:DngW8aVqWbuLRMHItjPUyqdj+HWPvnQe8V8y1nDpIbM= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= +github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385 h1:clC1lXBpe2kTj2VHdaIu9ajZQe4kcEY9j0NsnDDBZ3o= github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM= github.com/elastic/gosigar v0.14.2 h1:Dg80n8cr90OZ7x+bAax/QjoW/XqTI11RmA79ZwIm9/4= github.com/elastic/gosigar v0.14.2/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs= github.com/emicklei/dot v0.11.0 h1:Ase39UD9T9fRBOb5ptgpixrxfx8abVzNWZi2+lr53PI= github.com/emicklei/dot v0.11.0/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s= -github.com/emicklei/dot v1.6.2 h1:08GN+DD79cy/tzN6uLCT84+2Wk9u+wvqP+Hkx/dIR8A= github.com/emicklei/dot v1.6.2/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s= -github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg= github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -633,19 +615,14 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7 github.com/envoyproxy/protoc-gen-validate v0.10.1 h1:c0g45+xCJhdgFGw7a5QAfdS4byAbud7miNWJ1WwEVf8= github.com/envoyproxy/protoc-gen-validate v0.10.1/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= -github.com/ethereum/c-kzg-4844 v0.4.0 h1:3MS1s4JtA868KpJxroZoepdV0ZKBp3u/O5HcZ7R3nlY= github.com/ethereum/c-kzg-4844 v0.4.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= github.com/ethereum/go-ethereum v1.10.26/go.mod h1:EYFyF19u3ezGLD4RqOkLq+ZCXzYbLoNDdZlMt7kyKFg= -github.com/ethereum/go-ethereum v1.13.5 h1:U6TCRciCqZRe4FPXmy1sMGxTfuk8P7u2UoinF3VbaFk= github.com/ethereum/go-ethereum v1.13.5/go.mod h1:yMTu38GSuyxaYzQMViqNmQ1s3cE84abZexQmTgenWk0= github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= -github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/fatih/color v1.11.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/fatih/color v1.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w= github.com/fatih/color v1.14.1/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8WlgGZGg= +github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/felixge/fgprof v0.9.5 h1:8+vR6yu2vvSKn08urWyEuxx75NWPEvybbkBirEpsbVY= github.com/felixge/fgprof v0.9.5/go.mod h1:yKl+ERSa++RYOs32d8K6WEXCB4uXdLls4ZaZPpayhMM= @@ -653,15 +630,14 @@ github.com/ferranbt/fastssz v0.0.0-20210905181407-59cf6761a7d5/go.mod h1:S8yiDeA github.com/ferranbt/fastssz v0.1.2/go.mod h1:X5UPrE2u1UJjxHA8X54u04SBwdAQjG2sFtWs39YxyWs= github.com/ferranbt/fastssz v0.1.3 h1:ZI+z3JH05h4kgmFXdHuR1aWYsgrg7o+Fw7/NCzM16Mo= github.com/ferranbt/fastssz v0.1.3/go.mod h1:0Y9TEd/9XuFlh7mskMPfXiI2Dkw4Ddg9EyXt1W7MRvE= -github.com/ferranbt/fastssz v0.1.4 h1:OCDB+dYDEQDvAgtAGnTSidK1Pe2tW3nFV40XyMkTeDY= github.com/ferranbt/fastssz v0.1.4/go.mod h1:Ea3+oeoRGGLGm5shYAeDgu6PGUlcvQhE2fILyD9+tGg= github.com/fjl/gencodec v0.0.0-20230517082657-f9840df7b83e h1:bBLctRc7kr01YGvaDfgLbTwjFNW5jdp5y5rj8XXBHfY= github.com/fjl/gencodec v0.0.0-20230517082657-f9840df7b83e/go.mod h1:AzA8Lj6YtixmJWL+wkKoBGsLWy9gFrAzi4g+5bCKwpY= -github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 h1:FtmdgXiUlNeRsoNMFlKLDt+S+6hbjVMEW6RGQ7aUf7c= github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= github.com/flosch/pongo2 v0.0.0-20190707114632-bbf5a6c351f4/go.mod h1:T9YF2M40nIgbVgp3rreNmTged+9HrbNTIQf1PsaIiTA= +github.com/flosch/pongo2/v4 v4.0.2 h1:gv+5Pe3vaSVmiJvh/BZa82b7/00YUGm0PIyVVLop0Hw= github.com/flosch/pongo2/v4 v4.0.2/go.mod h1:B5ObFANs/36VwxxlgKpdchIJHMvHB562PW+BWPhwZD8= -github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= +github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ= github.com/flynn/noise v1.1.0 h1:KjPQoQCEFdZDiP03phOvGi11+SVVhBG2wOWAorLsstg= github.com/flynn/noise v1.1.0/go.mod h1:xbMo+0i6+IGbYdJhF31t2eR1BIU0CYc12+BNAKwUTag= github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk= @@ -672,7 +648,6 @@ github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7z github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= -github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/gabriel-vasile/mimetype v1.4.1 h1:TRWk7se+TOjCYgRth7+1/OYLNiRNIotknkFtf/dnN7Q= github.com/gabriel-vasile/mimetype v1.4.1/go.mod h1:05Vi0w3Y9c/lNvJOdmIwvrrAhX3rYhfQQCaf9VJcv7M= @@ -680,7 +655,6 @@ github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61 h1:IZqZOB2fydHte3kUgx github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61/go.mod h1:Q0X6pkwTILDlzrGEckF6HKjXe48EgsY/l7K7vhY4MW8= github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc= github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= -github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 h1:f6D9Hr8xV8uYKlyuj8XIruxlh9WjVjdh1gIicAS7ays= github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= github.com/gballet/go-verkle v0.0.0-20230607174250-df487255f46b h1:vMT47RYsrftsHSTQhqXwC3BYflo38OLC3Y4LtXtLyU0= github.com/gballet/go-verkle v0.0.0-20230607174250-df487255f46b/go.mod h1:CDncRYVRSDqwakm282WEkjfaAj1hxU/v5RXxk5nXOiI= @@ -691,30 +665,25 @@ github.com/gdamore/tcell/v2 v2.6.0/go.mod h1:be9omFATkdr0D9qewWW3d+MEvl5dha+Etb5 github.com/getkin/kin-openapi v0.53.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4= github.com/getkin/kin-openapi v0.61.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4= github.com/getsentry/sentry-go v0.18.0/go.mod h1:Kgon4Mby+FJ7ZWHFUAZgVaIa8sxHtnRJRLTXZr51aKQ= -github.com/getsentry/sentry-go v0.25.0 h1:q6Eo+hS+yoJlTO3uu/azhQadsD8V+jQn2D8VvX1eOyI= github.com/getsentry/sentry-go v0.25.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= github.com/ghemawat/stream v0.0.0-20171120220530-696b145b53b9/go.mod h1:106OIgooyS7OzLDOpUGgm9fA3bQENb/cFSyyBmMoJDs= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM= +github.com/gin-gonic/gin v1.8.1 h1:4+fr/el88TOO3ewCmQr8cx/CtZ/umlIRIs5M4NTNjf8= github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk= github.com/glendc/go-external-ip v0.1.0 h1:iX3xQ2Q26atAmLTbd++nUce2P5ht5P4uD4V7caSY/xg= github.com/glendc/go-external-ip v0.1.0/go.mod h1:CNx312s2FLAJoWNdJWZ2Fpf5O4oLsMFwuYviHjS4uJE= -github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= +github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= -github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4= github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E= -github.com/go-git/go-billy/v5 v5.0.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= -github.com/go-git/go-billy/v5 v5.1.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= -github.com/go-git/go-billy/v5 v5.3.1 h1:CPiOUAzKtMRvolEKw+bG1PLRpT7D3LIs3/3ey4Aiu34= github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= -github.com/go-git/go-git-fixtures/v4 v4.0.2-0.20200613231340-f56387b50c12/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw= -github.com/go-git/go-git/v5 v5.3.0 h1:8WKMtJR2j8RntEXR/uvTKagfEt4GYlwQ7mntE4+0GWc= github.com/go-git/go-git/v5 v5.3.0/go.mod h1:xdX4bWJ48aOrdhnl2XqHYstHbbp6+LFS4r4X+lNVprw= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1 h1:QbL/5oDUmRBzO9/Z7Seo6zf912W/a6Sr4Eu0G/3Jho0= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= @@ -739,11 +708,11 @@ github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab h1:xveKWz2iaueeTaUgdetzel+U7exyigDYBryyVfV/rZk= github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8= github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= -github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/go-openapi/analysis v0.22.2 h1:ZBmNoP2h5omLKr/srIC9bfqrUGzT6g6gNv03HE9Vpj0= github.com/go-openapi/analysis v0.22.2/go.mod h1:pDF4UbZsQTo/oNuRfAWWd4dAh4yuYf//LYorPTjrpvo= @@ -782,7 +751,6 @@ github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq github.com/go-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyLFfuisuzeidWPMPWmECqU= github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= @@ -790,28 +758,30 @@ github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4 github.com/go-yaml/yaml v2.1.0+incompatible h1:RYi2hDdss1u4YE7GwixGzWwVo47T8UQwnTLB6vQiq+o= github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= +github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU= github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= +github.com/gobwas/ws v1.2.1 h1:F2aeBZrm2NDsc7vbovKrWSogd4wvfAxg0FQ89/iqOTk= github.com/gobwas/ws v1.2.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY= github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gogo/googleapis v0.0.0-20180223154316-0cd9801be74a/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= +github.com/gogo/googleapis v1.4.1 h1:1Yx4Myt7BxzvUr5ldGSbwYiZG6t9wGBZ+8/fX3Wvtq0= github.com/gogo/googleapis v1.4.1/go.mod h1:2lpHqI5OcWCtVElxXnPt+s8oJvMpySlOyM6xDCrzib4= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/gogo/status v1.1.0 h1:+eIkrewn5q6b30y+g/BJINVVdi2xH7je5MPJ3ZPK3JA= github.com/gogo/status v1.1.0/go.mod h1:BFv9nrluPLmrS0EmGVvLaPNmRosr9KapBYd5/hpY1WM= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= -github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/gddo v0.0.0-20200528160355-8d077c1d8f4c h1:HoqgYR60VYu5+0BuG6pjeGp7LKEPZnHt+dUClx9PeIs= github.com/golang/gddo v0.0.0-20200528160355-8d077c1d8f4c/go.mod h1:sam69Hju0uq+5uvLJUMDlsKlQ21Vrs1Kd/1YFPNYdOU= @@ -856,7 +826,6 @@ github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiu github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk= github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y= github.com/gomodule/redigo v1.7.1-0.20190724094224-574c33c3df38/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= @@ -923,7 +892,6 @@ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= @@ -936,6 +904,7 @@ github.com/googleapis/gax-go/v2 v2.11.0/go.mod h1:DxmR61SGKkGLa2xigwuZIQpkCI2S5i github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c h1:7lF+Vz0LqiRidnzC1Oq86fpX1q/iEv2KJdrCtttYjT4= github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= @@ -944,7 +913,6 @@ github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoA github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= -github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gostaticanalysis/comment v1.4.2 h1:hlnx5+S2fY9Zo9ePo4AhgYsYHbM2+eAv8m/s1JiCd6Q= github.com/gostaticanalysis/comment v1.4.2/go.mod h1:KLUTGDv6HOCotCH8h2erHKmpci2ZoR8VPu34YA2uzdM= @@ -967,7 +935,6 @@ github.com/gxed/hashland/murmur3 v0.0.1 h1:SheiaIt0sda5K+8FLz952/1iWS9zrnKsEJaOJ github.com/gxed/hashland/murmur3 v0.0.1/go.mod h1:KjXop02n4/ckmZSnY2+HKcLud/tcmvhST0bie/0lS48= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE= github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= @@ -978,7 +945,6 @@ github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9 github.com/hashicorp/go-retryablehttp v0.7.4 h1:ZQgVdpTdAL7WpMIwLzCfbalOcSUdkDZnpUv3/+BxzFA= github.com/hashicorp/go-retryablehttp v0.7.4/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= @@ -986,24 +952,22 @@ github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d h1:dg1dEPuW github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/golang-lru/v2 v2.0.5 h1:wW7h1TG88eUIJ2i69gaE3uNVtEPIagzhGvHgwfx2Vm4= github.com/hashicorp/golang-lru/v2 v2.0.5/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/herumi/bls-eth-go-binary v0.0.0-20210917013441-d37c07cfda4e/go.mod h1:luAnRm3OsMQeokhGzpYmc0ZKwawY7o87PUEP11Z7r7U= github.com/herumi/bls-eth-go-binary v1.28.1 h1:fcIZ48y5EE9973k05XjE8+P3YiQgjZz4JI/YabAm8KA= github.com/herumi/bls-eth-go-binary v1.28.1/go.mod h1:luAnRm3OsMQeokhGzpYmc0ZKwawY7o87PUEP11Z7r7U= -github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7 h1:3JQNjnMRil1yD0IfZKHF9GxxWKDJGj8I0IqOUol//sw= github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7/go.mod h1:5GuXa7vkL8u9FkFuWdVvfR5ix8hRB7DbOAaYULamFpc= -github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= github.com/holiman/goevmlab v0.0.0-20231201084119-c73b3c97929c h1:J973NLskKmFIj3EGfpQ1ztUQKdwyJ+fG34638ief0eA= github.com/holiman/goevmlab v0.0.0-20231201084119-c73b3c97929c/go.mod h1:K6KFgcQq1U9ksldcRyLYcwtj4nUTPn4rEaZtX4Gjofc= github.com/holiman/uint256 v1.2.3/go.mod h1:SC8Ryt4n+UBbPbIBKaG9zbbDlp4jOru9xFZmPzLUTxw= -github.com/holiman/uint256 v1.2.4 h1:jUc4Nk8fm9jZabQuqr2JzednajVmBpC+oiTiXZJEApU= github.com/holiman/uint256 v1.2.4/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= github.com/hydrogen18/memlistener v0.0.0-20141126152155-54553eb933fb/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE= +github.com/hydrogen18/memlistener v1.0.0 h1:JR7eDj8HD6eXrc5fWLbSUnfcQFL06PYvCc0DKQnWfaU= github.com/hydrogen18/memlistener v1.0.0/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE= github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/ianlancetaylor/cgosymbolizer v0.0.0-20200424224625-be1b05b0b279 h1:IpTHAzWv1pKDDWeJDY5VOHvqc2T9d3C8cPKEf2VPqHE= @@ -1013,8 +977,8 @@ github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639 h1:mV02weK github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= github.com/ianlancetaylor/demangle v0.0.0-20230524184225-eabc099b10ab/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw= +github.com/ianlancetaylor/demangle v0.0.0-20240312041847-bd984b5ce465 h1:KwWnWVWCNtNq/ewIX7HIKnELmEx2nDP42yskD/pi7QE= github.com/ianlancetaylor/demangle v0.0.0-20240312041847-bd984b5ce465/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw= -github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= @@ -1110,12 +1074,11 @@ github.com/iris-contrib/httpexpect/v2 v2.12.1/go.mod h1:7+RB6W5oNClX7PTwJgJnsQP3 github.com/iris-contrib/i18n v0.0.0-20171121225848-987a633949d0/go.mod h1:pMCz62A0xJL6I+umB2YTlFRwWXaDFA0jy+5HzGiJjqI= github.com/iris-contrib/jade v1.1.4/go.mod h1:EDqR+ur9piDl6DUgs6qRrlfzmlx/D5UybogqrXvJTBE= github.com/iris-contrib/schema v0.0.1/go.mod h1:urYA3uvUNG1TIIjOSCzHr9/LmbQo8LrOcOqfqxa4hXw= +github.com/iris-contrib/schema v0.0.6 h1:CPSBLyx2e91H2yJzPuhGuifVRnZBBJ3pCOMbOvPZaTw= github.com/iris-contrib/schema v0.0.6/go.mod h1:iYszG0IOsuIsfzjymw1kMzTL8YQcCWlm65f3wX8J5iA= -github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= github.com/jbenet/go-cienv v0.1.0 h1:Vc/s0QbQtoxX8MwwSLWWh+xNNZvM3Lw7NsTcHrvvhMc= github.com/jbenet/go-cienv v0.1.0/go.mod h1:TqNnHUmJgXau0nCzC7kXWeotg3J9W34CUv5Djy1+FlA= -github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jbenet/go-temp-err-catcher v0.1.0 h1:zpb3ZH6wIE8Shj2sKS+khgRvf7T7RABoLk/+KKHggpk= github.com/jbenet/go-temp-err-catcher v0.1.0/go.mod h1:0kJRvmDZXNMIiJirNPEYfhpPwbGVtZVWC34vc5WLsDk= @@ -1128,7 +1091,7 @@ github.com/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267/go.mod h1:h1n github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= +github.com/jessevdk/go-flags v1.5.0 h1:1jKYvbxEjfUl0fmqTCOfonvskHHXMjBySTLW4y9LFvc= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= @@ -1165,20 +1128,24 @@ github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1 github.com/karalabe/usb v0.0.2/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= github.com/karalabe/usb v0.0.3-0.20230711191512-61db3e06439c h1:AqsttAyEyIEsNz5WLRwuRwjiT5CMDUfLk6cFJDVPebs= github.com/karalabe/usb v0.0.3-0.20230711191512-61db3e06439c/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= +github.com/kataras/blocks v0.0.7 h1:cF3RDY/vxnSRezc7vLFlQFTYXG/yAr1o7WImJuZbzC4= github.com/kataras/blocks v0.0.7/go.mod h1:UJIU97CluDo0f+zEjbnbkeMRlvYORtmc1304EeyXf4I= github.com/kataras/golog v0.0.9/go.mod h1:12HJgwBIZFNGL0EJnMRhmvGA0PQGx8VFwrZtM4CqbAk= +github.com/kataras/golog v0.1.8 h1:isP8th4PJH2SrbkciKnylaND9xoTtfxv++NB+DF0l9g= github.com/kataras/golog v0.1.8/go.mod h1:rGPAin4hYROfk1qT9wZP6VY2rsb4zzc37QpdPjdkqVw= github.com/kataras/iris/v12 v12.0.1/go.mod h1:udK4vLQKkdDqMGJJVd/msuMtN6hpYJhg/lSzuxjhO+U= +github.com/kataras/iris/v12 v12.2.0 h1:WzDY5nGuW/LgVaFS5BtTkW3crdSKJ/FEgWnxPnIVVLI= github.com/kataras/iris/v12 v12.2.0/go.mod h1:BLzBpEunc41GbE68OUaQlqX4jzi791mx5HU04uPb90Y= github.com/kataras/jwt v0.1.8/go.mod h1:Q5j2IkcIHnfwy+oNY3TVWuEBJNw0ADgCcXK9CaZwV4o= github.com/kataras/neffos v0.0.10/go.mod h1:ZYmJC07hQPW67eKuzlfY7SO3bC0mw83A3j6im82hfqw= github.com/kataras/neffos v0.0.21/go.mod h1:FeGka8lu8cjD2H+0OpBvW8c6xXawy3fj5VX6xcIJ1Fg= github.com/kataras/pio v0.0.0-20190103105442-ea782b38602d/go.mod h1:NV88laa9UiiDuX9AhMbDPkGYSPugBOV6yTZB1l2K9Z0= +github.com/kataras/pio v0.0.11 h1:kqreJ5KOEXGMwHAWHDwIl+mjfNCPhAwZPa8gK7MKlyw= github.com/kataras/pio v0.0.11/go.mod h1:38hH6SWH6m4DKSYmRhlrCJ5WItwWgCVrTNU62XZyUvI= +github.com/kataras/sitemap v0.0.6 h1:w71CRMMKYMJh6LR2wTgnk5hSgjVNB9KL60n5e2KHvLY= github.com/kataras/sitemap v0.0.6/go.mod h1:dW4dOCNs896OR1HmG+dMLdT7JjDk7mYBzoIRwuj5jA4= +github.com/kataras/tunnel v0.0.4 h1:sCAqWuJV7nPzGrlb0os3j49lk2JhILT0rID38NHNLpA= github.com/kataras/tunnel v0.0.4/go.mod h1:9FkU4LaeifdMWqZu7o20ojmW4B7hdhv2CMLwfnHGpYw= -github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= -github.com/kevinburke/ssh_config v1.1.0 h1:pH/t1WS9NzT8go394IqZeJTMHVm6Cr6ZJ6AQ+mdNo/o= github.com/kevinburke/ssh_config v1.1.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kilic/bls12-381 v0.1.0 h1:encrdjqKMEvabVQ7qYOKu1OvhqpK4s47wDYtNiPtlp4= github.com/kilic/bls12-381 v0.1.0/go.mod h1:vDTTHJONJ6G+P2R74EhnyotQDTliQDnFEwhdmfzw1ig= @@ -1198,7 +1165,6 @@ github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9 github.com/klauspost/compress v1.16.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/compress v1.17.6 h1:60eq2E/jlfwQXtvZEeBUYADs+BwKBWURIY+Gj2eRGjI= github.com/klauspost/compress v1.17.6/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= -github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/klauspost/cpuid v1.2.1 h1:vJi+O/nMdFt0vqm8NZBI6wzALWdA2X+egi0ogNyrC/w= github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= @@ -1207,8 +1173,8 @@ github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa02 github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= -github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM= github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/koron/go-ssdp v0.0.4 h1:1IDwrghSKYM7yLf7XCzbByg2sJ/JcNOZRXS2jczTwz0= @@ -1217,24 +1183,23 @@ github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFB github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/labstack/echo/v4 v4.1.11/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvfxNnFqi74g= github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg= github.com/labstack/echo/v4 v4.9.0/go.mod h1:xkCDAdFCIf8jsFQ5NnbK7oqaF/yU1A1X20Ltm0OvSks= +github.com/labstack/echo/v4 v4.10.0 h1:5CiyngihEO4HXsz3vVsJn7f8xAlWwRr3aY6Ih280ZKA= github.com/labstack/echo/v4 v4.10.0/go.mod h1:S/T/5fy/GigaXnHTkh0ZGe4LpkkQysvRjFMSUTkDRNQ= github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= github.com/labstack/gommon v0.3.1/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= +github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8= github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= -github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= +github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80 h1:6Yzfa6GP0rIo/kULo2bwGEkFvCePZ3qHDDTC3/J9Swo= github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs= github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= github.com/leodido/go-urn v1.2.3 h1:6BE2vPT0lqoz3fmOesHZiaiFh7889ssCo2GMvLCfiuA= @@ -1288,7 +1253,9 @@ github.com/lunixbochs/vtclean v1.0.0 h1:xu2sLAri4lGiovBDQKxl5mrXyESr3gUr5m5SM5+L github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= github.com/lyft/protoc-gen-star/v2 v2.0.1/go.mod h1:RcCdONR2ScXaYnQC5tUzxzlpA3WVYF7/opLeUgcQs/o= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/mailgun/raymond/v2 v2.0.48 h1:5dmlB680ZkFG2RN/0lvTAghrSxIESeu9/2aeDqACtjw= github.com/mailgun/raymond/v2 v2.0.48/go.mod h1:lsgvL50kgt1ylcFJYZiULi5fjPBkkhNfj4KA0W54Z18= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= @@ -1298,36 +1265,27 @@ github.com/manifoldco/promptui v0.7.0 h1:3l11YT8tm9MnwGFQ4kETwkzpAwY2Jt9lCrumCUW github.com/manifoldco/promptui v0.7.0/go.mod h1:n4zTdgP0vr0S3w7/O/g98U+e0gwLScEXGwov2nIKuGQ= github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd h1:br0buuQ854V8u83wA0rVZ8ttrq5CpaPZdvrK0LP2lOk= github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd/go.mod h1:QuCEs1Nt24+FYQEqAAncTDPJIuGs+LxK1MCiFL25pMU= -github.com/matryer/is v1.3.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA= github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ= github.com/matryer/try v0.0.0-20161228173917-9ac251b645a2/go.mod h1:0KeJpeMD6o+O4hW7qJOT7vyQPKrWmj26uf5wMc/IiIs= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= -github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= -github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= @@ -1339,9 +1297,9 @@ github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQth github.com/mediocregopher/mediocre-go-lib v0.0.0-20181029021733-cb65787f37ed/go.mod h1:dSsfyI2zABAdhcbvkXqgxOxrCsbYeHCPgrZkku60dSg= github.com/mediocregopher/radix/v3 v3.3.0/go.mod h1:EmfVyvspXz1uZEyPBMyGK+kjWiKQGvsUt6O3Pj+LDCQ= github.com/mediocregopher/radix/v3 v3.8.1/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8= -github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc= +github.com/microcosm-cc/bluemonday v1.0.23 h1:SMZe2IGa0NuHvnVNAZ+6B38gsTbi5e4sViiWJyDDqFY= github.com/microcosm-cc/bluemonday v1.0.23/go.mod h1:mN70sk7UkkF8TUr2IGBpNN0jAgStuPzlK76QuruE/z4= github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4= github.com/miekg/dns v1.1.58/go.mod h1:Ypv+3b/KadlvW9vJfXOTf300O4UqaHFzFCuHz+rPkBY= @@ -1353,28 +1311,22 @@ github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcs github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 h1:lYpkrQH5ajf0OXOcUbGjvZxxijuBwbbmlSxLiuofa+g= github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ= github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE= -github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g= github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= github.com/minio/sha256-simd v0.0.0-20190131020904-2d45a736cd16/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U= github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM= -github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ= github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw= -github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A= github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4= -github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY= github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU= github.com/mmcloughlin/profile v0.1.1 h1:jhDmAqPyebOsVDOCICJoINoLb/AnLBaUw58nFzxWS2w= github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU= @@ -1430,7 +1382,6 @@ github.com/multiformats/go-varint v0.0.5/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXS github.com/multiformats/go-varint v0.0.6/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/nEGOHFS8= github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU= -github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= @@ -1448,11 +1399,9 @@ github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OS github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= @@ -1460,7 +1409,6 @@ github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108 github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= -github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o= @@ -1472,7 +1420,6 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= -github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8= github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= @@ -1483,6 +1430,7 @@ github.com/opencontainers/runtime-spec v1.2.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/ github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= +github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde h1:x0TT0RDC7UhAVbbWWBzr41ElhJx5tXPWkIHA2HWPRuw= github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= @@ -1493,7 +1441,9 @@ github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhM github.com/pborman/uuid v1.2.1 h1:+ZZIw58t/ozdjRaXh/3awHfmWRbzYxJoAdNJxe/3pvw= github.com/pborman/uuid v1.2.1/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pelletier/go-toml v1.9.1 h1:a6qW1EVNZWH9WGI6CsYdD8WAylkoXBS5yv0XHlh17Tc= github.com/pelletier/go-toml v1.9.1/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/pelletier/go-toml/v2 v2.0.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwbMiyQg= github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas= github.com/petar/GoLLRB v0.0.0-20210522233825-ae3b015fd3e9 h1:1/WtZae0yGtPq+TI6+Tv1WTxkukpXeMlviSxvL7SRgk= github.com/petar/GoLLRB v0.0.0-20210522233825-ae3b015fd3e9/go.mod h1:x3N5drFsm2uilKKuuYo6LdyD8vZAW55sH/9w+pbo1sw= @@ -1501,14 +1451,14 @@ github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssy github.com/peterh/liner v1.2.0 h1:w/UPXyl5GfahFxcTOz2j9wCIHNI+pUPr2laqpojKNCg= github.com/peterh/liner v1.2.0/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0= github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e h1:aoZm08cpOy4WuID//EZDgcC4zIxODThtZNPirFr42+A= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/polydawn/refmt v0.0.0-20190221155625-df39d6c2d992/go.mod h1:uIp+gprXxxrWSjjklXD+mN4wed/tMfjMMmN/9+JsA9o= github.com/polydawn/refmt v0.0.0-20190807091052-3d65705ee9f1/go.mod h1:uIp+gprXxxrWSjjklXD+mN4wed/tMfjMMmN/9+JsA9o= @@ -1517,9 +1467,7 @@ github.com/polydawn/refmt v0.89.0/go.mod h1:/zvteZs/GwLtCgZ4BL6CBsk9IKIlexP43ObX github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= -github.com/princjef/gomarkdoc v0.4.1 h1:Ubt5OiHYi2PdxrDkWMeWM4ROrbvAGkIXBz3PquxglBM= github.com/princjef/gomarkdoc v0.4.1/go.mod h1:+o04FW4GNL2vPr/35yxMV/8eXjhsdNBBPMVVDOOTLec= -github.com/princjef/mageutil v1.0.0 h1:1OfZcJUMsooPqieOz2ooLjI+uHUo618pdaJsbCXcFjQ= github.com/princjef/mageutil v1.0.0/go.mod h1:mkShhaUomCYfAoVvTKRcbAs8YSVPdtezI5j6K+VXhrs= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= @@ -1529,7 +1477,6 @@ github.com/prometheus/client_golang v1.12.0/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrb github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk= github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA= github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= -github.com/prometheus/client_golang v1.20.0 h1:jBzTZ7B099Rg24tny+qngoynol8LtVYlA2bqx3vEloI= github.com/prometheus/client_golang v1.20.0/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -1539,7 +1486,6 @@ github.com/prometheus/client_model v0.2.1-0.20210607210712-147c58e9608a/go.mod h github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= github.com/prometheus/client_model v0.6.0 h1:k1v3CzpSRUTrKMppY35TLwPvxHqBu0bYgxZzqGIgaos= github.com/prometheus/client_model v0.6.0/go.mod h1:NTQHnmxFpouOD0DpvP4XujX3CdOAGQPoaGhyTchlyt8= -github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= @@ -1548,7 +1494,6 @@ github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+ github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY= github.com/prometheus/common v0.47.0 h1:p5Cz0FNHo7SnWOmWmoRozVcjEp0bIVU8cV7OShpjL1k= github.com/prometheus/common v0.47.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc= -github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= @@ -1557,7 +1502,6 @@ github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1 github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= -github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/prometheus/prom2json v1.3.0 h1:BlqrtbT9lLH3ZsOVhXPsHzFrApCTKRifB7gjJuypu6Y= github.com/prometheus/prom2json v1.3.0/go.mod h1:rMN7m0ApCowcoDlypBHlkNbp5eJQf/+1isKykIP5ZnM= @@ -1571,13 +1515,10 @@ github.com/prysmaticlabs/fastssz v0.0.0-20221107182844-78142813af44/go.mod h1:MA github.com/prysmaticlabs/fastssz v0.0.0-20241008181541-518c4ce73516 h1:xuVAdtz5ShYblG2sPyb4gw01DF8InbOI/kBCQjk7NiM= github.com/prysmaticlabs/go-bitfield v0.0.0-20210809151128-385d8c5e3fb7 h1:0tVE4tdWQK9ZpYygoV7+vS6QkDvQVySboMVEIxBJmXw= github.com/prysmaticlabs/go-bitfield v0.0.0-20210809151128-385d8c5e3fb7/go.mod h1:wmuf/mdK4VMD+jA9ThwcUKjg3a2XWM9cVfFYjDyY4j4= -github.com/prysmaticlabs/go-bitfield v0.0.0-20240328144219-a1caa50c3a1e h1:ATgOe+abbzfx9kCPeXIW4fiWyDdxlwHw07j8UGhdTd4= github.com/prysmaticlabs/go-bitfield v0.0.0-20240328144219-a1caa50c3a1e/go.mod h1:wmuf/mdK4VMD+jA9ThwcUKjg3a2XWM9cVfFYjDyY4j4= -github.com/prysmaticlabs/go-ssz v0.0.0-20210121151755-f6208871c388 h1:4bD+ujqGfY4zoDUF3q9MhdmpPXzdp03DYUIlXeQ72kk= github.com/prysmaticlabs/go-ssz v0.0.0-20210121151755-f6208871c388/go.mod h1:VecIJZrewdAuhVckySLFt2wAAHRME934bSDurP8ftkc= github.com/prysmaticlabs/gohashtree v0.0.0-20240129161530-f61e0ca8e562/go.mod h1:BFdtALS+Ffhg3lGQIHv9HDWuHS8cTvHZzrHWxwOtGOs= github.com/prysmaticlabs/gohashtree v0.0.3-alpha/go.mod h1:4pWaT30XoEx1j8KNJf3TV+E3mQkaufn7mf+jRNb/Fuk= -github.com/prysmaticlabs/gohashtree v0.0.4-beta h1:H/EbCuXPeTV3lpKeXGPpEV9gsUpkqOOVnWapUyeWro4= github.com/prysmaticlabs/gohashtree v0.0.4-beta/go.mod h1:BFdtALS+Ffhg3lGQIHv9HDWuHS8cTvHZzrHWxwOtGOs= github.com/prysmaticlabs/prombbolt v0.0.0-20210126082820-9b7adba6db7c h1:9PHRCuO/VN0s9k+RmLykho7AjDxblNYI5bYKed16NPU= github.com/prysmaticlabs/prombbolt v0.0.0-20210126082820-9b7adba6db7c/go.mod h1:ZRws458tYHS/Zs936OQ6oCrL+Ict5O4Xpwve1UQ6C9M= @@ -1599,23 +1540,15 @@ github.com/raulk/go-watchdog v1.3.0 h1:oUmdlHxdkXRJlwfG0O9omj8ukerm8MEQavSiDTEtB github.com/raulk/go-watchdog v1.3.0/go.mod h1:fIvOnLbF0b0ZwkB9YU4mOW9Did//4vPZtDqv66NfsMU= github.com/rivo/tview v0.0.0-20230208211350-7dfff1ce7854 h1:/IIOjnKLbuO5YtZUZaJVw9fc062ChPlaGWEBmJ6jyGY= github.com/rivo/tview v0.0.0-20230208211350-7dfff1ce7854/go.mod h1:lBUy/T5kyMudFzWUH/C2moN+NlU5qF505vzOyINXuUQ= -github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= -github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rjeczalik/notify v0.9.1 h1:CLCKso/QK1snAlnhNR/CNvNiFU2saUtjV0bx3EwNeCE= github.com/rjeczalik/notify v0.9.1/go.mod h1:rKwnCoCGeuQnwBtTSPL9Dad03Vh2n40ePRrjvIXnJho= github.com/rocket-pool/go-merkletree v1.0.1-0.20220406020931-c262d9b976dd h1:p9KuetSKB9nte9I/MkkiM3pwKFVQgqxxPTQ0y56Ff6s= github.com/rocket-pool/go-merkletree v1.0.1-0.20220406020931-c262d9b976dd/go.mod h1:UE9fof8P7iESVtLn1K9CTSkNRYVFHZHlf96RKbU33kA= -github.com/rocket-pool/rocketpool-go v1.8.4-0.20241122223132-c5f2be18f72b/go.mod h1:f2TVsMOYmCwaJOhshG2zRoX89PZmvCkCD7UYJ9waRkI= -github.com/rocket-pool/rocketpool-go v1.8.4-0.20250512200217-48e87a4a441b h1:T6h7a9JyhL+kfQDUKsWphwR7PJizthWxpZzpjN8SGCo= -github.com/rocket-pool/rocketpool-go v1.8.4-0.20250512200217-48e87a4a441b/go.mod h1:f2TVsMOYmCwaJOhshG2zRoX89PZmvCkCD7UYJ9waRkI= -github.com/rocket-pool/rocketpool-go v1.8.4-0.20250516194146-403b19d31a82 h1:aqXxniftRdjBInazBwWU/AgZMagf6iexry8bbXBsO1A= -github.com/rocket-pool/rocketpool-go v1.8.4-0.20250516194146-403b19d31a82/go.mod h1:f2TVsMOYmCwaJOhshG2zRoX89PZmvCkCD7UYJ9waRkI= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.5.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= @@ -1626,11 +1559,9 @@ github.com/rs/cors v1.8.2/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/samber/lo v1.36.0/go.mod h1:HLeWcJRRyLKp3+/XBJvOrerCQn9mhdKMHyd7IRlgeQ8= -github.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U= github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g= github.com/schollz/progressbar/v3 v3.3.4/go.mod h1:Rp5lZwpgtYmlvmGo1FyDwXMqagyRBQYSDwzlP9QDu84= github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw= @@ -1639,26 +1570,22 @@ github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNX github.com/sethvargo/go-password v0.2.0 h1:BTDl4CC/gjf/axHMaDQtw507ogrXLci6XRiLc7i/UHI= github.com/sethvargo/go-password v0.2.0/go.mod h1:Ym4Mr9JXLBycr02MFuVQ/0JHidNetSgbzutTr3zsYXE= github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= -github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= +github.com/shirou/gopsutil/v3 v3.23.1 h1:a9KKO+kGLKEvcPIs4W62v0nu3sciVDOOOPUD0Hz7z/4= github.com/shirou/gopsutil/v3 v3.23.1/go.mod h1:NN6mnm5/0k8jw4cBfCnJtr5L7ErOTg18tMNpgFkn0hA= github.com/shirou/gopsutil/v3 v3.23.2 h1:PAWSuiAszn7IhPMBtXsbSCafej7PqUOvY6YywlQUExU= github.com/shirou/gopsutil/v3 v3.23.2/go.mod h1:gv0aQw33GLo3pG8SiWKiQrbDzbRY1K80RyZJ7V4Th1M= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= -github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= github.com/smartystreets/goconvey v0.0.0-20190222223459-a17d461953aa/go.mod h1:2RVY1rIf+2J2o/IM9+vPq9RzmHDSseB7FoXiSNIUsoU= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/smartystreets/goconvey v1.7.2/go.mod h1:Vw0tHAZW6lzCRk3xgdin6fKYcG+G3Pg9vgXWeJpQFMM= -github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.10.0/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= @@ -1692,7 +1619,6 @@ github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= -github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbeoEm4= github.com/supranational/blst v0.3.11/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= @@ -1701,18 +1627,13 @@ github.com/tdewolff/parse/v2 v2.6.4/go.mod h1:woz0cgbLwFdtbjJu8PIKxhW05KplTFQkOd github.com/tdewolff/test v1.0.7/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE= github.com/thomaso-mirodin/intmath v0.0.0-20160323211736-5dc6d854e46e h1:cR8/SYRgyQCt5cNCMniB/ZScMkhI9nk8U5C7SbISXjo= github.com/thomaso-mirodin/intmath v0.0.0-20160323211736-5dc6d854e46e/go.mod h1:Tu4lItkATkonrYuvtVjG0/rhy15qrNGNTjPdaphtZ/8= -github.com/tklauser/go-sysconf v0.3.5/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI= github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI= github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= -github.com/tklauser/go-sysconf v0.3.13 h1:GBUpcahXSpR2xN01jhkNAbTLRk2Yzgggk8IM08lq3r4= github.com/tklauser/go-sysconf v0.3.13/go.mod h1:zwleP4Q4OehZHGn4CYZDipCgg9usW5IJePewFCGVEa0= -github.com/tklauser/numcpus v0.2.2/go.mod h1:x3qojaO3uyYt0i56EW/VUYs7uBvdl2fkfZFu0T9wgjM= github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4= github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= -github.com/tklauser/numcpus v0.7.0 h1:yjuerZP127QG9m5Zh/mSO4wqurYil27tHrqwRoRjpr4= github.com/tklauser/numcpus v0.7.0/go.mod h1:bb6dMVcj8A42tSE7i32fsIUCbQNllK5iDguyOZRUzAY= github.com/trailofbits/go-mutexasserts v0.0.0-20230328101604-8cdbc5f3d279/go.mod h1:GA3+Mq3kt3tYAfM0WZCu7ofy+GW9PuGysHfhr+6JX7s= -github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= github.com/uber/jaeger-client-go v2.25.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= github.com/ucarion/urlpath v0.0.0-20200424170820-7ccc79b76bbb/go.mod h1:ikPs9bRWicNw3S7XpJ8sK/smGwU9WcSVU3dy9qahYBM= @@ -1725,7 +1646,6 @@ github.com/urfave/cli v1.22.10/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60Nt github.com/urfave/cli v1.22.12 h1:igJgVw1JdKH+trcLWLeLwZjU9fEfPesQ+9/e4MQ44S8= github.com/urfave/cli v1.22.12/go.mod h1:sSBEIC79qR6OvcmsD4U3KABeOTxDqQtdDnaFuUN30b8= github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= -github.com/urfave/cli/v2 v2.26.0 h1:3f3AMg3HpThFNT4I++TKOejZO8yU55t3JnnSr4S4QEI= github.com/urfave/cli/v2 v2.26.0/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4= github.com/uudashr/gocognit v1.0.5/go.mod h1:wgYz0mitoKOTysqxTDMOUXg+Jb5SvtihkfmugIZYpEA= @@ -1776,7 +1696,6 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1: github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= -github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI= github.com/yosssi/ace v0.0.5/go.mod h1:ALfIzm2vT7t5ZE7uoIZqF3TQ7SAOyupFZnkrF5id+K0= @@ -1793,8 +1712,7 @@ github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1 github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= -github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= -go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= +go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= go.mongodb.org/mongo-driver v1.13.1 h1:YIc7HTYsKndGK4RFzJ3covLz1byri52x0IoMB0Pt/vk= go.mongodb.org/mongo-driver v1.13.1/go.mod h1:wcDf1JBCXy2mOW0bWHwO/IOYqdca1MPCwDtFu/Z9+eo= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= @@ -1805,21 +1723,17 @@ go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/otel v1.17.0 h1:MW+phZ6WZ5/uk2nd93ANk/6yJ+dVrvNWUjGhnnFU5jM= go.opentelemetry.io/otel v1.17.0/go.mod h1:I2vmBGtFaODIVMBSTPVDlJSzBDNf93k60E6Ft0nyjo0= go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw= go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8= -go.opentelemetry.io/otel/metric v1.17.0 h1:iG6LGVz5Gh+IuO0jmgvpTB6YVrCGngi8QGm+pMd8Pdc= go.opentelemetry.io/otel/metric v1.17.0/go.mod h1:h4skoxdZI17AxwITdmdZjjYJQH5nzijUUjm+wtPph5o= go.opentelemetry.io/otel/metric v1.29.0 h1:vPf/HFWTNkPu1aYeIsc98l4ktOQaL6LeSoeV2g+8YLc= go.opentelemetry.io/otel/metric v1.29.0/go.mod h1:auu/QWieFVWx+DmQOUMgj0F8LHWdgalxXqvp7BII/W8= go.opentelemetry.io/otel/sdk v1.17.0/go.mod h1:U87sE0f5vQB7hwUoW98pW5Rz4ZDuCFBZFNUBlSgmDFQ= -go.opentelemetry.io/otel/trace v1.17.0 h1:/SWhSRHmDPOImIAetP1QAeMnZYiQXrTy4fMMYOdSKWQ= go.opentelemetry.io/otel/trace v1.17.0/go.mod h1:I/4vKTgFclIsXRVucpH25X0mpFSczM7aHeaz0ZBLWjY= go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4= go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= @@ -1843,7 +1757,6 @@ golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -1854,21 +1767,15 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= golang.org/x/crypto v0.2.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= -golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= -golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1881,9 +1788,7 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= -golang.org/x/exp v0.0.0-20240213143201-ec583247a57a h1:HinSgX1tJRX3KsL//Gxynpw5CTOAIPhgL4W8PNiIpVE= golang.org/x/exp v0.0.0-20240213143201-ec583247a57a/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc= -golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa h1:ELnwvuAXPNtPk1TJRuGkI9fDTwym6AYBu0qzT8AcHdI= golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ= golang.org/x/exp/typeparams v0.0.0-20231108232855-2478ac86f678/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= @@ -1899,7 +1804,6 @@ golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRu golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= @@ -1913,7 +1817,6 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= @@ -1961,7 +1864,6 @@ golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= -golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= @@ -1975,14 +1877,10 @@ golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= -golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= -golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= -golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= -golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -2000,11 +1898,7 @@ golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw= golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4= -golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= -golang.org/x/oauth2 v0.12.0/go.mod h1:A74bZ3aGXgCY0qaIC9Ahg6Lglin4AMAco8cIv9baba4= -golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o= golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= -golang.org/x/perf v0.0.0-20230113213139-801c7ef9e5c5/go.mod h1:UBKtEnL8aqnd+0JHqZ+2qoMDwtuy6cYhhKNoHLBiTQc= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -2018,12 +1912,9 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= -golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -2048,10 +1939,8 @@ golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -2088,11 +1977,8 @@ golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210412220455-f1c623a9e750/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -2125,29 +2011,21 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/telemetry v0.0.0-20240208230135-b75ee8823808/go.mod h1:KG1lNk5ZFNssSZLrpVb4sMXKMpGwGXOxSG3rnu2gZQQ= golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457/go.mod h1:pRgIJT+bRLFKnoM1ldnzKoxTIn14Yxz928LQRYYgIN0= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= -golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -2164,10 +2042,8 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= -golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -2233,10 +2109,8 @@ golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.12.0/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= @@ -2273,8 +2147,6 @@ google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBz google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= google.golang.org/api v0.45.0/go.mod h1:ISLIJCedJolbZvDfAk+Ctuq5hf+aJ33WgtUsfyFoLXA= google.golang.org/api v0.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg= -google.golang.org/api v0.118.0/go.mod h1:76TtD3vkgmZ66zZzp72bUUklpmQmKlhh6sYtIjYK+5E= -google.golang.org/api v0.126.0/go.mod h1:mBwVAtz+87bEN6CbA1GtZPDOqY2R5ONPqJeIlvyo4Aw= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -2331,17 +2203,6 @@ google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVix google.golang.org/genproto v0.0.0-20230323212658-478b75c54725/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A= google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU= -google.golang.org/genproto v0.0.0-20230525234025-438c736192d0/go.mod h1:9ExIQyXL5hZrHzQceCwuSYwZZ5QZBazOcprJ5rgs3lY= -google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc h1:8DyZCyvI8mE1IdLy/60bS+52xfymkE72wv1asokgtao= -google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:xZnkP7mREFX5MORlOPEzLMr+90PPZQ2QWzrVTWfAq64= -google.golang.org/genproto/googleapis/api v0.0.0-20230526203410-71b5a4ffd15e/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= -google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc h1:kVKPf/IiYSBWEWtkIn6wZXwWGCnLKcC8oWfZvXjsGnM= -google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= -google.golang.org/genproto/googleapis/bytestream v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:ylj+BE99M198VPbBh6A8d9n3w8fChvyLK3wwBOjXBFA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230526203410-71b5a4ffd15e/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc h1:XSJ8Vk1SWuNr8S18z1NZSziL0CPIXLCCMDOEFtHBOFc= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= @@ -2363,15 +2224,12 @@ google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g= -google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8= google.golang.org/grpc v1.56.3 h1:8I4C0Yq1EjstUzUJzpcRVbuYA2mODtEmpWiQoN/b2nc= google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.0.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -2389,32 +2247,22 @@ google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= -google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= -gopkg.in/VividCortex/ewma.v1 v1.1.1/go.mod h1:TekXuFipeiHWiAlO1+wSS23vTcyFau5u3rxXUSXj710= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/cheggaaa/pb.v2 v2.0.7/go.mod h1:0CiZ1p8pvtxBlQpLXkHuUTpdJ1shm3OqCF1QugkjHL4= gopkg.in/d4l3k/messagediff.v1 v1.2.1/go.mod h1:EUzikiKadqXWcD1AzJLagx0j/BeeWGtn++04Xniyg44= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/fatih/color.v1 v1.7.0/go.mod h1:P7yosIhqIl/sX8J8UypY5M+dDpD2KmyfP5IRs5v/fo0= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/mattn/go-colorable.v0 v0.1.0/go.mod h1:BVJlBXzARQxdi3nZo6f6bnl5yR20/tOL6p+V0KejgSY= -gopkg.in/mattn/go-isatty.v0 v0.0.4/go.mod h1:wt691ab7g0X4ilKZNmMII3egK0bTxl37fEn/Fwbd8gc= -gopkg.in/mattn/go-runewidth.v0 v0.0.4/go.mod h1:BmXejnxvhwdaATwiJbB1vZ2dtXkQKZGu9yLFCZb4msQ= gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c= @@ -2427,11 +2275,9 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -2446,17 +2292,14 @@ k8s.io/apimachinery v0.20.0/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRp k8s.io/client-go v0.20.0/go.mod h1:4KWh/g+Ocd8KkCwKF8vUNnmqgv+EVnQDK4MBF4oB5tY= k8s.io/klog/v2 v2.80.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -lukechampine.com/blake3 v1.2.1 h1:YuqqRuaqsGV71BV/nm9xlI0MKUv4QC54jQnBChWbGnI= lukechampine.com/blake3 v1.2.1/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE= lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= -moul.io/http2curl/v2 v2.3.0/go.mod h1:RW4hyBjTWSYDOxapodpNEtX0g5Eb16sxklBqmd2RHcE= mvdan.cc/xurls/v2 v2.2.0/go.mod h1:EV1RMtya9D6G5DMYPGD8zTQzaHet6Jh8gFlRgGRJeO8= nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU= rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= diff --git a/rocketpool-cli/auction/bid-lot.go b/rocketpool-cli/auction/bid-lot.go index 3f41d362b..404574c22 100644 --- a/rocketpool-cli/auction/bid-lot.go +++ b/rocketpool-cli/auction/bid-lot.go @@ -5,7 +5,7 @@ import ( "math/big" "strconv" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services/gas" diff --git a/rocketpool-cli/auction/claim-lot.go b/rocketpool-cli/auction/claim-lot.go index 1454f5a58..e564fcecd 100644 --- a/rocketpool-cli/auction/claim-lot.go +++ b/rocketpool-cli/auction/claim-lot.go @@ -4,8 +4,8 @@ import ( "fmt" "strconv" - rocketpoolapi "github.com/rocket-pool/rocketpool-go/rocketpool" - "github.com/rocket-pool/rocketpool-go/utils/eth" + rocketpoolapi "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services/gas" diff --git a/rocketpool-cli/auction/lots.go b/rocketpool-cli/auction/lots.go index b48a67358..684d652c4 100644 --- a/rocketpool-cli/auction/lots.go +++ b/rocketpool-cli/auction/lots.go @@ -4,7 +4,7 @@ import ( "fmt" "math/big" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services/rocketpool" diff --git a/rocketpool-cli/auction/recover-lot.go b/rocketpool-cli/auction/recover-lot.go index 58d4263c6..330c15c4b 100644 --- a/rocketpool-cli/auction/recover-lot.go +++ b/rocketpool-cli/auction/recover-lot.go @@ -4,8 +4,8 @@ import ( "fmt" "strconv" - rocketpoolapi "github.com/rocket-pool/rocketpool-go/rocketpool" - "github.com/rocket-pool/rocketpool-go/utils/eth" + rocketpoolapi "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services/gas" diff --git a/rocketpool-cli/auction/status.go b/rocketpool-cli/auction/status.go index 01bd1ffd3..b6b4d22a3 100644 --- a/rocketpool-cli/auction/status.go +++ b/rocketpool-cli/auction/status.go @@ -3,7 +3,7 @@ package auction import ( "fmt" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services/rocketpool" diff --git a/rocketpool-cli/go.mod b/rocketpool-cli/go.mod index 623678161..1c63cafdb 100644 --- a/rocketpool-cli/go.mod +++ b/rocketpool-cli/go.mod @@ -33,7 +33,6 @@ require ( github.com/prysmaticlabs/go-bitfield v0.0.0-20210809151128-385d8c5e3fb7 github.com/prysmaticlabs/prysm/v5 v5.0.3 github.com/rivo/tview v0.0.0-20230208211350-7dfff1ce7854 - github.com/rocket-pool/rocketpool-go v1.8.4-0.20250516194146-403b19d31a82 github.com/sethvargo/go-password v0.2.0 github.com/shirou/gopsutil/v3 v3.23.1 github.com/tyler-smith/go-bip39 v1.1.0 diff --git a/rocketpool-cli/megapool/claim.go b/rocketpool-cli/megapool/claim.go index 7b0916655..834a0f1a0 100644 --- a/rocketpool-cli/megapool/claim.go +++ b/rocketpool-cli/megapool/claim.go @@ -4,7 +4,7 @@ import ( "fmt" "math/big" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/rocket-pool/smartnode/shared/services/gas" "github.com/rocket-pool/smartnode/shared/services/rocketpool" cliutils "github.com/rocket-pool/smartnode/shared/utils/cli" diff --git a/rocketpool-cli/megapool/deposit.go b/rocketpool-cli/megapool/deposit.go index dd47994a1..0ff6cd3df 100644 --- a/rocketpool-cli/megapool/deposit.go +++ b/rocketpool-cli/megapool/deposit.go @@ -4,7 +4,7 @@ import ( "fmt" "math/big" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/urfave/cli" "golang.org/x/sync/errgroup" diff --git a/rocketpool-cli/megapool/reduce-bond.go b/rocketpool-cli/megapool/reduce-bond.go index f1242a6f7..e5c0f8362 100644 --- a/rocketpool-cli/megapool/reduce-bond.go +++ b/rocketpool-cli/megapool/reduce-bond.go @@ -4,7 +4,7 @@ import ( "fmt" "strconv" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/rocket-pool/smartnode/shared/services/gas" "github.com/rocket-pool/smartnode/shared/services/rocketpool" cliutils "github.com/rocket-pool/smartnode/shared/utils/cli" diff --git a/rocketpool-cli/megapool/repay-debt.go b/rocketpool-cli/megapool/repay-debt.go index 8c5e93c82..caafee49a 100644 --- a/rocketpool-cli/megapool/repay-debt.go +++ b/rocketpool-cli/megapool/repay-debt.go @@ -4,7 +4,7 @@ import ( "fmt" "strconv" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/rocket-pool/smartnode/shared/services/gas" "github.com/rocket-pool/smartnode/shared/services/rocketpool" cliutils "github.com/rocket-pool/smartnode/shared/utils/cli" diff --git a/rocketpool-cli/megapool/status.go b/rocketpool-cli/megapool/status.go index 5295aabbf..fad460951 100644 --- a/rocketpool-cli/megapool/status.go +++ b/rocketpool-cli/megapool/status.go @@ -5,7 +5,7 @@ import ( "math/big" "sort" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/rocket-pool/smartnode/shared/types/api" "github.com/rocket-pool/smartnode/shared/services/rocketpool" diff --git a/rocketpool-cli/minipool/close.go b/rocketpool-cli/minipool/close.go index c07b59903..eddfb4164 100644 --- a/rocketpool-cli/minipool/close.go +++ b/rocketpool-cli/minipool/close.go @@ -6,9 +6,9 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" - rocketpoolapi "github.com/rocket-pool/rocketpool-go/rocketpool" - "github.com/rocket-pool/rocketpool-go/types" - "github.com/rocket-pool/rocketpool-go/utils/eth" + rocketpoolapi "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/types" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services/beacon" diff --git a/rocketpool-cli/minipool/delegate.go b/rocketpool-cli/minipool/delegate.go index b93ee4dea..c2b36b678 100644 --- a/rocketpool-cli/minipool/delegate.go +++ b/rocketpool-cli/minipool/delegate.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/urfave/cli" - rocketpoolapi "github.com/rocket-pool/rocketpool-go/rocketpool" + rocketpoolapi "github.com/rocket-pool/smartnode/bindings/rocketpool" "github.com/rocket-pool/smartnode/shared/services/gas" "github.com/rocket-pool/smartnode/shared/services/rocketpool" "github.com/rocket-pool/smartnode/shared/types/api" diff --git a/rocketpool-cli/minipool/dissolve.go b/rocketpool-cli/minipool/dissolve.go index c1ee41fd5..ec2930156 100644 --- a/rocketpool-cli/minipool/dissolve.go +++ b/rocketpool-cli/minipool/dissolve.go @@ -5,9 +5,9 @@ import ( "fmt" "github.com/ethereum/go-ethereum/common" - rocketpoolapi "github.com/rocket-pool/rocketpool-go/rocketpool" - "github.com/rocket-pool/rocketpool-go/types" - "github.com/rocket-pool/rocketpool-go/utils/eth" + rocketpoolapi "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/types" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services/gas" diff --git a/rocketpool-cli/minipool/distribute.go b/rocketpool-cli/minipool/distribute.go index 883a7d36c..54af3f955 100644 --- a/rocketpool-cli/minipool/distribute.go +++ b/rocketpool-cli/minipool/distribute.go @@ -7,9 +7,9 @@ import ( "sort" "github.com/ethereum/go-ethereum/common" - rocketpoolapi "github.com/rocket-pool/rocketpool-go/rocketpool" - "github.com/rocket-pool/rocketpool-go/types" - "github.com/rocket-pool/rocketpool-go/utils/eth" + rocketpoolapi "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/types" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services/gas" diff --git a/rocketpool-cli/minipool/exit.go b/rocketpool-cli/minipool/exit.go index 83ed1f978..c4456ca88 100644 --- a/rocketpool-cli/minipool/exit.go +++ b/rocketpool-cli/minipool/exit.go @@ -5,7 +5,7 @@ import ( "fmt" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/types" + "github.com/rocket-pool/smartnode/bindings/types" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services/rocketpool" diff --git a/rocketpool-cli/minipool/promote.go b/rocketpool-cli/minipool/promote.go index 627acf187..a2c07c7be 100644 --- a/rocketpool-cli/minipool/promote.go +++ b/rocketpool-cli/minipool/promote.go @@ -5,7 +5,7 @@ import ( "fmt" "github.com/ethereum/go-ethereum/common" - rocketpoolapi "github.com/rocket-pool/rocketpool-go/rocketpool" + rocketpoolapi "github.com/rocket-pool/smartnode/bindings/rocketpool" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services/gas" diff --git a/rocketpool-cli/minipool/reduce-bond.go b/rocketpool-cli/minipool/reduce-bond.go index 49328f0f2..4f10af4a1 100644 --- a/rocketpool-cli/minipool/reduce-bond.go +++ b/rocketpool-cli/minipool/reduce-bond.go @@ -7,9 +7,9 @@ import ( "time" "github.com/ethereum/go-ethereum/common" - rocketpoolapi "github.com/rocket-pool/rocketpool-go/rocketpool" - "github.com/rocket-pool/rocketpool-go/types" - "github.com/rocket-pool/rocketpool-go/utils/eth" + rocketpoolapi "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/types" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/rocket-pool/smartnode/shared/services/gas" "github.com/rocket-pool/smartnode/shared/services/rocketpool" "github.com/rocket-pool/smartnode/shared/types/api" diff --git a/rocketpool-cli/minipool/refund.go b/rocketpool-cli/minipool/refund.go index 7b4bda2bd..eee049fb3 100644 --- a/rocketpool-cli/minipool/refund.go +++ b/rocketpool-cli/minipool/refund.go @@ -5,8 +5,8 @@ import ( "fmt" "github.com/ethereum/go-ethereum/common" - rocketpoolapi "github.com/rocket-pool/rocketpool-go/rocketpool" - "github.com/rocket-pool/rocketpool-go/utils/eth" + rocketpoolapi "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services/gas" diff --git a/rocketpool-cli/minipool/rescue-dissolved.go b/rocketpool-cli/minipool/rescue-dissolved.go index 184fe20e9..487a00772 100644 --- a/rocketpool-cli/minipool/rescue-dissolved.go +++ b/rocketpool-cli/minipool/rescue-dissolved.go @@ -7,8 +7,8 @@ import ( "strconv" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/types" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/types" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services/beacon" diff --git a/rocketpool-cli/minipool/stake.go b/rocketpool-cli/minipool/stake.go index 0e2cf1db7..fe486c497 100644 --- a/rocketpool-cli/minipool/stake.go +++ b/rocketpool-cli/minipool/stake.go @@ -5,7 +5,7 @@ import ( "fmt" "github.com/ethereum/go-ethereum/common" - rocketpoolapi "github.com/rocket-pool/rocketpool-go/rocketpool" + rocketpoolapi "github.com/rocket-pool/smartnode/bindings/rocketpool" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services/gas" diff --git a/rocketpool-cli/minipool/status.go b/rocketpool-cli/minipool/status.go index a49689c18..9218548f8 100644 --- a/rocketpool-cli/minipool/status.go +++ b/rocketpool-cli/minipool/status.go @@ -5,8 +5,8 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/types" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/types" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services/rocketpool" diff --git a/rocketpool-cli/minipool/vanity.go b/rocketpool-cli/minipool/vanity.go index 4396bb94c..5c79e6170 100644 --- a/rocketpool-cli/minipool/vanity.go +++ b/rocketpool-cli/minipool/vanity.go @@ -11,7 +11,7 @@ import ( "github.com/dustin/go-humanize" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services/rocketpool" diff --git a/rocketpool-cli/network/dao-proposals.go b/rocketpool-cli/network/dao-proposals.go index 42be5dc86..2fa0efae0 100644 --- a/rocketpool-cli/network/dao-proposals.go +++ b/rocketpool-cli/network/dao-proposals.go @@ -7,7 +7,7 @@ import ( "time" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/rocket-pool/smartnode/shared/services/rocketpool" cliutils "github.com/rocket-pool/smartnode/shared/utils/cli" "github.com/urfave/cli" diff --git a/rocketpool-cli/network/rpl-price.go b/rocketpool-cli/network/rpl-price.go index 86953f962..c0ee8a94a 100644 --- a/rocketpool-cli/network/rpl-price.go +++ b/rocketpool-cli/network/rpl-price.go @@ -3,7 +3,7 @@ package network import ( "fmt" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services/rocketpool" diff --git a/rocketpool-cli/node/burn.go b/rocketpool-cli/node/burn.go index 1ce87892d..dee803245 100644 --- a/rocketpool-cli/node/burn.go +++ b/rocketpool-cli/node/burn.go @@ -3,7 +3,7 @@ package node import ( "fmt" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services/gas" diff --git a/rocketpool-cli/node/claim-rewards.go b/rocketpool-cli/node/claim-rewards.go index ae99fa2b9..8fc8de9af 100644 --- a/rocketpool-cli/node/claim-rewards.go +++ b/rocketpool-cli/node/claim-rewards.go @@ -8,7 +8,7 @@ import ( "strings" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services/gas" diff --git a/rocketpool-cli/node/create-vacant-minipool.go b/rocketpool-cli/node/create-vacant-minipool.go index 9afc63437..755671d56 100644 --- a/rocketpool-cli/node/create-vacant-minipool.go +++ b/rocketpool-cli/node/create-vacant-minipool.go @@ -7,8 +7,8 @@ import ( "strconv" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/types" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/types" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/rocket-pool/smartnode/rocketpool-cli/wallet" "github.com/rocket-pool/smartnode/shared/services/gas" "github.com/rocket-pool/smartnode/shared/services/rocketpool" diff --git a/rocketpool-cli/node/deposit.go b/rocketpool-cli/node/deposit.go index 352c2041b..39b67fdf9 100644 --- a/rocketpool-cli/node/deposit.go +++ b/rocketpool-cli/node/deposit.go @@ -6,7 +6,7 @@ import ( "math/big" "strconv" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services/gas" diff --git a/rocketpool-cli/node/distributor.go b/rocketpool-cli/node/distributor.go index 3d7138610..57ba1d830 100644 --- a/rocketpool-cli/node/distributor.go +++ b/rocketpool-cli/node/distributor.go @@ -5,7 +5,7 @@ import ( "github.com/urfave/cli" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/rocket-pool/smartnode/shared/services/gas" "github.com/rocket-pool/smartnode/shared/services/rocketpool" cliutils "github.com/rocket-pool/smartnode/shared/utils/cli" diff --git a/rocketpool-cli/node/rpl-withdrawal-address.go b/rocketpool-cli/node/rpl-withdrawal-address.go index 8d9b798fe..4af1db5e9 100644 --- a/rocketpool-cli/node/rpl-withdrawal-address.go +++ b/rocketpool-cli/node/rpl-withdrawal-address.go @@ -8,7 +8,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/urfave/cli" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/rocket-pool/smartnode/shared/services/gas" "github.com/rocket-pool/smartnode/shared/services/rocketpool" cliutils "github.com/rocket-pool/smartnode/shared/utils/cli" diff --git a/rocketpool-cli/node/stake-rpl.go b/rocketpool-cli/node/stake-rpl.go index f39bd556a..1e849b9cc 100644 --- a/rocketpool-cli/node/stake-rpl.go +++ b/rocketpool-cli/node/stake-rpl.go @@ -6,7 +6,7 @@ import ( "strconv" "strings" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services/gas" diff --git a/rocketpool-cli/node/status.go b/rocketpool-cli/node/status.go index f7a0eba4b..a7ccce2ba 100644 --- a/rocketpool-cli/node/status.go +++ b/rocketpool-cli/node/status.go @@ -8,7 +8,7 @@ import ( "sort" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/addons/rescue_node" diff --git a/rocketpool-cli/node/swap-rpl.go b/rocketpool-cli/node/swap-rpl.go index c1df52bbb..0f395313c 100644 --- a/rocketpool-cli/node/swap-rpl.go +++ b/rocketpool-cli/node/swap-rpl.go @@ -5,7 +5,7 @@ import ( "math/big" "strconv" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services/gas" diff --git a/rocketpool-cli/node/utils.go b/rocketpool-cli/node/utils.go index 879134587..195799912 100644 --- a/rocketpool-cli/node/utils.go +++ b/rocketpool-cli/node/utils.go @@ -18,7 +18,7 @@ import ( "github.com/urfave/cli" "gopkg.in/yaml.v2" - "github.com/rocket-pool/rocketpool-go/types" + "github.com/rocket-pool/smartnode/bindings/types" "github.com/rocket-pool/smartnode/shared/services/config" "github.com/rocket-pool/smartnode/shared/services/rocketpool" "github.com/rocket-pool/smartnode/shared/types/api" diff --git a/rocketpool-cli/node/withdraw-credit.go b/rocketpool-cli/node/withdraw-credit.go index 9b09beb59..1b247128c 100644 --- a/rocketpool-cli/node/withdraw-credit.go +++ b/rocketpool-cli/node/withdraw-credit.go @@ -5,7 +5,7 @@ import ( "math/big" "strconv" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services/gas" diff --git a/rocketpool-cli/node/withdraw-eth.go b/rocketpool-cli/node/withdraw-eth.go index a1677ae05..0a927d226 100644 --- a/rocketpool-cli/node/withdraw-eth.go +++ b/rocketpool-cli/node/withdraw-eth.go @@ -5,7 +5,7 @@ import ( "math/big" "strconv" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services/gas" diff --git a/rocketpool-cli/node/withdraw-rpl.go b/rocketpool-cli/node/withdraw-rpl.go index 10dc45daa..aaa3ea5a7 100644 --- a/rocketpool-cli/node/withdraw-rpl.go +++ b/rocketpool-cli/node/withdraw-rpl.go @@ -5,7 +5,7 @@ import ( "math/big" "strconv" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services/gas" diff --git a/rocketpool-cli/odao/cancel-proposal.go b/rocketpool-cli/odao/cancel-proposal.go index a9c4e62f5..94e165ad9 100644 --- a/rocketpool-cli/odao/cancel-proposal.go +++ b/rocketpool-cli/odao/cancel-proposal.go @@ -5,8 +5,8 @@ import ( "fmt" "strconv" - "github.com/rocket-pool/rocketpool-go/dao" - "github.com/rocket-pool/rocketpool-go/types" + "github.com/rocket-pool/smartnode/bindings/dao" + "github.com/rocket-pool/smartnode/bindings/types" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services/gas" diff --git a/rocketpool-cli/odao/execute-proposal.go b/rocketpool-cli/odao/execute-proposal.go index e2a3bdbaf..3cfaff2c6 100644 --- a/rocketpool-cli/odao/execute-proposal.go +++ b/rocketpool-cli/odao/execute-proposal.go @@ -4,9 +4,9 @@ import ( "fmt" "strconv" - "github.com/rocket-pool/rocketpool-go/dao" - rocketpoolapi "github.com/rocket-pool/rocketpool-go/rocketpool" - "github.com/rocket-pool/rocketpool-go/types" + "github.com/rocket-pool/smartnode/bindings/dao" + rocketpoolapi "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/types" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services/gas" diff --git a/rocketpool-cli/odao/get-settings.go b/rocketpool-cli/odao/get-settings.go index cb37b42fa..0a90c1378 100644 --- a/rocketpool-cli/odao/get-settings.go +++ b/rocketpool-cli/odao/get-settings.go @@ -6,7 +6,7 @@ import ( "github.com/urfave/cli" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/rocket-pool/smartnode/shared/services/rocketpool" ) diff --git a/rocketpool-cli/odao/join.go b/rocketpool-cli/odao/join.go index 468e0d9d0..cd13547b5 100644 --- a/rocketpool-cli/odao/join.go +++ b/rocketpool-cli/odao/join.go @@ -4,7 +4,7 @@ import ( "fmt" "math/big" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services/gas" diff --git a/rocketpool-cli/odao/members.go b/rocketpool-cli/odao/members.go index 376954bb2..f0af1ef0a 100644 --- a/rocketpool-cli/odao/members.go +++ b/rocketpool-cli/odao/members.go @@ -3,7 +3,7 @@ package odao import ( "fmt" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services/rocketpool" diff --git a/rocketpool-cli/odao/penalise-megapool.go b/rocketpool-cli/odao/penalise-megapool.go index fce5345e3..38264cc3c 100644 --- a/rocketpool-cli/odao/penalise-megapool.go +++ b/rocketpool-cli/odao/penalise-megapool.go @@ -6,7 +6,7 @@ import ( "strconv" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/rocket-pool/smartnode/shared/services/gas" "github.com/rocket-pool/smartnode/shared/services/rocketpool" cliutils "github.com/rocket-pool/smartnode/shared/utils/cli" diff --git a/rocketpool-cli/odao/proposals.go b/rocketpool-cli/odao/proposals.go index 92304ea00..e4c65e008 100644 --- a/rocketpool-cli/odao/proposals.go +++ b/rocketpool-cli/odao/proposals.go @@ -7,8 +7,8 @@ import ( "slices" "strings" - "github.com/rocket-pool/rocketpool-go/dao" - "github.com/rocket-pool/rocketpool-go/types" + "github.com/rocket-pool/smartnode/bindings/dao" + "github.com/rocket-pool/smartnode/bindings/types" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services/rocketpool" diff --git a/rocketpool-cli/odao/propose-kick.go b/rocketpool-cli/odao/propose-kick.go index a75d2b532..66f172d87 100644 --- a/rocketpool-cli/odao/propose-kick.go +++ b/rocketpool-cli/odao/propose-kick.go @@ -7,8 +7,8 @@ import ( "strconv" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/dao/trustednode" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/dao/trustednode" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services/gas" diff --git a/rocketpool-cli/odao/propose-settings.go b/rocketpool-cli/odao/propose-settings.go index 9be3e386b..9a96d9210 100644 --- a/rocketpool-cli/odao/propose-settings.go +++ b/rocketpool-cli/odao/propose-settings.go @@ -4,7 +4,7 @@ import ( "fmt" "time" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services/gas" diff --git a/rocketpool-cli/odao/vote-proposal.go b/rocketpool-cli/odao/vote-proposal.go index 635779f9f..636b1df43 100644 --- a/rocketpool-cli/odao/vote-proposal.go +++ b/rocketpool-cli/odao/vote-proposal.go @@ -5,8 +5,8 @@ import ( "fmt" "strconv" - "github.com/rocket-pool/rocketpool-go/dao" - "github.com/rocket-pool/rocketpool-go/types" + "github.com/rocket-pool/smartnode/bindings/dao" + "github.com/rocket-pool/smartnode/bindings/types" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services/gas" diff --git a/rocketpool-cli/pdao/claim-bonds.go b/rocketpool-cli/pdao/claim-bonds.go index cb7165e8b..050922421 100644 --- a/rocketpool-cli/pdao/claim-bonds.go +++ b/rocketpool-cli/pdao/claim-bonds.go @@ -5,8 +5,8 @@ import ( "sort" "strconv" - rocketpoolapi "github.com/rocket-pool/rocketpool-go/rocketpool" - "github.com/rocket-pool/rocketpool-go/utils/eth" + rocketpoolapi "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services/gas" diff --git a/rocketpool-cli/pdao/commands.go b/rocketpool-cli/pdao/commands.go index 237c8fe0a..96ec2e73e 100644 --- a/rocketpool-cli/pdao/commands.go +++ b/rocketpool-cli/pdao/commands.go @@ -5,7 +5,7 @@ import ( "github.com/urfave/cli" - "github.com/rocket-pool/rocketpool-go/settings/protocol" + "github.com/rocket-pool/smartnode/bindings/settings/protocol" cliutils "github.com/rocket-pool/smartnode/shared/utils/cli" ) diff --git a/rocketpool-cli/pdao/execute-proposal.go b/rocketpool-cli/pdao/execute-proposal.go index bdb8bb59a..6fdc08fa8 100644 --- a/rocketpool-cli/pdao/execute-proposal.go +++ b/rocketpool-cli/pdao/execute-proposal.go @@ -4,9 +4,9 @@ import ( "fmt" "strconv" - rocketpoolapi "github.com/rocket-pool/rocketpool-go/rocketpool" - "github.com/rocket-pool/rocketpool-go/types" - "github.com/rocket-pool/rocketpool-go/utils/strings" + rocketpoolapi "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/types" + "github.com/rocket-pool/smartnode/bindings/utils/strings" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services/gas" diff --git a/rocketpool-cli/pdao/get-settings.go b/rocketpool-cli/pdao/get-settings.go index efc7dfc3f..4b959d53f 100644 --- a/rocketpool-cli/pdao/get-settings.go +++ b/rocketpool-cli/pdao/get-settings.go @@ -5,7 +5,7 @@ import ( "github.com/urfave/cli" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/rocket-pool/smartnode/shared/services/rocketpool" ) diff --git a/rocketpool-cli/pdao/percentages.go b/rocketpool-cli/pdao/percentages.go index b1476342e..2bf277f6c 100644 --- a/rocketpool-cli/pdao/percentages.go +++ b/rocketpool-cli/pdao/percentages.go @@ -3,7 +3,7 @@ package pdao import ( "fmt" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/rocket-pool/smartnode/shared/services/gas" "github.com/rocket-pool/smartnode/shared/services/rocketpool" cliutils "github.com/rocket-pool/smartnode/shared/utils/cli" diff --git a/rocketpool-cli/pdao/proposals.go b/rocketpool-cli/pdao/proposals.go index cbadc212e..a41bd6c68 100644 --- a/rocketpool-cli/pdao/proposals.go +++ b/rocketpool-cli/pdao/proposals.go @@ -8,9 +8,9 @@ import ( "strings" "time" - "github.com/rocket-pool/rocketpool-go/types" - "github.com/rocket-pool/rocketpool-go/utils/eth" - utilsStrings "github.com/rocket-pool/rocketpool-go/utils/strings" + "github.com/rocket-pool/smartnode/bindings/types" + "github.com/rocket-pool/smartnode/bindings/utils/eth" + utilsStrings "github.com/rocket-pool/smartnode/bindings/utils/strings" "github.com/urfave/cli" diff --git a/rocketpool-cli/pdao/propose-settings.go b/rocketpool-cli/pdao/propose-settings.go index 50e285191..3b0cfddbe 100644 --- a/rocketpool-cli/pdao/propose-settings.go +++ b/rocketpool-cli/pdao/propose-settings.go @@ -5,8 +5,8 @@ import ( "math/big" "time" - "github.com/rocket-pool/rocketpool-go/settings/protocol" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/settings/protocol" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services/gas" diff --git a/rocketpool-cli/pdao/status.go b/rocketpool-cli/pdao/status.go index 1b516b023..ba616bc3d 100644 --- a/rocketpool-cli/pdao/status.go +++ b/rocketpool-cli/pdao/status.go @@ -8,9 +8,9 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/urfave/cli" - "github.com/rocket-pool/rocketpool-go/types" - "github.com/rocket-pool/rocketpool-go/utils/eth" - "github.com/rocket-pool/rocketpool-go/utils/strings" + "github.com/rocket-pool/smartnode/bindings/types" + "github.com/rocket-pool/smartnode/bindings/utils/eth" + "github.com/rocket-pool/smartnode/bindings/utils/strings" "github.com/rocket-pool/smartnode/shared/services/rocketpool" "github.com/rocket-pool/smartnode/shared/types/api" cliutils "github.com/rocket-pool/smartnode/shared/utils/cli" diff --git a/rocketpool-cli/pdao/utils.go b/rocketpool-cli/pdao/utils.go index b1fe1322c..db096231a 100644 --- a/rocketpool-cli/pdao/utils.go +++ b/rocketpool-cli/pdao/utils.go @@ -5,7 +5,7 @@ import ( "math/big" "strconv" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/utils/eth" cliutils "github.com/rocket-pool/smartnode/shared/utils/cli" "github.com/rocket-pool/smartnode/shared/utils/cli/prompt" "github.com/urfave/cli" diff --git a/rocketpool-cli/pdao/vote-proposal.go b/rocketpool-cli/pdao/vote-proposal.go index 1c7c5603f..2d6b17141 100644 --- a/rocketpool-cli/pdao/vote-proposal.go +++ b/rocketpool-cli/pdao/vote-proposal.go @@ -5,8 +5,8 @@ import ( "strconv" "time" - "github.com/rocket-pool/rocketpool-go/types" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/types" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services/gas" diff --git a/rocketpool-cli/queue/status.go b/rocketpool-cli/queue/status.go index 111375c63..0a80e5a8f 100644 --- a/rocketpool-cli/queue/status.go +++ b/rocketpool-cli/queue/status.go @@ -3,7 +3,7 @@ package queue import ( "fmt" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services/rocketpool" diff --git a/rocketpool-cli/security/cancel-proposal.go b/rocketpool-cli/security/cancel-proposal.go index eb6587afe..e386cb232 100644 --- a/rocketpool-cli/security/cancel-proposal.go +++ b/rocketpool-cli/security/cancel-proposal.go @@ -5,8 +5,8 @@ import ( "fmt" "strconv" - "github.com/rocket-pool/rocketpool-go/dao" - "github.com/rocket-pool/rocketpool-go/types" + "github.com/rocket-pool/smartnode/bindings/dao" + "github.com/rocket-pool/smartnode/bindings/types" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services/gas" diff --git a/rocketpool-cli/security/commands.go b/rocketpool-cli/security/commands.go index 257f6ec31..d5469ab60 100644 --- a/rocketpool-cli/security/commands.go +++ b/rocketpool-cli/security/commands.go @@ -5,7 +5,7 @@ import ( "github.com/urfave/cli" - "github.com/rocket-pool/rocketpool-go/settings/protocol" + "github.com/rocket-pool/smartnode/bindings/settings/protocol" cliutils "github.com/rocket-pool/smartnode/shared/utils/cli" ) diff --git a/rocketpool-cli/security/execute-proposal.go b/rocketpool-cli/security/execute-proposal.go index 1f0ac5b5b..7f93b13bf 100644 --- a/rocketpool-cli/security/execute-proposal.go +++ b/rocketpool-cli/security/execute-proposal.go @@ -4,9 +4,9 @@ import ( "fmt" "strconv" - "github.com/rocket-pool/rocketpool-go/dao" - rocketpoolapi "github.com/rocket-pool/rocketpool-go/rocketpool" - "github.com/rocket-pool/rocketpool-go/types" + "github.com/rocket-pool/smartnode/bindings/dao" + rocketpoolapi "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/types" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services/gas" diff --git a/rocketpool-cli/security/proposals.go b/rocketpool-cli/security/proposals.go index e2bad47dd..a084356ae 100644 --- a/rocketpool-cli/security/proposals.go +++ b/rocketpool-cli/security/proposals.go @@ -7,8 +7,8 @@ import ( "slices" "strings" - "github.com/rocket-pool/rocketpool-go/dao" - "github.com/rocket-pool/rocketpool-go/types" + "github.com/rocket-pool/smartnode/bindings/dao" + "github.com/rocket-pool/smartnode/bindings/types" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services/rocketpool" diff --git a/rocketpool-cli/security/propose-settings.go b/rocketpool-cli/security/propose-settings.go index 43fdf4c07..0b6d33dcd 100644 --- a/rocketpool-cli/security/propose-settings.go +++ b/rocketpool-cli/security/propose-settings.go @@ -4,7 +4,7 @@ import ( "fmt" "math/big" - "github.com/rocket-pool/rocketpool-go/settings/protocol" + "github.com/rocket-pool/smartnode/bindings/settings/protocol" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services/gas" diff --git a/rocketpool-cli/security/vote-proposal.go b/rocketpool-cli/security/vote-proposal.go index 162390ffc..4ddd3afc4 100644 --- a/rocketpool-cli/security/vote-proposal.go +++ b/rocketpool-cli/security/vote-proposal.go @@ -5,8 +5,8 @@ import ( "fmt" "strconv" - "github.com/rocket-pool/rocketpool-go/dao" - "github.com/rocket-pool/rocketpool-go/types" + "github.com/rocket-pool/smartnode/bindings/dao" + "github.com/rocket-pool/smartnode/bindings/types" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services/gas" diff --git a/rocketpool-cli/wallet/utils.go b/rocketpool-cli/wallet/utils.go index 83d5f5775..2d52cf9e1 100644 --- a/rocketpool-cli/wallet/utils.go +++ b/rocketpool-cli/wallet/utils.go @@ -11,7 +11,7 @@ import ( "github.com/mitchellh/go-homedir" "gopkg.in/yaml.v2" - "github.com/rocket-pool/rocketpool-go/types" + "github.com/rocket-pool/smartnode/bindings/types" "github.com/rocket-pool/smartnode/rocketpool-cli/wallet/bip39" "github.com/rocket-pool/smartnode/shared/services/config" "github.com/rocket-pool/smartnode/shared/services/passwords" diff --git a/rocketpool/api/api.go b/rocketpool/api/api.go index 63e08ba2e..789c1a5b1 100644 --- a/rocketpool/api/api.go +++ b/rocketpool/api/api.go @@ -10,7 +10,7 @@ import ( "github.com/rocket-pool/smartnode/rocketpool/api/security" "github.com/urfave/cli" - "github.com/rocket-pool/rocketpool-go/utils" + "github.com/rocket-pool/smartnode/bindings/utils" "github.com/rocket-pool/smartnode/rocketpool/api/auction" "github.com/rocket-pool/smartnode/rocketpool/api/minipool" "github.com/rocket-pool/smartnode/rocketpool/api/network" diff --git a/rocketpool/api/auction/bid-lot.go b/rocketpool/api/auction/bid-lot.go index eeda7d7d0..d6a152cc6 100644 --- a/rocketpool/api/auction/bid-lot.go +++ b/rocketpool/api/auction/bid-lot.go @@ -4,8 +4,8 @@ import ( "fmt" "math/big" - "github.com/rocket-pool/rocketpool-go/auction" - "github.com/rocket-pool/rocketpool-go/settings/protocol" + "github.com/rocket-pool/smartnode/bindings/auction" + "github.com/rocket-pool/smartnode/bindings/settings/protocol" "github.com/urfave/cli" "golang.org/x/sync/errgroup" diff --git a/rocketpool/api/auction/claim-lot.go b/rocketpool/api/auction/claim-lot.go index 9af9bdec0..ee68072d2 100644 --- a/rocketpool/api/auction/claim-lot.go +++ b/rocketpool/api/auction/claim-lot.go @@ -4,7 +4,7 @@ import ( "fmt" "math/big" - "github.com/rocket-pool/rocketpool-go/auction" + "github.com/rocket-pool/smartnode/bindings/auction" "github.com/urfave/cli" "golang.org/x/sync/errgroup" diff --git a/rocketpool/api/auction/create-lot.go b/rocketpool/api/auction/create-lot.go index f2c706e75..42d52e48d 100644 --- a/rocketpool/api/auction/create-lot.go +++ b/rocketpool/api/auction/create-lot.go @@ -3,8 +3,8 @@ package auction import ( "fmt" - "github.com/rocket-pool/rocketpool-go/auction" - "github.com/rocket-pool/rocketpool-go/settings/protocol" + "github.com/rocket-pool/smartnode/bindings/auction" + "github.com/rocket-pool/smartnode/bindings/settings/protocol" "github.com/urfave/cli" "golang.org/x/sync/errgroup" diff --git a/rocketpool/api/auction/recover-lot.go b/rocketpool/api/auction/recover-lot.go index 2d0f89e6a..c6abefd33 100644 --- a/rocketpool/api/auction/recover-lot.go +++ b/rocketpool/api/auction/recover-lot.go @@ -4,7 +4,7 @@ import ( "fmt" "math/big" - "github.com/rocket-pool/rocketpool-go/auction" + "github.com/rocket-pool/smartnode/bindings/auction" "github.com/urfave/cli" "golang.org/x/sync/errgroup" diff --git a/rocketpool/api/auction/status.go b/rocketpool/api/auction/status.go index 64142e85f..b316a561c 100644 --- a/rocketpool/api/auction/status.go +++ b/rocketpool/api/auction/status.go @@ -1,7 +1,7 @@ package auction import ( - "github.com/rocket-pool/rocketpool-go/auction" + "github.com/rocket-pool/smartnode/bindings/auction" "github.com/urfave/cli" "golang.org/x/sync/errgroup" diff --git a/rocketpool/api/auction/utils.go b/rocketpool/api/auction/utils.go index 2f0288ce7..2e984f311 100644 --- a/rocketpool/api/auction/utils.go +++ b/rocketpool/api/auction/utils.go @@ -5,11 +5,11 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/auction" - "github.com/rocket-pool/rocketpool-go/network" - "github.com/rocket-pool/rocketpool-go/rocketpool" - "github.com/rocket-pool/rocketpool-go/settings/protocol" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/auction" + "github.com/rocket-pool/smartnode/bindings/network" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/settings/protocol" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "golang.org/x/sync/errgroup" "github.com/rocket-pool/smartnode/shared/types/api" diff --git a/rocketpool/api/debug/validators.go b/rocketpool/api/debug/validators.go index 945b195c2..09ae296b4 100644 --- a/rocketpool/api/debug/validators.go +++ b/rocketpool/api/debug/validators.go @@ -8,10 +8,10 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/minipool" - "github.com/rocket-pool/rocketpool-go/rocketpool" - "github.com/rocket-pool/rocketpool-go/types" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/minipool" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/types" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/services/beacon" "github.com/rocket-pool/smartnode/shared/utils/eth2" diff --git a/rocketpool/api/megapool/claim-refunds.go b/rocketpool/api/megapool/claim-refunds.go index 6b0096b8c..d3e9eac4c 100644 --- a/rocketpool/api/megapool/claim-refunds.go +++ b/rocketpool/api/megapool/claim-refunds.go @@ -4,7 +4,7 @@ import ( "fmt" "math/big" - "github.com/rocket-pool/rocketpool-go/megapool" + "github.com/rocket-pool/smartnode/bindings/megapool" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services" diff --git a/rocketpool/api/megapool/delegate.go b/rocketpool/api/megapool/delegate.go index ec2c1fb7c..b81aa84be 100644 --- a/rocketpool/api/megapool/delegate.go +++ b/rocketpool/api/megapool/delegate.go @@ -4,7 +4,7 @@ import ( "fmt" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/megapool" + "github.com/rocket-pool/smartnode/bindings/megapool" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" "github.com/rocket-pool/smartnode/shared/utils/eth1" diff --git a/rocketpool/api/megapool/deploy-megapool.go b/rocketpool/api/megapool/deploy-megapool.go index 615e0c8a6..6380f2910 100644 --- a/rocketpool/api/megapool/deploy-megapool.go +++ b/rocketpool/api/megapool/deploy-megapool.go @@ -1,8 +1,8 @@ package megapool import ( - "github.com/rocket-pool/rocketpool-go/megapool" - "github.com/rocket-pool/rocketpool-go/node" + "github.com/rocket-pool/smartnode/bindings/megapool" + "github.com/rocket-pool/smartnode/bindings/node" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" "github.com/urfave/cli" diff --git a/rocketpool/api/megapool/dissolve-validator.go b/rocketpool/api/megapool/dissolve-validator.go index 20e38005a..c963ba5ca 100644 --- a/rocketpool/api/megapool/dissolve-validator.go +++ b/rocketpool/api/megapool/dissolve-validator.go @@ -3,7 +3,7 @@ package megapool import ( "fmt" - "github.com/rocket-pool/rocketpool-go/megapool" + "github.com/rocket-pool/smartnode/bindings/megapool" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" "github.com/rocket-pool/smartnode/shared/utils/eth1" diff --git a/rocketpool/api/megapool/distribute.go b/rocketpool/api/megapool/distribute.go index 6cbe7a2aa..d3e3f5085 100644 --- a/rocketpool/api/megapool/distribute.go +++ b/rocketpool/api/megapool/distribute.go @@ -3,7 +3,7 @@ package megapool import ( "fmt" - "github.com/rocket-pool/rocketpool-go/megapool" + "github.com/rocket-pool/smartnode/bindings/megapool" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" "github.com/rocket-pool/smartnode/shared/utils/eth1" diff --git a/rocketpool/api/megapool/exit-queue.go b/rocketpool/api/megapool/exit-queue.go index 1de3eb29c..a9ed23ce5 100644 --- a/rocketpool/api/megapool/exit-queue.go +++ b/rocketpool/api/megapool/exit-queue.go @@ -3,7 +3,7 @@ package megapool import ( "fmt" - "github.com/rocket-pool/rocketpool-go/megapool" + "github.com/rocket-pool/smartnode/bindings/megapool" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" "github.com/rocket-pool/smartnode/shared/utils/eth1" diff --git a/rocketpool/api/megapool/exit-validator.go b/rocketpool/api/megapool/exit-validator.go index 6ed70106d..94220b174 100644 --- a/rocketpool/api/megapool/exit-validator.go +++ b/rocketpool/api/megapool/exit-validator.go @@ -1,8 +1,8 @@ package megapool import ( - "github.com/rocket-pool/rocketpool-go/megapool" - "github.com/rocket-pool/rocketpool-go/types" + "github.com/rocket-pool/smartnode/bindings/megapool" + "github.com/rocket-pool/smartnode/bindings/types" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" "github.com/rocket-pool/smartnode/shared/utils/validator" diff --git a/rocketpool/api/megapool/notify-validator-exit.go b/rocketpool/api/megapool/notify-validator-exit.go index 085f141c6..45745f4fc 100644 --- a/rocketpool/api/megapool/notify-validator-exit.go +++ b/rocketpool/api/megapool/notify-validator-exit.go @@ -4,8 +4,8 @@ import ( "fmt" "math/big" - "github.com/rocket-pool/rocketpool-go/megapool" - "github.com/rocket-pool/rocketpool-go/types" + "github.com/rocket-pool/smartnode/bindings/megapool" + "github.com/rocket-pool/smartnode/bindings/types" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" "github.com/rocket-pool/smartnode/shared/utils/eth1" diff --git a/rocketpool/api/megapool/reduce-bond.go b/rocketpool/api/megapool/reduce-bond.go index 1aaf9bd1e..624cebcbe 100644 --- a/rocketpool/api/megapool/reduce-bond.go +++ b/rocketpool/api/megapool/reduce-bond.go @@ -4,7 +4,7 @@ import ( "fmt" "math/big" - "github.com/rocket-pool/rocketpool-go/megapool" + "github.com/rocket-pool/smartnode/bindings/megapool" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services" diff --git a/rocketpool/api/megapool/repay-debt.go b/rocketpool/api/megapool/repay-debt.go index f1890b50a..b18bfec6e 100644 --- a/rocketpool/api/megapool/repay-debt.go +++ b/rocketpool/api/megapool/repay-debt.go @@ -5,7 +5,7 @@ import ( "fmt" "math/big" - "github.com/rocket-pool/rocketpool-go/megapool" + "github.com/rocket-pool/smartnode/bindings/megapool" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services" diff --git a/rocketpool/api/megapool/stake.go b/rocketpool/api/megapool/stake.go index 44625dff9..51ac53672 100644 --- a/rocketpool/api/megapool/stake.go +++ b/rocketpool/api/megapool/stake.go @@ -4,8 +4,8 @@ import ( "fmt" "strings" - "github.com/rocket-pool/rocketpool-go/megapool" - "github.com/rocket-pool/rocketpool-go/types" + "github.com/rocket-pool/smartnode/bindings/megapool" + "github.com/rocket-pool/smartnode/bindings/types" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services" diff --git a/rocketpool/api/megapool/status.go b/rocketpool/api/megapool/status.go index a010bd5e4..47bd27c4d 100644 --- a/rocketpool/api/megapool/status.go +++ b/rocketpool/api/megapool/status.go @@ -4,7 +4,7 @@ import ( "fmt" "math/big" - "github.com/rocket-pool/rocketpool-go/megapool" + "github.com/rocket-pool/smartnode/bindings/megapool" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" "github.com/urfave/cli" diff --git a/rocketpool/api/megapool/utils.go b/rocketpool/api/megapool/utils.go index 78ec54d8a..58dae1fe5 100644 --- a/rocketpool/api/megapool/utils.go +++ b/rocketpool/api/megapool/utils.go @@ -8,13 +8,13 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" - "github.com/rocket-pool/rocketpool-go/megapool" - "github.com/rocket-pool/rocketpool-go/network" - "github.com/rocket-pool/rocketpool-go/node" - "github.com/rocket-pool/rocketpool-go/rocketpool" - "github.com/rocket-pool/rocketpool-go/settings/protocol" - "github.com/rocket-pool/rocketpool-go/storage" - "github.com/rocket-pool/rocketpool-go/types" + "github.com/rocket-pool/smartnode/bindings/megapool" + "github.com/rocket-pool/smartnode/bindings/network" + "github.com/rocket-pool/smartnode/bindings/node" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/settings/protocol" + "github.com/rocket-pool/smartnode/bindings/storage" + "github.com/rocket-pool/smartnode/bindings/types" "github.com/rocket-pool/smartnode/shared/services/beacon" "github.com/rocket-pool/smartnode/shared/types/api" "golang.org/x/sync/errgroup" diff --git a/rocketpool/api/minipool/change-withdrawal-creds.go b/rocketpool/api/minipool/change-withdrawal-creds.go index 9d1f3cfd9..20de5618d 100644 --- a/rocketpool/api/minipool/change-withdrawal-creds.go +++ b/rocketpool/api/minipool/change-withdrawal-creds.go @@ -5,8 +5,8 @@ import ( "fmt" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/minipool" - "github.com/rocket-pool/rocketpool-go/types" + "github.com/rocket-pool/smartnode/bindings/minipool" + "github.com/rocket-pool/smartnode/bindings/types" "github.com/urfave/cli" eth2types "github.com/wealdtech/go-eth2-types/v2" util "github.com/wealdtech/go-eth2-util" diff --git a/rocketpool/api/minipool/close.go b/rocketpool/api/minipool/close.go index f33b7fb42..1e910c3c8 100644 --- a/rocketpool/api/minipool/close.go +++ b/rocketpool/api/minipool/close.go @@ -7,11 +7,11 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/minipool" - "github.com/rocket-pool/rocketpool-go/node" - "github.com/rocket-pool/rocketpool-go/rocketpool" - "github.com/rocket-pool/rocketpool-go/types" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/minipool" + "github.com/rocket-pool/smartnode/bindings/node" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/types" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/urfave/cli" "golang.org/x/sync/errgroup" diff --git a/rocketpool/api/minipool/delegate.go b/rocketpool/api/minipool/delegate.go index 8b1646d95..815427ff6 100644 --- a/rocketpool/api/minipool/delegate.go +++ b/rocketpool/api/minipool/delegate.go @@ -4,9 +4,9 @@ import ( "fmt" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/minipool" - "github.com/rocket-pool/rocketpool-go/rocketpool" - rptypes "github.com/rocket-pool/rocketpool-go/types" + "github.com/rocket-pool/smartnode/bindings/minipool" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + rptypes "github.com/rocket-pool/smartnode/bindings/types" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services" diff --git a/rocketpool/api/minipool/dissolve.go b/rocketpool/api/minipool/dissolve.go index 56f126c59..3703cb8a6 100644 --- a/rocketpool/api/minipool/dissolve.go +++ b/rocketpool/api/minipool/dissolve.go @@ -4,8 +4,8 @@ import ( "fmt" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/minipool" - "github.com/rocket-pool/rocketpool-go/types" + "github.com/rocket-pool/smartnode/bindings/minipool" + "github.com/rocket-pool/smartnode/bindings/types" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services" diff --git a/rocketpool/api/minipool/distribute.go b/rocketpool/api/minipool/distribute.go index 6615928da..ba4a91ae2 100644 --- a/rocketpool/api/minipool/distribute.go +++ b/rocketpool/api/minipool/distribute.go @@ -6,9 +6,9 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/minipool" - "github.com/rocket-pool/rocketpool-go/types" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/minipool" + "github.com/rocket-pool/smartnode/bindings/types" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/urfave/cli" "golang.org/x/sync/errgroup" diff --git a/rocketpool/api/minipool/exit.go b/rocketpool/api/minipool/exit.go index eccafa399..024e963a7 100644 --- a/rocketpool/api/minipool/exit.go +++ b/rocketpool/api/minipool/exit.go @@ -2,8 +2,8 @@ package minipool import ( "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/minipool" - "github.com/rocket-pool/rocketpool-go/types" + "github.com/rocket-pool/smartnode/bindings/minipool" + "github.com/rocket-pool/smartnode/bindings/types" "github.com/urfave/cli" eth2types "github.com/wealdtech/go-eth2-types/v2" diff --git a/rocketpool/api/minipool/import-key.go b/rocketpool/api/minipool/import-key.go index 8bd31b3d7..f59532413 100644 --- a/rocketpool/api/minipool/import-key.go +++ b/rocketpool/api/minipool/import-key.go @@ -5,8 +5,8 @@ import ( "fmt" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/minipool" - "github.com/rocket-pool/rocketpool-go/types" + "github.com/rocket-pool/smartnode/bindings/minipool" + "github.com/rocket-pool/smartnode/bindings/types" "github.com/urfave/cli" eth2types "github.com/wealdtech/go-eth2-types/v2" diff --git a/rocketpool/api/minipool/promote.go b/rocketpool/api/minipool/promote.go index 933c1942f..9cd1704a6 100644 --- a/rocketpool/api/minipool/promote.go +++ b/rocketpool/api/minipool/promote.go @@ -6,8 +6,8 @@ import ( "time" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/minipool" - "github.com/rocket-pool/rocketpool-go/settings/trustednode" + "github.com/rocket-pool/smartnode/bindings/minipool" + "github.com/rocket-pool/smartnode/bindings/settings/trustednode" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services" diff --git a/rocketpool/api/minipool/reduce-bond.go b/rocketpool/api/minipool/reduce-bond.go index 3926c2122..caab073b0 100644 --- a/rocketpool/api/minipool/reduce-bond.go +++ b/rocketpool/api/minipool/reduce-bond.go @@ -5,9 +5,9 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/minipool" - "github.com/rocket-pool/rocketpool-go/rocketpool" - "github.com/rocket-pool/rocketpool-go/settings/protocol" + "github.com/rocket-pool/smartnode/bindings/minipool" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/settings/protocol" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/services/beacon" "github.com/rocket-pool/smartnode/shared/types/api" diff --git a/rocketpool/api/minipool/refund.go b/rocketpool/api/minipool/refund.go index 8e2a82a65..ed7b2293a 100644 --- a/rocketpool/api/minipool/refund.go +++ b/rocketpool/api/minipool/refund.go @@ -5,7 +5,7 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/minipool" + "github.com/rocket-pool/smartnode/bindings/minipool" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services" diff --git a/rocketpool/api/minipool/rescue-dissolved.go b/rocketpool/api/minipool/rescue-dissolved.go index 5fb1c1812..8419f3d20 100644 --- a/rocketpool/api/minipool/rescue-dissolved.go +++ b/rocketpool/api/minipool/rescue-dissolved.go @@ -7,10 +7,10 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" - "github.com/rocket-pool/rocketpool-go/minipool" - "github.com/rocket-pool/rocketpool-go/rocketpool" - rptypes "github.com/rocket-pool/rocketpool-go/types" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/minipool" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + rptypes "github.com/rocket-pool/smartnode/bindings/types" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/urfave/cli" "golang.org/x/sync/errgroup" diff --git a/rocketpool/api/minipool/stake.go b/rocketpool/api/minipool/stake.go index 712f70db7..09f1fb5f9 100644 --- a/rocketpool/api/minipool/stake.go +++ b/rocketpool/api/minipool/stake.go @@ -6,11 +6,11 @@ import ( "time" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/minipool" - "github.com/rocket-pool/rocketpool-go/settings/trustednode" + "github.com/rocket-pool/smartnode/bindings/minipool" + "github.com/rocket-pool/smartnode/bindings/settings/trustednode" "github.com/urfave/cli" - rptypes "github.com/rocket-pool/rocketpool-go/types" + rptypes "github.com/rocket-pool/smartnode/bindings/types" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" "github.com/rocket-pool/smartnode/shared/utils/eth1" diff --git a/rocketpool/api/minipool/utils.go b/rocketpool/api/minipool/utils.go index d526b99d6..532f1f9fa 100644 --- a/rocketpool/api/minipool/utils.go +++ b/rocketpool/api/minipool/utils.go @@ -8,13 +8,13 @@ import ( "time" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/minipool" - "github.com/rocket-pool/rocketpool-go/rocketpool" - "github.com/rocket-pool/rocketpool-go/settings/protocol" - "github.com/rocket-pool/rocketpool-go/settings/trustednode" - "github.com/rocket-pool/rocketpool-go/tokens" - "github.com/rocket-pool/rocketpool-go/types" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/minipool" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/settings/protocol" + "github.com/rocket-pool/smartnode/bindings/settings/trustednode" + "github.com/rocket-pool/smartnode/bindings/tokens" + "github.com/rocket-pool/smartnode/bindings/types" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "golang.org/x/sync/errgroup" "github.com/rocket-pool/smartnode/shared/services/beacon" diff --git a/rocketpool/api/network/dao-proposals.go b/rocketpool/api/network/dao-proposals.go index 9025e4de8..97ae79577 100644 --- a/rocketpool/api/network/dao-proposals.go +++ b/rocketpool/api/network/dao-proposals.go @@ -7,8 +7,8 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/network" - "github.com/rocket-pool/rocketpool-go/node" + "github.com/rocket-pool/smartnode/bindings/network" + "github.com/rocket-pool/smartnode/bindings/node" "github.com/rocket-pool/smartnode/rocketpool/api/pdao" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/services/proposals" diff --git a/rocketpool/api/network/node-fee.go b/rocketpool/api/network/node-fee.go index ba18260a9..8d88b139f 100644 --- a/rocketpool/api/network/node-fee.go +++ b/rocketpool/api/network/node-fee.go @@ -1,8 +1,8 @@ package network import ( - "github.com/rocket-pool/rocketpool-go/network" - "github.com/rocket-pool/rocketpool-go/settings/protocol" + "github.com/rocket-pool/smartnode/bindings/network" + "github.com/rocket-pool/smartnode/bindings/settings/protocol" "github.com/urfave/cli" "golang.org/x/sync/errgroup" diff --git a/rocketpool/api/network/rpl-price.go b/rocketpool/api/network/rpl-price.go index bffd2a751..5e8aede99 100644 --- a/rocketpool/api/network/rpl-price.go +++ b/rocketpool/api/network/rpl-price.go @@ -3,7 +3,7 @@ package network import ( "math/big" - "github.com/rocket-pool/rocketpool-go/network" + "github.com/rocket-pool/smartnode/bindings/network" "github.com/urfave/cli" "golang.org/x/sync/errgroup" diff --git a/rocketpool/api/network/stats.go b/rocketpool/api/network/stats.go index ef7b521c0..8f2f25e54 100644 --- a/rocketpool/api/network/stats.go +++ b/rocketpool/api/network/stats.go @@ -5,13 +5,13 @@ import ( "fmt" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/deposit" - "github.com/rocket-pool/rocketpool-go/minipool" - "github.com/rocket-pool/rocketpool-go/network" - "github.com/rocket-pool/rocketpool-go/node" - "github.com/rocket-pool/rocketpool-go/tokens" - "github.com/rocket-pool/rocketpool-go/utils/eth" - rpstate "github.com/rocket-pool/rocketpool-go/utils/state" + "github.com/rocket-pool/smartnode/bindings/deposit" + "github.com/rocket-pool/smartnode/bindings/minipool" + "github.com/rocket-pool/smartnode/bindings/network" + "github.com/rocket-pool/smartnode/bindings/node" + "github.com/rocket-pool/smartnode/bindings/tokens" + "github.com/rocket-pool/smartnode/bindings/utils/eth" + rpstate "github.com/rocket-pool/smartnode/bindings/utils/state" updateCheck "github.com/rocket-pool/smartnode/shared/services/state" "github.com/urfave/cli" diff --git a/rocketpool/api/network/timezones.go b/rocketpool/api/network/timezones.go index c55d0939a..a3b791338 100644 --- a/rocketpool/api/network/timezones.go +++ b/rocketpool/api/network/timezones.go @@ -4,7 +4,7 @@ import ( "math/big" "time" - "github.com/rocket-pool/rocketpool-go/node" + "github.com/rocket-pool/smartnode/bindings/node" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services" diff --git a/rocketpool/api/node/burn.go b/rocketpool/api/node/burn.go index fc37de9a4..c4ae051bd 100644 --- a/rocketpool/api/node/burn.go +++ b/rocketpool/api/node/burn.go @@ -4,7 +4,7 @@ import ( "fmt" "math/big" - "github.com/rocket-pool/rocketpool-go/tokens" + "github.com/rocket-pool/smartnode/bindings/tokens" "github.com/urfave/cli" "golang.org/x/sync/errgroup" diff --git a/rocketpool/api/node/claim-rewards.go b/rocketpool/api/node/claim-rewards.go index 2ef2e1480..1baf28beb 100644 --- a/rocketpool/api/node/claim-rewards.go +++ b/rocketpool/api/node/claim-rewards.go @@ -10,12 +10,12 @@ import ( "github.com/urfave/cli" "golang.org/x/sync/errgroup" - "github.com/rocket-pool/rocketpool-go/network" - "github.com/rocket-pool/rocketpool-go/node" - "github.com/rocket-pool/rocketpool-go/rewards" - "github.com/rocket-pool/rocketpool-go/rocketpool" - "github.com/rocket-pool/rocketpool-go/settings/protocol" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/network" + "github.com/rocket-pool/smartnode/bindings/node" + "github.com/rocket-pool/smartnode/bindings/rewards" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/settings/protocol" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/services/config" rprewards "github.com/rocket-pool/smartnode/shared/services/rewards" diff --git a/rocketpool/api/node/claim-rpl.go b/rocketpool/api/node/claim-rpl.go index 27e83cb26..a09d7b2af 100644 --- a/rocketpool/api/node/claim-rpl.go +++ b/rocketpool/api/node/claim-rpl.go @@ -6,7 +6,7 @@ import ( "github.com/urfave/cli" - "github.com/rocket-pool/rocketpool-go/legacy/v1.0.0/rewards" + "github.com/rocket-pool/smartnode/bindings/legacy/v1.0.0/rewards" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" "github.com/rocket-pool/smartnode/shared/utils/eth1" diff --git a/rocketpool/api/node/create-vacant-minipool.go b/rocketpool/api/node/create-vacant-minipool.go index f0c13cf24..e5e4f238f 100644 --- a/rocketpool/api/node/create-vacant-minipool.go +++ b/rocketpool/api/node/create-vacant-minipool.go @@ -6,12 +6,12 @@ import ( "math/big" "time" - "github.com/rocket-pool/rocketpool-go/minipool" - "github.com/rocket-pool/rocketpool-go/node" - "github.com/rocket-pool/rocketpool-go/settings/protocol" - "github.com/rocket-pool/rocketpool-go/settings/trustednode" - rptypes "github.com/rocket-pool/rocketpool-go/types" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/minipool" + "github.com/rocket-pool/smartnode/bindings/node" + "github.com/rocket-pool/smartnode/bindings/settings/protocol" + "github.com/rocket-pool/smartnode/bindings/settings/trustednode" + rptypes "github.com/rocket-pool/smartnode/bindings/types" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/services/beacon" "github.com/rocket-pool/smartnode/shared/types/api" diff --git a/rocketpool/api/node/deposit.go b/rocketpool/api/node/deposit.go index 5f5234381..85bd3db31 100644 --- a/rocketpool/api/node/deposit.go +++ b/rocketpool/api/node/deposit.go @@ -10,20 +10,20 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/signing" - "github.com/rocket-pool/rocketpool-go/deposit" - "github.com/rocket-pool/rocketpool-go/megapool" - "github.com/rocket-pool/rocketpool-go/minipool" - "github.com/rocket-pool/rocketpool-go/node" - "github.com/rocket-pool/rocketpool-go/settings/protocol" - "github.com/rocket-pool/rocketpool-go/settings/trustednode" - rptypes "github.com/rocket-pool/rocketpool-go/types" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/deposit" + "github.com/rocket-pool/smartnode/bindings/minipool" + "github.com/rocket-pool/smartnode/bindings/node" + "github.com/rocket-pool/smartnode/bindings/settings/protocol" + "github.com/rocket-pool/smartnode/bindings/settings/trustednode" + rptypes "github.com/rocket-pool/smartnode/bindings/types" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/urfave/cli" "golang.org/x/sync/errgroup" prdeposit "github.com/prysmaticlabs/prysm/v5/contracts/deposit" ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1" - nodev131 "github.com/rocket-pool/rocketpool-go/legacy/v1.3.1/node" + nodev131 "github.com/rocket-pool/smartnode/bindings/legacy/v1.3.1/node" + "github.com/rocket-pool/smartnode/bindings/megapool" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/services/beacon" "github.com/rocket-pool/smartnode/shared/services/state" diff --git a/rocketpool/api/node/distributor.go b/rocketpool/api/node/distributor.go index 250a28bf3..c9de72c33 100644 --- a/rocketpool/api/node/distributor.go +++ b/rocketpool/api/node/distributor.go @@ -4,8 +4,8 @@ import ( "context" "fmt" - "github.com/rocket-pool/rocketpool-go/node" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/node" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/urfave/cli" "golang.org/x/sync/errgroup" diff --git a/rocketpool/api/node/express-ticket.go b/rocketpool/api/node/express-ticket.go index 77c3adc89..0e1d08ae4 100644 --- a/rocketpool/api/node/express-ticket.go +++ b/rocketpool/api/node/express-ticket.go @@ -1,7 +1,7 @@ package node import ( - "github.com/rocket-pool/rocketpool-go/node" + "github.com/rocket-pool/smartnode/bindings/node" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" "github.com/urfave/cli" diff --git a/rocketpool/api/node/primary-withdrawal-address.go b/rocketpool/api/node/primary-withdrawal-address.go index 2acdb12fd..dadf46428 100644 --- a/rocketpool/api/node/primary-withdrawal-address.go +++ b/rocketpool/api/node/primary-withdrawal-address.go @@ -4,7 +4,7 @@ import ( "fmt" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/storage" + "github.com/rocket-pool/smartnode/bindings/storage" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services" diff --git a/rocketpool/api/node/register.go b/rocketpool/api/node/register.go index f6673ea87..ee972afe3 100644 --- a/rocketpool/api/node/register.go +++ b/rocketpool/api/node/register.go @@ -3,8 +3,8 @@ package node import ( "fmt" - "github.com/rocket-pool/rocketpool-go/node" - "github.com/rocket-pool/rocketpool-go/settings/protocol" + "github.com/rocket-pool/smartnode/bindings/node" + "github.com/rocket-pool/smartnode/bindings/settings/protocol" "github.com/urfave/cli" "golang.org/x/sync/errgroup" diff --git a/rocketpool/api/node/rewards.go b/rocketpool/api/node/rewards.go index 4f7b5c155..6f2630a10 100644 --- a/rocketpool/api/node/rewards.go +++ b/rocketpool/api/node/rewards.go @@ -7,13 +7,13 @@ import ( "time" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/dao/trustednode" - "github.com/rocket-pool/rocketpool-go/minipool" - "github.com/rocket-pool/rocketpool-go/node" - "github.com/rocket-pool/rocketpool-go/rewards" - "github.com/rocket-pool/rocketpool-go/tokens" - "github.com/rocket-pool/rocketpool-go/utils/eth" - rpstate "github.com/rocket-pool/rocketpool-go/utils/state" + "github.com/rocket-pool/smartnode/bindings/dao/trustednode" + "github.com/rocket-pool/smartnode/bindings/minipool" + "github.com/rocket-pool/smartnode/bindings/node" + "github.com/rocket-pool/smartnode/bindings/rewards" + "github.com/rocket-pool/smartnode/bindings/tokens" + "github.com/rocket-pool/smartnode/bindings/utils/eth" + rpstate "github.com/rocket-pool/smartnode/bindings/utils/state" updateCheck "github.com/rocket-pool/smartnode/shared/services/state" "github.com/urfave/cli" diff --git a/rocketpool/api/node/rpl-withdrawal-address.go b/rocketpool/api/node/rpl-withdrawal-address.go index a22ce2197..3530ea828 100644 --- a/rocketpool/api/node/rpl-withdrawal-address.go +++ b/rocketpool/api/node/rpl-withdrawal-address.go @@ -5,8 +5,8 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/node" - "github.com/rocket-pool/rocketpool-go/storage" + "github.com/rocket-pool/smartnode/bindings/node" + "github.com/rocket-pool/smartnode/bindings/storage" "github.com/urfave/cli" "golang.org/x/sync/errgroup" diff --git a/rocketpool/api/node/send-message.go b/rocketpool/api/node/send-message.go index 29059feb6..4560bbe38 100644 --- a/rocketpool/api/node/send-message.go +++ b/rocketpool/api/node/send-message.go @@ -3,7 +3,7 @@ package node import ( "fmt" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/ethereum/go-ethereum/common" "github.com/rocket-pool/smartnode/shared/services" diff --git a/rocketpool/api/node/send.go b/rocketpool/api/node/send.go index b4f34cd85..ebc1e098e 100644 --- a/rocketpool/api/node/send.go +++ b/rocketpool/api/node/send.go @@ -8,8 +8,8 @@ import ( "strings" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/tokens" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/tokens" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services" diff --git a/rocketpool/api/node/set-rpl-lock-allowed.go b/rocketpool/api/node/set-rpl-lock-allowed.go index 6b6a974ef..3cb74f9bd 100644 --- a/rocketpool/api/node/set-rpl-lock-allowed.go +++ b/rocketpool/api/node/set-rpl-lock-allowed.go @@ -3,7 +3,7 @@ package node import ( "fmt" - "github.com/rocket-pool/rocketpool-go/node" + "github.com/rocket-pool/smartnode/bindings/node" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services" diff --git a/rocketpool/api/node/set-stake-rpl-for-allowed.go b/rocketpool/api/node/set-stake-rpl-for-allowed.go index 6c620102a..c3267c9f9 100644 --- a/rocketpool/api/node/set-stake-rpl-for-allowed.go +++ b/rocketpool/api/node/set-stake-rpl-for-allowed.go @@ -4,7 +4,7 @@ import ( "fmt" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/node" + "github.com/rocket-pool/smartnode/bindings/node" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services" diff --git a/rocketpool/api/node/set-timezone.go b/rocketpool/api/node/set-timezone.go index 1661b37b0..fa6297785 100644 --- a/rocketpool/api/node/set-timezone.go +++ b/rocketpool/api/node/set-timezone.go @@ -4,7 +4,7 @@ import ( "fmt" _ "time/tzdata" - "github.com/rocket-pool/rocketpool-go/node" + "github.com/rocket-pool/smartnode/bindings/node" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services" diff --git a/rocketpool/api/node/smoothing-pool.go b/rocketpool/api/node/smoothing-pool.go index 0f2f7b6a4..665fc510a 100644 --- a/rocketpool/api/node/smoothing-pool.go +++ b/rocketpool/api/node/smoothing-pool.go @@ -5,9 +5,9 @@ import ( "fmt" "time" - "github.com/rocket-pool/rocketpool-go/node" - "github.com/rocket-pool/rocketpool-go/rewards" - rocketpoolapi "github.com/rocket-pool/rocketpool-go/rocketpool" + "github.com/rocket-pool/smartnode/bindings/node" + "github.com/rocket-pool/smartnode/bindings/rewards" + rocketpoolapi "github.com/rocket-pool/smartnode/bindings/rocketpool" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/services/rocketpool" "github.com/rocket-pool/smartnode/shared/types/api" diff --git a/rocketpool/api/node/stake-rpl.go b/rocketpool/api/node/stake-rpl.go index 995f1e175..4f14f4641 100644 --- a/rocketpool/api/node/stake-rpl.go +++ b/rocketpool/api/node/stake-rpl.go @@ -5,10 +5,10 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/node" - "github.com/rocket-pool/rocketpool-go/settings/protocol" - "github.com/rocket-pool/rocketpool-go/tokens" - "github.com/rocket-pool/rocketpool-go/utils" + "github.com/rocket-pool/smartnode/bindings/node" + "github.com/rocket-pool/smartnode/bindings/settings/protocol" + "github.com/rocket-pool/smartnode/bindings/tokens" + "github.com/rocket-pool/smartnode/bindings/utils" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services" diff --git a/rocketpool/api/node/status.go b/rocketpool/api/node/status.go index 09b5b25a6..7b4d70198 100644 --- a/rocketpool/api/node/status.go +++ b/rocketpool/api/node/status.go @@ -9,17 +9,17 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/dao/trustednode" - "github.com/rocket-pool/rocketpool-go/megapool" - "github.com/rocket-pool/rocketpool-go/minipool" - "github.com/rocket-pool/rocketpool-go/network" - "github.com/rocket-pool/rocketpool-go/node" - "github.com/rocket-pool/rocketpool-go/rocketpool" - "github.com/rocket-pool/rocketpool-go/settings/protocol" - tnsettings "github.com/rocket-pool/rocketpool-go/settings/trustednode" - "github.com/rocket-pool/rocketpool-go/tokens" - "github.com/rocket-pool/rocketpool-go/types" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/dao/trustednode" + "github.com/rocket-pool/smartnode/bindings/megapool" + "github.com/rocket-pool/smartnode/bindings/minipool" + "github.com/rocket-pool/smartnode/bindings/network" + "github.com/rocket-pool/smartnode/bindings/node" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/settings/protocol" + tnsettings "github.com/rocket-pool/smartnode/bindings/settings/trustednode" + "github.com/rocket-pool/smartnode/bindings/tokens" + "github.com/rocket-pool/smartnode/bindings/types" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/urfave/cli" "golang.org/x/sync/errgroup" diff --git a/rocketpool/api/node/swap-rpl.go b/rocketpool/api/node/swap-rpl.go index eea12ff82..a66bd26aa 100644 --- a/rocketpool/api/node/swap-rpl.go +++ b/rocketpool/api/node/swap-rpl.go @@ -5,8 +5,8 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/tokens" - "github.com/rocket-pool/rocketpool-go/utils" + "github.com/rocket-pool/smartnode/bindings/tokens" + "github.com/rocket-pool/smartnode/bindings/utils" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services" diff --git a/rocketpool/api/node/utils.go b/rocketpool/api/node/utils.go index 173e48fdc..be63a04c3 100644 --- a/rocketpool/api/node/utils.go +++ b/rocketpool/api/node/utils.go @@ -5,9 +5,9 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/minipool" - "github.com/rocket-pool/rocketpool-go/rocketpool" - "github.com/rocket-pool/rocketpool-go/types" + "github.com/rocket-pool/smartnode/bindings/minipool" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/types" "golang.org/x/sync/errgroup" ) diff --git a/rocketpool/api/node/withdraw-credit.go b/rocketpool/api/node/withdraw-credit.go index 254adc08f..d867bdac3 100644 --- a/rocketpool/api/node/withdraw-credit.go +++ b/rocketpool/api/node/withdraw-credit.go @@ -4,7 +4,7 @@ import ( "fmt" "math/big" - "github.com/rocket-pool/rocketpool-go/node" + "github.com/rocket-pool/smartnode/bindings/node" "github.com/urfave/cli" "golang.org/x/sync/errgroup" diff --git a/rocketpool/api/node/withdraw-eth.go b/rocketpool/api/node/withdraw-eth.go index 32f796f68..60f852c48 100644 --- a/rocketpool/api/node/withdraw-eth.go +++ b/rocketpool/api/node/withdraw-eth.go @@ -4,7 +4,7 @@ import ( "fmt" "math/big" - "github.com/rocket-pool/rocketpool-go/node" + "github.com/rocket-pool/smartnode/bindings/node" "github.com/urfave/cli" "golang.org/x/sync/errgroup" diff --git a/rocketpool/api/node/withdraw-rpl.go b/rocketpool/api/node/withdraw-rpl.go index 6fc690a66..e3714521c 100644 --- a/rocketpool/api/node/withdraw-rpl.go +++ b/rocketpool/api/node/withdraw-rpl.go @@ -7,8 +7,8 @@ import ( "time" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/node" - "github.com/rocket-pool/rocketpool-go/settings/protocol" + "github.com/rocket-pool/smartnode/bindings/node" + "github.com/rocket-pool/smartnode/bindings/settings/protocol" "github.com/urfave/cli" "golang.org/x/sync/errgroup" diff --git a/rocketpool/api/odao/cancel-proposal.go b/rocketpool/api/odao/cancel-proposal.go index 42fbb32de..705f1ee86 100644 --- a/rocketpool/api/odao/cancel-proposal.go +++ b/rocketpool/api/odao/cancel-proposal.go @@ -4,9 +4,9 @@ import ( "bytes" "fmt" - "github.com/rocket-pool/rocketpool-go/dao" - "github.com/rocket-pool/rocketpool-go/dao/trustednode" - rptypes "github.com/rocket-pool/rocketpool-go/types" + "github.com/rocket-pool/smartnode/bindings/dao" + "github.com/rocket-pool/smartnode/bindings/dao/trustednode" + rptypes "github.com/rocket-pool/smartnode/bindings/types" "github.com/urfave/cli" "golang.org/x/sync/errgroup" diff --git a/rocketpool/api/odao/execute-proposal.go b/rocketpool/api/odao/execute-proposal.go index 45da936ec..2889859df 100644 --- a/rocketpool/api/odao/execute-proposal.go +++ b/rocketpool/api/odao/execute-proposal.go @@ -3,9 +3,9 @@ package odao import ( "fmt" - "github.com/rocket-pool/rocketpool-go/dao" - "github.com/rocket-pool/rocketpool-go/dao/trustednode" - rptypes "github.com/rocket-pool/rocketpool-go/types" + "github.com/rocket-pool/smartnode/bindings/dao" + "github.com/rocket-pool/smartnode/bindings/dao/trustednode" + rptypes "github.com/rocket-pool/smartnode/bindings/types" "github.com/urfave/cli" "golang.org/x/sync/errgroup" diff --git a/rocketpool/api/odao/get-settings.go b/rocketpool/api/odao/get-settings.go index 56feb2532..4c1da5940 100644 --- a/rocketpool/api/odao/get-settings.go +++ b/rocketpool/api/odao/get-settings.go @@ -3,7 +3,7 @@ package odao import ( "fmt" - "github.com/rocket-pool/rocketpool-go/settings/trustednode" + "github.com/rocket-pool/smartnode/bindings/settings/trustednode" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services" diff --git a/rocketpool/api/odao/join.go b/rocketpool/api/odao/join.go index d929c18cf..e41fa6ead 100644 --- a/rocketpool/api/odao/join.go +++ b/rocketpool/api/odao/join.go @@ -5,10 +5,10 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" - tndao "github.com/rocket-pool/rocketpool-go/dao/trustednode" - tnsettings "github.com/rocket-pool/rocketpool-go/settings/trustednode" - "github.com/rocket-pool/rocketpool-go/tokens" - "github.com/rocket-pool/rocketpool-go/utils" + tndao "github.com/rocket-pool/smartnode/bindings/dao/trustednode" + tnsettings "github.com/rocket-pool/smartnode/bindings/settings/trustednode" + "github.com/rocket-pool/smartnode/bindings/tokens" + "github.com/rocket-pool/smartnode/bindings/utils" "github.com/urfave/cli" "golang.org/x/sync/errgroup" diff --git a/rocketpool/api/odao/leave.go b/rocketpool/api/odao/leave.go index 5d4644b70..a8f647db7 100644 --- a/rocketpool/api/odao/leave.go +++ b/rocketpool/api/odao/leave.go @@ -4,7 +4,7 @@ import ( "fmt" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/dao/trustednode" + "github.com/rocket-pool/smartnode/bindings/dao/trustednode" "github.com/urfave/cli" "golang.org/x/sync/errgroup" diff --git a/rocketpool/api/odao/members.go b/rocketpool/api/odao/members.go index e48dac372..0766dcf88 100644 --- a/rocketpool/api/odao/members.go +++ b/rocketpool/api/odao/members.go @@ -1,7 +1,7 @@ package odao import ( - "github.com/rocket-pool/rocketpool-go/dao/trustednode" + "github.com/rocket-pool/smartnode/bindings/dao/trustednode" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services" diff --git a/rocketpool/api/odao/penalise-megapool.go b/rocketpool/api/odao/penalise-megapool.go index 81d0e1827..37ef4f9ae 100644 --- a/rocketpool/api/odao/penalise-megapool.go +++ b/rocketpool/api/odao/penalise-megapool.go @@ -5,7 +5,7 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/megapool" + "github.com/rocket-pool/smartnode/bindings/megapool" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services" diff --git a/rocketpool/api/odao/proposals.go b/rocketpool/api/odao/proposals.go index 15b6e273f..01e4a9fbf 100644 --- a/rocketpool/api/odao/proposals.go +++ b/rocketpool/api/odao/proposals.go @@ -1,7 +1,7 @@ package odao import ( - "github.com/rocket-pool/rocketpool-go/dao" + "github.com/rocket-pool/smartnode/bindings/dao" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services" diff --git a/rocketpool/api/odao/propose-invite.go b/rocketpool/api/odao/propose-invite.go index 1b193141a..9f950b990 100644 --- a/rocketpool/api/odao/propose-invite.go +++ b/rocketpool/api/odao/propose-invite.go @@ -4,7 +4,7 @@ import ( "fmt" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/dao/trustednode" + "github.com/rocket-pool/smartnode/bindings/dao/trustednode" "github.com/urfave/cli" "golang.org/x/sync/errgroup" diff --git a/rocketpool/api/odao/propose-kick.go b/rocketpool/api/odao/propose-kick.go index 49fbece96..91d6ce3bc 100644 --- a/rocketpool/api/odao/propose-kick.go +++ b/rocketpool/api/odao/propose-kick.go @@ -5,8 +5,8 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/dao/trustednode" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/dao/trustednode" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/urfave/cli" "golang.org/x/sync/errgroup" diff --git a/rocketpool/api/odao/propose-leave.go b/rocketpool/api/odao/propose-leave.go index 0b24e8325..097374673 100644 --- a/rocketpool/api/odao/propose-leave.go +++ b/rocketpool/api/odao/propose-leave.go @@ -3,7 +3,7 @@ package odao import ( "fmt" - "github.com/rocket-pool/rocketpool-go/dao/trustednode" + "github.com/rocket-pool/smartnode/bindings/dao/trustednode" "github.com/urfave/cli" "golang.org/x/sync/errgroup" diff --git a/rocketpool/api/odao/propose-settings.go b/rocketpool/api/odao/propose-settings.go index 76eaa3daa..5b46faaef 100644 --- a/rocketpool/api/odao/propose-settings.go +++ b/rocketpool/api/odao/propose-settings.go @@ -4,8 +4,8 @@ import ( "fmt" "math/big" - "github.com/rocket-pool/rocketpool-go/rocketpool" - "github.com/rocket-pool/rocketpool-go/settings/trustednode" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/settings/trustednode" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services" diff --git a/rocketpool/api/odao/status.go b/rocketpool/api/odao/status.go index c25bc7606..bd93bd6e7 100644 --- a/rocketpool/api/odao/status.go +++ b/rocketpool/api/odao/status.go @@ -1,8 +1,8 @@ package odao import ( - "github.com/rocket-pool/rocketpool-go/dao/trustednode" - rptypes "github.com/rocket-pool/rocketpool-go/types" + "github.com/rocket-pool/smartnode/bindings/dao/trustednode" + rptypes "github.com/rocket-pool/smartnode/bindings/types" "github.com/urfave/cli" "golang.org/x/sync/errgroup" diff --git a/rocketpool/api/odao/utils.go b/rocketpool/api/odao/utils.go index bd8e27026..27ae6817f 100644 --- a/rocketpool/api/odao/utils.go +++ b/rocketpool/api/odao/utils.go @@ -4,11 +4,11 @@ import ( "time" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/dao" - tndao "github.com/rocket-pool/rocketpool-go/dao/trustednode" - "github.com/rocket-pool/rocketpool-go/rocketpool" - tnsettings "github.com/rocket-pool/rocketpool-go/settings/trustednode" - rptypes "github.com/rocket-pool/rocketpool-go/types" + "github.com/rocket-pool/smartnode/bindings/dao" + tndao "github.com/rocket-pool/smartnode/bindings/dao/trustednode" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + tnsettings "github.com/rocket-pool/smartnode/bindings/settings/trustednode" + rptypes "github.com/rocket-pool/smartnode/bindings/types" "golang.org/x/sync/errgroup" ) diff --git a/rocketpool/api/odao/vote-proposal.go b/rocketpool/api/odao/vote-proposal.go index 4a278291e..e782cbad9 100644 --- a/rocketpool/api/odao/vote-proposal.go +++ b/rocketpool/api/odao/vote-proposal.go @@ -3,9 +3,9 @@ package odao import ( "fmt" - "github.com/rocket-pool/rocketpool-go/dao" - "github.com/rocket-pool/rocketpool-go/dao/trustednode" - rptypes "github.com/rocket-pool/rocketpool-go/types" + "github.com/rocket-pool/smartnode/bindings/dao" + "github.com/rocket-pool/smartnode/bindings/dao/trustednode" + rptypes "github.com/rocket-pool/smartnode/bindings/types" "github.com/urfave/cli" "golang.org/x/sync/errgroup" diff --git a/rocketpool/api/pdao/claim-bonds.go b/rocketpool/api/pdao/claim-bonds.go index 2bcda9755..6ac83607b 100644 --- a/rocketpool/api/pdao/claim-bonds.go +++ b/rocketpool/api/pdao/claim-bonds.go @@ -3,9 +3,9 @@ package pdao import ( "fmt" - "github.com/rocket-pool/rocketpool-go/dao/protocol" - "github.com/rocket-pool/rocketpool-go/rocketpool" - "github.com/rocket-pool/rocketpool-go/types" + "github.com/rocket-pool/smartnode/bindings/dao/protocol" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/types" "github.com/urfave/cli" "golang.org/x/sync/errgroup" diff --git a/rocketpool/api/pdao/claimable-bonds.go b/rocketpool/api/pdao/claimable-bonds.go index bd22416d6..5ba701470 100644 --- a/rocketpool/api/pdao/claimable-bonds.go +++ b/rocketpool/api/pdao/claimable-bonds.go @@ -7,9 +7,9 @@ import ( "time" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/dao/protocol" - "github.com/rocket-pool/rocketpool-go/types" - "github.com/rocket-pool/rocketpool-go/utils/state" + "github.com/rocket-pool/smartnode/bindings/dao/protocol" + "github.com/rocket-pool/smartnode/bindings/types" + "github.com/rocket-pool/smartnode/bindings/utils/state" updateCheck "github.com/rocket-pool/smartnode/shared/services/state" "github.com/rocket-pool/smartnode/shared/services" diff --git a/rocketpool/api/pdao/defeat-proposal.go b/rocketpool/api/pdao/defeat-proposal.go index 118a416cd..7f3c29604 100644 --- a/rocketpool/api/pdao/defeat-proposal.go +++ b/rocketpool/api/pdao/defeat-proposal.go @@ -4,8 +4,8 @@ import ( "fmt" "time" - "github.com/rocket-pool/rocketpool-go/dao/protocol" - "github.com/rocket-pool/rocketpool-go/types" + "github.com/rocket-pool/smartnode/bindings/dao/protocol" + "github.com/rocket-pool/smartnode/bindings/types" "github.com/urfave/cli" "golang.org/x/sync/errgroup" diff --git a/rocketpool/api/pdao/execute-proposal.go b/rocketpool/api/pdao/execute-proposal.go index f8a8b61fe..3cc5913c3 100644 --- a/rocketpool/api/pdao/execute-proposal.go +++ b/rocketpool/api/pdao/execute-proposal.go @@ -3,8 +3,8 @@ package pdao import ( "fmt" - "github.com/rocket-pool/rocketpool-go/dao/protocol" - rptypes "github.com/rocket-pool/rocketpool-go/types" + "github.com/rocket-pool/smartnode/bindings/dao/protocol" + rptypes "github.com/rocket-pool/smartnode/bindings/types" "github.com/urfave/cli" "golang.org/x/sync/errgroup" diff --git a/rocketpool/api/pdao/finalize-proposal.go b/rocketpool/api/pdao/finalize-proposal.go index 5d73bb275..8c8357869 100644 --- a/rocketpool/api/pdao/finalize-proposal.go +++ b/rocketpool/api/pdao/finalize-proposal.go @@ -3,8 +3,8 @@ package pdao import ( "fmt" - "github.com/rocket-pool/rocketpool-go/dao/protocol" - "github.com/rocket-pool/rocketpool-go/types" + "github.com/rocket-pool/smartnode/bindings/dao/protocol" + "github.com/rocket-pool/smartnode/bindings/types" "github.com/urfave/cli" "golang.org/x/sync/errgroup" diff --git a/rocketpool/api/pdao/get-settings.go b/rocketpool/api/pdao/get-settings.go index 97c78836e..b35b2ebaf 100644 --- a/rocketpool/api/pdao/get-settings.go +++ b/rocketpool/api/pdao/get-settings.go @@ -3,7 +3,7 @@ package pdao import ( "time" - "github.com/rocket-pool/rocketpool-go/settings/protocol" + "github.com/rocket-pool/smartnode/bindings/settings/protocol" "github.com/urfave/cli" "golang.org/x/sync/errgroup" diff --git a/rocketpool/api/pdao/initialize-voting-with-delegate.go b/rocketpool/api/pdao/initialize-voting-with-delegate.go index 1460fa5ce..e43350ec9 100644 --- a/rocketpool/api/pdao/initialize-voting-with-delegate.go +++ b/rocketpool/api/pdao/initialize-voting-with-delegate.go @@ -6,7 +6,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/urfave/cli" - "github.com/rocket-pool/rocketpool-go/network" + "github.com/rocket-pool/smartnode/bindings/network" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" "github.com/rocket-pool/smartnode/shared/utils/eth1" diff --git a/rocketpool/api/pdao/initialize-voting.go b/rocketpool/api/pdao/initialize-voting.go index 18c6f110c..a2789c66f 100644 --- a/rocketpool/api/pdao/initialize-voting.go +++ b/rocketpool/api/pdao/initialize-voting.go @@ -5,7 +5,7 @@ import ( "github.com/urfave/cli" - "github.com/rocket-pool/rocketpool-go/network" + "github.com/rocket-pool/smartnode/bindings/network" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" "github.com/rocket-pool/smartnode/shared/utils/eth1" diff --git a/rocketpool/api/pdao/invite-security.go b/rocketpool/api/pdao/invite-security.go index 6069e7177..ccaec32f5 100644 --- a/rocketpool/api/pdao/invite-security.go +++ b/rocketpool/api/pdao/invite-security.go @@ -4,9 +4,9 @@ import ( "fmt" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/dao/protocol" - "github.com/rocket-pool/rocketpool-go/dao/security" - "github.com/rocket-pool/rocketpool-go/node" + "github.com/rocket-pool/smartnode/bindings/dao/protocol" + "github.com/rocket-pool/smartnode/bindings/dao/security" + "github.com/rocket-pool/smartnode/bindings/node" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" "github.com/rocket-pool/smartnode/shared/utils/eth1" diff --git a/rocketpool/api/pdao/kick-multi-security.go b/rocketpool/api/pdao/kick-multi-security.go index 609f67754..04d263eee 100644 --- a/rocketpool/api/pdao/kick-multi-security.go +++ b/rocketpool/api/pdao/kick-multi-security.go @@ -4,7 +4,7 @@ import ( "fmt" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/dao/protocol" + "github.com/rocket-pool/smartnode/bindings/dao/protocol" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" "github.com/rocket-pool/smartnode/shared/utils/eth1" diff --git a/rocketpool/api/pdao/kick-security.go b/rocketpool/api/pdao/kick-security.go index 976165182..fda3abea3 100644 --- a/rocketpool/api/pdao/kick-security.go +++ b/rocketpool/api/pdao/kick-security.go @@ -4,8 +4,8 @@ import ( "fmt" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/dao/protocol" - "github.com/rocket-pool/rocketpool-go/node" + "github.com/rocket-pool/smartnode/bindings/dao/protocol" + "github.com/rocket-pool/smartnode/bindings/node" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" "github.com/rocket-pool/smartnode/shared/utils/eth1" diff --git a/rocketpool/api/pdao/one-time-spend.go b/rocketpool/api/pdao/one-time-spend.go index d1264253a..ebb5158d8 100644 --- a/rocketpool/api/pdao/one-time-spend.go +++ b/rocketpool/api/pdao/one-time-spend.go @@ -5,8 +5,8 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/dao/protocol" - "github.com/rocket-pool/rocketpool-go/node" + "github.com/rocket-pool/smartnode/bindings/dao/protocol" + "github.com/rocket-pool/smartnode/bindings/node" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" "github.com/rocket-pool/smartnode/shared/utils/eth1" diff --git a/rocketpool/api/pdao/override-vote.go b/rocketpool/api/pdao/override-vote.go index b71c58f2f..67546dadc 100644 --- a/rocketpool/api/pdao/override-vote.go +++ b/rocketpool/api/pdao/override-vote.go @@ -4,9 +4,9 @@ import ( "fmt" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/dao/protocol" - "github.com/rocket-pool/rocketpool-go/network" - "github.com/rocket-pool/rocketpool-go/types" + "github.com/rocket-pool/smartnode/bindings/dao/protocol" + "github.com/rocket-pool/smartnode/bindings/network" + "github.com/rocket-pool/smartnode/bindings/types" "github.com/urfave/cli" "golang.org/x/sync/errgroup" diff --git a/rocketpool/api/pdao/percentages.go b/rocketpool/api/pdao/percentages.go index 41cf2e990..6431e9971 100644 --- a/rocketpool/api/pdao/percentages.go +++ b/rocketpool/api/pdao/percentages.go @@ -4,10 +4,10 @@ import ( "fmt" "math/big" - "github.com/rocket-pool/rocketpool-go/dao/protocol" - rpnode "github.com/rocket-pool/rocketpool-go/node" - psettings "github.com/rocket-pool/rocketpool-go/settings/protocol" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/dao/protocol" + rpnode "github.com/rocket-pool/smartnode/bindings/node" + psettings "github.com/rocket-pool/smartnode/bindings/settings/protocol" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" "github.com/rocket-pool/smartnode/shared/utils/eth1" diff --git a/rocketpool/api/pdao/proposals.go b/rocketpool/api/pdao/proposals.go index bc8fa91e5..e33f85681 100644 --- a/rocketpool/api/pdao/proposals.go +++ b/rocketpool/api/pdao/proposals.go @@ -2,9 +2,9 @@ package pdao import ( "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/dao/protocol" - "github.com/rocket-pool/rocketpool-go/network" - "github.com/rocket-pool/rocketpool-go/rocketpool" + "github.com/rocket-pool/smartnode/bindings/dao/protocol" + "github.com/rocket-pool/smartnode/bindings/network" + "github.com/rocket-pool/smartnode/bindings/rocketpool" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" "github.com/urfave/cli" diff --git a/rocketpool/api/pdao/propose-settings.go b/rocketpool/api/pdao/propose-settings.go index 282b61b4e..11c507171 100644 --- a/rocketpool/api/pdao/propose-settings.go +++ b/rocketpool/api/pdao/propose-settings.go @@ -5,9 +5,9 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/node" - "github.com/rocket-pool/rocketpool-go/rocketpool" - "github.com/rocket-pool/rocketpool-go/settings/protocol" + "github.com/rocket-pool/smartnode/bindings/node" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/settings/protocol" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" cliutils "github.com/rocket-pool/smartnode/shared/utils/cli" diff --git a/rocketpool/api/pdao/recurring-spend.go b/rocketpool/api/pdao/recurring-spend.go index 91a927a76..c8aab810e 100644 --- a/rocketpool/api/pdao/recurring-spend.go +++ b/rocketpool/api/pdao/recurring-spend.go @@ -6,8 +6,8 @@ import ( "time" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/dao/protocol" - "github.com/rocket-pool/rocketpool-go/node" + "github.com/rocket-pool/smartnode/bindings/dao/protocol" + "github.com/rocket-pool/smartnode/bindings/node" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" "github.com/rocket-pool/smartnode/shared/utils/eth1" diff --git a/rocketpool/api/pdao/replace-security.go b/rocketpool/api/pdao/replace-security.go index 189dd9a3a..0493d307f 100644 --- a/rocketpool/api/pdao/replace-security.go +++ b/rocketpool/api/pdao/replace-security.go @@ -4,9 +4,9 @@ import ( "fmt" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/dao/protocol" - "github.com/rocket-pool/rocketpool-go/dao/security" - "github.com/rocket-pool/rocketpool-go/node" + "github.com/rocket-pool/smartnode/bindings/dao/protocol" + "github.com/rocket-pool/smartnode/bindings/dao/security" + "github.com/rocket-pool/smartnode/bindings/node" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" "github.com/rocket-pool/smartnode/shared/utils/eth1" diff --git a/rocketpool/api/pdao/set-snapshot-address.go b/rocketpool/api/pdao/set-snapshot-address.go index 3e73ae2af..4d3a1c8a4 100644 --- a/rocketpool/api/pdao/set-snapshot-address.go +++ b/rocketpool/api/pdao/set-snapshot-address.go @@ -7,8 +7,8 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/network" - "github.com/rocket-pool/rocketpool-go/rocketpool" + "github.com/rocket-pool/smartnode/bindings/network" + "github.com/rocket-pool/smartnode/bindings/rocketpool" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/services/contracts" "github.com/rocket-pool/smartnode/shared/types/api" diff --git a/rocketpool/api/pdao/status.go b/rocketpool/api/pdao/status.go index f17d1b4f4..c91c9c6e7 100644 --- a/rocketpool/api/pdao/status.go +++ b/rocketpool/api/pdao/status.go @@ -16,8 +16,8 @@ import ( "github.com/wealdtech/go-ens/v3" "golang.org/x/sync/errgroup" - "github.com/rocket-pool/rocketpool-go/network" - "github.com/rocket-pool/rocketpool-go/node" + "github.com/rocket-pool/smartnode/bindings/network" + "github.com/rocket-pool/smartnode/bindings/node" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/services/proposals" "github.com/rocket-pool/smartnode/shared/types/api" diff --git a/rocketpool/api/pdao/update-recurring-spend.go b/rocketpool/api/pdao/update-recurring-spend.go index 321336d1a..88614f1ee 100644 --- a/rocketpool/api/pdao/update-recurring-spend.go +++ b/rocketpool/api/pdao/update-recurring-spend.go @@ -6,8 +6,8 @@ import ( "time" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/dao/protocol" - "github.com/rocket-pool/rocketpool-go/node" + "github.com/rocket-pool/smartnode/bindings/dao/protocol" + "github.com/rocket-pool/smartnode/bindings/node" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" "github.com/rocket-pool/smartnode/shared/utils/eth1" diff --git a/rocketpool/api/pdao/utils.go b/rocketpool/api/pdao/utils.go index a4891d94e..93e8661ec 100644 --- a/rocketpool/api/pdao/utils.go +++ b/rocketpool/api/pdao/utils.go @@ -1,8 +1,8 @@ package pdao import ( - "github.com/rocket-pool/rocketpool-go/rocketpool" - "github.com/rocket-pool/rocketpool-go/types" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/types" "github.com/rocket-pool/smartnode/shared/services/beacon" "github.com/rocket-pool/smartnode/shared/services/config" "github.com/rocket-pool/smartnode/shared/services/proposals" diff --git a/rocketpool/api/pdao/vote-proposal.go b/rocketpool/api/pdao/vote-proposal.go index 876479d75..a2c5a92a9 100644 --- a/rocketpool/api/pdao/vote-proposal.go +++ b/rocketpool/api/pdao/vote-proposal.go @@ -4,8 +4,8 @@ import ( "fmt" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/dao/protocol" - "github.com/rocket-pool/rocketpool-go/types" + "github.com/rocket-pool/smartnode/bindings/dao/protocol" + "github.com/rocket-pool/smartnode/bindings/types" "github.com/urfave/cli" "golang.org/x/sync/errgroup" diff --git a/rocketpool/api/pdao/voting.go b/rocketpool/api/pdao/voting.go index d02bd36df..7366f5d24 100644 --- a/rocketpool/api/pdao/voting.go +++ b/rocketpool/api/pdao/voting.go @@ -6,7 +6,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/urfave/cli" - "github.com/rocket-pool/rocketpool-go/network" + "github.com/rocket-pool/smartnode/bindings/network" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" "github.com/rocket-pool/smartnode/shared/utils/eth1" diff --git a/rocketpool/api/queue/process.go b/rocketpool/api/queue/process.go index 474a99936..ad4ae6ea6 100644 --- a/rocketpool/api/queue/process.go +++ b/rocketpool/api/queue/process.go @@ -5,13 +5,13 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/deposit" - "github.com/rocket-pool/rocketpool-go/rocketpool" - "github.com/rocket-pool/rocketpool-go/settings/protocol" + "github.com/rocket-pool/smartnode/bindings/deposit" + "github.com/rocket-pool/smartnode/bindings/settings/protocol" "github.com/urfave/cli" "golang.org/x/sync/errgroup" - nodev131 "github.com/rocket-pool/rocketpool-go/legacy/v1.3.1/node" + nodev131 "github.com/rocket-pool/smartnode/bindings/legacy/v1.3.1/node" + "github.com/rocket-pool/smartnode/bindings/rocketpool" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/services/state" "github.com/rocket-pool/smartnode/shared/types/api" diff --git a/rocketpool/api/queue/queue-length.go b/rocketpool/api/queue/queue-length.go index af98dc26f..0fa3bd379 100644 --- a/rocketpool/api/queue/queue-length.go +++ b/rocketpool/api/queue/queue-length.go @@ -1,8 +1,8 @@ package queue import ( - "github.com/rocket-pool/rocketpool-go/deposit" - "github.com/rocket-pool/rocketpool-go/settings/protocol" + "github.com/rocket-pool/smartnode/bindings/deposit" + "github.com/rocket-pool/smartnode/bindings/settings/protocol" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" "github.com/urfave/cli" diff --git a/rocketpool/api/queue/status.go b/rocketpool/api/queue/status.go index b60bef223..eb768a9a7 100644 --- a/rocketpool/api/queue/status.go +++ b/rocketpool/api/queue/status.go @@ -1,8 +1,8 @@ package queue import ( - "github.com/rocket-pool/rocketpool-go/deposit" - "github.com/rocket-pool/rocketpool-go/minipool" + "github.com/rocket-pool/smartnode/bindings/deposit" + "github.com/rocket-pool/smartnode/bindings/minipool" "github.com/urfave/cli" "golang.org/x/sync/errgroup" diff --git a/rocketpool/api/security/cancel-proposal.go b/rocketpool/api/security/cancel-proposal.go index 307b36dc1..36809b902 100644 --- a/rocketpool/api/security/cancel-proposal.go +++ b/rocketpool/api/security/cancel-proposal.go @@ -4,9 +4,9 @@ import ( "bytes" "fmt" - "github.com/rocket-pool/rocketpool-go/dao" - "github.com/rocket-pool/rocketpool-go/dao/security" - rptypes "github.com/rocket-pool/rocketpool-go/types" + "github.com/rocket-pool/smartnode/bindings/dao" + "github.com/rocket-pool/smartnode/bindings/dao/security" + rptypes "github.com/rocket-pool/smartnode/bindings/types" "github.com/urfave/cli" "golang.org/x/sync/errgroup" diff --git a/rocketpool/api/security/execute-proposal.go b/rocketpool/api/security/execute-proposal.go index bd78cd365..092741743 100644 --- a/rocketpool/api/security/execute-proposal.go +++ b/rocketpool/api/security/execute-proposal.go @@ -3,9 +3,9 @@ package security import ( "fmt" - "github.com/rocket-pool/rocketpool-go/dao" - "github.com/rocket-pool/rocketpool-go/dao/security" - rptypes "github.com/rocket-pool/rocketpool-go/types" + "github.com/rocket-pool/smartnode/bindings/dao" + "github.com/rocket-pool/smartnode/bindings/dao/security" + rptypes "github.com/rocket-pool/smartnode/bindings/types" "github.com/urfave/cli" "golang.org/x/sync/errgroup" diff --git a/rocketpool/api/security/join.go b/rocketpool/api/security/join.go index 268fc175d..d94fc24c0 100644 --- a/rocketpool/api/security/join.go +++ b/rocketpool/api/security/join.go @@ -3,7 +3,7 @@ package security import ( "fmt" - "github.com/rocket-pool/rocketpool-go/dao/security" + "github.com/rocket-pool/smartnode/bindings/dao/security" "github.com/urfave/cli" "golang.org/x/sync/errgroup" diff --git a/rocketpool/api/security/leave.go b/rocketpool/api/security/leave.go index 7d491fc28..39321cf85 100644 --- a/rocketpool/api/security/leave.go +++ b/rocketpool/api/security/leave.go @@ -3,7 +3,7 @@ package security import ( "fmt" - "github.com/rocket-pool/rocketpool-go/dao/security" + "github.com/rocket-pool/smartnode/bindings/dao/security" "github.com/urfave/cli" "golang.org/x/sync/errgroup" diff --git a/rocketpool/api/security/members.go b/rocketpool/api/security/members.go index a50ddc578..13dc47273 100644 --- a/rocketpool/api/security/members.go +++ b/rocketpool/api/security/members.go @@ -3,7 +3,7 @@ package security import ( "github.com/urfave/cli" - "github.com/rocket-pool/rocketpool-go/dao/security" + "github.com/rocket-pool/smartnode/bindings/dao/security" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" ) diff --git a/rocketpool/api/security/proposals.go b/rocketpool/api/security/proposals.go index 8c5b5af5f..2a7bfc393 100644 --- a/rocketpool/api/security/proposals.go +++ b/rocketpool/api/security/proposals.go @@ -1,7 +1,7 @@ package security import ( - "github.com/rocket-pool/rocketpool-go/dao" + "github.com/rocket-pool/smartnode/bindings/dao" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services" diff --git a/rocketpool/api/security/propose-leave.go b/rocketpool/api/security/propose-leave.go index 4a814fe56..0aa19217c 100644 --- a/rocketpool/api/security/propose-leave.go +++ b/rocketpool/api/security/propose-leave.go @@ -3,7 +3,7 @@ package security import ( "fmt" - "github.com/rocket-pool/rocketpool-go/dao/security" + "github.com/rocket-pool/smartnode/bindings/dao/security" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services" diff --git a/rocketpool/api/security/propose-settings.go b/rocketpool/api/security/propose-settings.go index 4020a6574..a25628be1 100644 --- a/rocketpool/api/security/propose-settings.go +++ b/rocketpool/api/security/propose-settings.go @@ -4,9 +4,9 @@ import ( "fmt" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/rocketpool" - "github.com/rocket-pool/rocketpool-go/settings/protocol" - "github.com/rocket-pool/rocketpool-go/settings/security" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/settings/protocol" + "github.com/rocket-pool/smartnode/bindings/settings/security" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" cliutils "github.com/rocket-pool/smartnode/shared/utils/cli" diff --git a/rocketpool/api/security/status.go b/rocketpool/api/security/status.go index 4337f6c96..97b9c5023 100644 --- a/rocketpool/api/security/status.go +++ b/rocketpool/api/security/status.go @@ -1,8 +1,8 @@ package security import ( - "github.com/rocket-pool/rocketpool-go/dao/security" - rptypes "github.com/rocket-pool/rocketpool-go/types" + "github.com/rocket-pool/smartnode/bindings/dao/security" + rptypes "github.com/rocket-pool/smartnode/bindings/types" "github.com/urfave/cli" "golang.org/x/sync/errgroup" diff --git a/rocketpool/api/security/utils.go b/rocketpool/api/security/utils.go index 1460b9bbf..f65b6902a 100644 --- a/rocketpool/api/security/utils.go +++ b/rocketpool/api/security/utils.go @@ -4,11 +4,11 @@ import ( "time" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/dao" - "github.com/rocket-pool/rocketpool-go/dao/security" - "github.com/rocket-pool/rocketpool-go/rocketpool" - psettings "github.com/rocket-pool/rocketpool-go/settings/protocol" - rptypes "github.com/rocket-pool/rocketpool-go/types" + "github.com/rocket-pool/smartnode/bindings/dao" + "github.com/rocket-pool/smartnode/bindings/dao/security" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + psettings "github.com/rocket-pool/smartnode/bindings/settings/protocol" + rptypes "github.com/rocket-pool/smartnode/bindings/types" "golang.org/x/sync/errgroup" ) diff --git a/rocketpool/api/security/vote-proposal.go b/rocketpool/api/security/vote-proposal.go index 2fa7eda39..bddd3ea7f 100644 --- a/rocketpool/api/security/vote-proposal.go +++ b/rocketpool/api/security/vote-proposal.go @@ -3,9 +3,9 @@ package security import ( "fmt" - "github.com/rocket-pool/rocketpool-go/dao" - "github.com/rocket-pool/rocketpool-go/dao/security" - rptypes "github.com/rocket-pool/rocketpool-go/types" + "github.com/rocket-pool/smartnode/bindings/dao" + "github.com/rocket-pool/smartnode/bindings/dao/security" + rptypes "github.com/rocket-pool/smartnode/bindings/types" "github.com/urfave/cli" "golang.org/x/sync/errgroup" diff --git a/rocketpool/api/wallet/ens-name.go b/rocketpool/api/wallet/ens-name.go index 6d7d2dc43..34a3f8108 100644 --- a/rocketpool/api/wallet/ens-name.go +++ b/rocketpool/api/wallet/ens-name.go @@ -3,7 +3,7 @@ package wallet import ( "fmt" - "github.com/rocket-pool/rocketpool-go/rocketpool" + "github.com/rocket-pool/smartnode/bindings/rocketpool" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" "github.com/urfave/cli" diff --git a/rocketpool/api/wallet/recover.go b/rocketpool/api/wallet/recover.go index 6b0222b16..1e9842b47 100644 --- a/rocketpool/api/wallet/recover.go +++ b/rocketpool/api/wallet/recover.go @@ -5,7 +5,7 @@ import ( "fmt" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/rocketpool" + "github.com/rocket-pool/smartnode/bindings/rocketpool" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services" diff --git a/rocketpool/api/wallet/test.go b/rocketpool/api/wallet/test.go index 6199c0e1a..813dfd07e 100644 --- a/rocketpool/api/wallet/test.go +++ b/rocketpool/api/wallet/test.go @@ -4,7 +4,7 @@ import ( "fmt" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/rocketpool" + "github.com/rocket-pool/smartnode/bindings/rocketpool" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services" diff --git a/rocketpool/go.mod b/rocketpool/go.mod index f0222c038..36bf08709 100644 --- a/rocketpool/go.mod +++ b/rocketpool/go.mod @@ -33,7 +33,6 @@ require ( github.com/prysmaticlabs/go-bitfield v0.0.0-20210809151128-385d8c5e3fb7 github.com/prysmaticlabs/prysm/v5 v5.0.3 github.com/rivo/tview v0.0.0-20230208211350-7dfff1ce7854 - github.com/rocket-pool/rocketpool-go v1.8.4-0.20241122223132-c5f2be18f72b github.com/sethvargo/go-password v0.2.0 github.com/shirou/gopsutil/v3 v3.23.1 github.com/tyler-smith/go-bip39 v1.1.0 diff --git a/rocketpool/node/auto-init-voting-power.go b/rocketpool/node/auto-init-voting-power.go index 5022fe3f3..c51f1c2bb 100644 --- a/rocketpool/node/auto-init-voting-power.go +++ b/rocketpool/node/auto-init-voting-power.go @@ -5,9 +5,9 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/network" - "github.com/rocket-pool/rocketpool-go/rocketpool" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/network" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/services/beacon" "github.com/rocket-pool/smartnode/shared/services/config" diff --git a/rocketpool/node/collectors/beacon-collector.go b/rocketpool/node/collectors/beacon-collector.go index d7f908f15..6f9ced526 100644 --- a/rocketpool/node/collectors/beacon-collector.go +++ b/rocketpool/node/collectors/beacon-collector.go @@ -8,7 +8,7 @@ import ( "github.com/rocket-pool/smartnode/shared/services/beacon" "github.com/prometheus/client_golang/prometheus" - "github.com/rocket-pool/rocketpool-go/rocketpool" + "github.com/rocket-pool/smartnode/bindings/rocketpool" "golang.org/x/sync/errgroup" ) diff --git a/rocketpool/node/collectors/demand-collector.go b/rocketpool/node/collectors/demand-collector.go index 8c67c2f5e..6abe3d33e 100644 --- a/rocketpool/node/collectors/demand-collector.go +++ b/rocketpool/node/collectors/demand-collector.go @@ -4,8 +4,8 @@ import ( "fmt" "github.com/prometheus/client_golang/prometheus" - "github.com/rocket-pool/rocketpool-go/rocketpool" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/utils/eth" ) const namespace = "rocketpool" diff --git a/rocketpool/node/collectors/node-collector.go b/rocketpool/node/collectors/node-collector.go index a3dc922c2..c6a55cf47 100644 --- a/rocketpool/node/collectors/node-collector.go +++ b/rocketpool/node/collectors/node-collector.go @@ -11,8 +11,8 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/prometheus/client_golang/prometheus" - "github.com/rocket-pool/rocketpool-go/rocketpool" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/services/beacon" "github.com/rocket-pool/smartnode/shared/services/config" diff --git a/rocketpool/node/collectors/odao-collector.go b/rocketpool/node/collectors/odao-collector.go index 4c7959cc2..28c8d45b3 100644 --- a/rocketpool/node/collectors/odao-collector.go +++ b/rocketpool/node/collectors/odao-collector.go @@ -4,7 +4,7 @@ import ( "fmt" "github.com/prometheus/client_golang/prometheus" - "github.com/rocket-pool/rocketpool-go/rocketpool" + "github.com/rocket-pool/smartnode/bindings/rocketpool" ) // Represents the collector for the ODAO metrics diff --git a/rocketpool/node/collectors/performance-collector.go b/rocketpool/node/collectors/performance-collector.go index 2e7f34f7c..c64681e32 100644 --- a/rocketpool/node/collectors/performance-collector.go +++ b/rocketpool/node/collectors/performance-collector.go @@ -4,8 +4,8 @@ import ( "fmt" "github.com/prometheus/client_golang/prometheus" - "github.com/rocket-pool/rocketpool-go/rocketpool" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/utils/eth" ) // Represents the collector for the Performance metrics diff --git a/rocketpool/node/collectors/rpl-collector.go b/rocketpool/node/collectors/rpl-collector.go index 4df55e3ca..7cbe1955e 100644 --- a/rocketpool/node/collectors/rpl-collector.go +++ b/rocketpool/node/collectors/rpl-collector.go @@ -4,8 +4,8 @@ import ( "fmt" "github.com/prometheus/client_golang/prometheus" - "github.com/rocket-pool/rocketpool-go/rocketpool" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/rocket-pool/smartnode/shared/services/config" ) diff --git a/rocketpool/node/collectors/smoothing-pool-collector.go b/rocketpool/node/collectors/smoothing-pool-collector.go index de6ade39e..4493c89dd 100644 --- a/rocketpool/node/collectors/smoothing-pool-collector.go +++ b/rocketpool/node/collectors/smoothing-pool-collector.go @@ -4,8 +4,8 @@ import ( "fmt" "github.com/prometheus/client_golang/prometheus" - "github.com/rocket-pool/rocketpool-go/rocketpool" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/rocket-pool/smartnode/shared/services" ) diff --git a/rocketpool/node/collectors/snapshot-collector.go b/rocketpool/node/collectors/snapshot-collector.go index cb6fda5f4..a74036e62 100644 --- a/rocketpool/node/collectors/snapshot-collector.go +++ b/rocketpool/node/collectors/snapshot-collector.go @@ -8,9 +8,9 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/prometheus/client_golang/prometheus" - "github.com/rocket-pool/rocketpool-go/network" - "github.com/rocket-pool/rocketpool-go/rocketpool" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/network" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/rocket-pool/smartnode/rocketpool/api/pdao" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/services/config" diff --git a/rocketpool/node/collectors/supply-collector.go b/rocketpool/node/collectors/supply-collector.go index dfc683d35..0570fc162 100644 --- a/rocketpool/node/collectors/supply-collector.go +++ b/rocketpool/node/collectors/supply-collector.go @@ -4,9 +4,9 @@ import ( "fmt" "github.com/prometheus/client_golang/prometheus" - "github.com/rocket-pool/rocketpool-go/minipool" - "github.com/rocket-pool/rocketpool-go/node" - "github.com/rocket-pool/rocketpool-go/rocketpool" + "github.com/rocket-pool/smartnode/bindings/minipool" + "github.com/rocket-pool/smartnode/bindings/node" + "github.com/rocket-pool/smartnode/bindings/rocketpool" "golang.org/x/sync/errgroup" ) diff --git a/rocketpool/node/collectors/trusted-node-collector.go b/rocketpool/node/collectors/trusted-node-collector.go index 1e4ef5573..b9db2a0cd 100644 --- a/rocketpool/node/collectors/trusted-node-collector.go +++ b/rocketpool/node/collectors/trusted-node-collector.go @@ -10,12 +10,12 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/prometheus/client_golang/prometheus" - "github.com/rocket-pool/rocketpool-go/dao" - "github.com/rocket-pool/rocketpool-go/dao/trustednode" - "github.com/rocket-pool/rocketpool-go/rocketpool" - "github.com/rocket-pool/rocketpool-go/tokens" - "github.com/rocket-pool/rocketpool-go/types" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/dao" + "github.com/rocket-pool/smartnode/bindings/dao/trustednode" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/tokens" + "github.com/rocket-pool/smartnode/bindings/types" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/rocket-pool/smartnode/shared/services/beacon" "github.com/rocket-pool/smartnode/shared/services/config" "golang.org/x/sync/errgroup" diff --git a/rocketpool/node/defend-pdao-props.go b/rocketpool/node/defend-pdao-props.go index 05439eb35..a20b32220 100644 --- a/rocketpool/node/defend-pdao-props.go +++ b/rocketpool/node/defend-pdao-props.go @@ -7,10 +7,10 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/dao/protocol" - "github.com/rocket-pool/rocketpool-go/rocketpool" - "github.com/rocket-pool/rocketpool-go/types" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/dao/protocol" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/types" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/services/beacon" "github.com/rocket-pool/smartnode/shared/services/config" diff --git a/rocketpool/node/distribute-minipools.go b/rocketpool/node/distribute-minipools.go index f8e98fd51..755e3f586 100644 --- a/rocketpool/node/distribute-minipools.go +++ b/rocketpool/node/distribute-minipools.go @@ -7,11 +7,11 @@ import ( "github.com/docker/docker/client" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/minipool" - "github.com/rocket-pool/rocketpool-go/rocketpool" - rptypes "github.com/rocket-pool/rocketpool-go/types" - "github.com/rocket-pool/rocketpool-go/utils/eth" - rpstate "github.com/rocket-pool/rocketpool-go/utils/state" + "github.com/rocket-pool/smartnode/bindings/minipool" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + rptypes "github.com/rocket-pool/smartnode/bindings/types" + "github.com/rocket-pool/smartnode/bindings/utils/eth" + rpstate "github.com/rocket-pool/smartnode/bindings/utils/state" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services" diff --git a/rocketpool/node/download-reward-trees.go b/rocketpool/node/download-reward-trees.go index 79ae6d6a3..cbe7bcd2c 100644 --- a/rocketpool/node/download-reward-trees.go +++ b/rocketpool/node/download-reward-trees.go @@ -5,7 +5,7 @@ import ( "os" "github.com/docker/docker/client" - "github.com/rocket-pool/rocketpool-go/rocketpool" + "github.com/rocket-pool/smartnode/bindings/rocketpool" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services" diff --git a/rocketpool/node/manage-fee-recipient.go b/rocketpool/node/manage-fee-recipient.go index 5056ebf58..4309e3e76 100644 --- a/rocketpool/node/manage-fee-recipient.go +++ b/rocketpool/node/manage-fee-recipient.go @@ -5,7 +5,7 @@ import ( "github.com/docker/docker/client" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/rocketpool" + "github.com/rocket-pool/smartnode/bindings/rocketpool" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services" diff --git a/rocketpool/node/prestake-megapool-validator.go b/rocketpool/node/prestake-megapool-validator.go index d3f587dbc..bbb3a9ead 100644 --- a/rocketpool/node/prestake-megapool-validator.go +++ b/rocketpool/node/prestake-megapool-validator.go @@ -8,10 +8,10 @@ import ( "github.com/docker/docker/client" "github.com/ethereum/go-ethereum/accounts/abi/bind" - "github.com/rocket-pool/rocketpool-go/deposit" - "github.com/rocket-pool/rocketpool-go/megapool" - "github.com/rocket-pool/rocketpool-go/rocketpool" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/deposit" + "github.com/rocket-pool/smartnode/bindings/megapool" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services" diff --git a/rocketpool/node/promote-minipools.go b/rocketpool/node/promote-minipools.go index 0d1c0773f..659c17400 100644 --- a/rocketpool/node/promote-minipools.go +++ b/rocketpool/node/promote-minipools.go @@ -9,11 +9,11 @@ import ( "github.com/docker/docker/client" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/minipool" - "github.com/rocket-pool/rocketpool-go/rocketpool" - "github.com/rocket-pool/rocketpool-go/types" - "github.com/rocket-pool/rocketpool-go/utils/eth" - rpstate "github.com/rocket-pool/rocketpool-go/utils/state" + "github.com/rocket-pool/smartnode/bindings/minipool" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/types" + "github.com/rocket-pool/smartnode/bindings/utils/eth" + rpstate "github.com/rocket-pool/smartnode/bindings/utils/state" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services" diff --git a/rocketpool/node/reduce-bonds.go b/rocketpool/node/reduce-bonds.go index 20367c79b..7dbb000cd 100644 --- a/rocketpool/node/reduce-bonds.go +++ b/rocketpool/node/reduce-bonds.go @@ -9,15 +9,15 @@ import ( "github.com/docker/docker/client" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/minipool" - "github.com/rocket-pool/rocketpool-go/node" - "github.com/rocket-pool/rocketpool-go/rocketpool" - "github.com/rocket-pool/rocketpool-go/types" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/minipool" + "github.com/rocket-pool/smartnode/bindings/node" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/types" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/urfave/cli" "golang.org/x/sync/errgroup" - rpstate "github.com/rocket-pool/rocketpool-go/utils/state" + rpstate "github.com/rocket-pool/smartnode/bindings/utils/state" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/services/alerting" "github.com/rocket-pool/smartnode/shared/services/config" diff --git a/rocketpool/node/stake-megapool-validator.go b/rocketpool/node/stake-megapool-validator.go index 3f04133e8..180f60b04 100644 --- a/rocketpool/node/stake-megapool-validator.go +++ b/rocketpool/node/stake-megapool-validator.go @@ -5,10 +5,10 @@ import ( "github.com/docker/docker/client" "github.com/ethereum/go-ethereum/accounts/abi/bind" - "github.com/rocket-pool/rocketpool-go/megapool" - "github.com/rocket-pool/rocketpool-go/rocketpool" - "github.com/rocket-pool/rocketpool-go/types" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/megapool" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/types" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services" diff --git a/rocketpool/node/stake-prelaunch-minipools.go b/rocketpool/node/stake-prelaunch-minipools.go index 247d1267a..eacc84857 100644 --- a/rocketpool/node/stake-prelaunch-minipools.go +++ b/rocketpool/node/stake-prelaunch-minipools.go @@ -9,11 +9,11 @@ import ( "github.com/docker/docker/client" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/minipool" - "github.com/rocket-pool/rocketpool-go/rocketpool" - rptypes "github.com/rocket-pool/rocketpool-go/types" - "github.com/rocket-pool/rocketpool-go/utils/eth" - rpstate "github.com/rocket-pool/rocketpool-go/utils/state" + "github.com/rocket-pool/smartnode/bindings/minipool" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + rptypes "github.com/rocket-pool/smartnode/bindings/types" + "github.com/rocket-pool/smartnode/bindings/utils/eth" + rpstate "github.com/rocket-pool/smartnode/bindings/utils/state" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services" diff --git a/rocketpool/node/verify-pdao-props.go b/rocketpool/node/verify-pdao-props.go index 91638766b..579fa68e3 100644 --- a/rocketpool/node/verify-pdao-props.go +++ b/rocketpool/node/verify-pdao-props.go @@ -7,10 +7,10 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/dao/protocol" - "github.com/rocket-pool/rocketpool-go/rocketpool" - "github.com/rocket-pool/rocketpool-go/types" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/dao/protocol" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/types" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/services/beacon" "github.com/rocket-pool/smartnode/shared/services/config" diff --git a/rocketpool/watchtower/cancel-bond-reductions.go b/rocketpool/watchtower/cancel-bond-reductions.go index 6fce1af78..b8d4078e7 100644 --- a/rocketpool/watchtower/cancel-bond-reductions.go +++ b/rocketpool/watchtower/cancel-bond-reductions.go @@ -9,10 +9,10 @@ import ( "github.com/rocket-pool/smartnode/rocketpool/watchtower/collectors" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/minipool" - "github.com/rocket-pool/rocketpool-go/rocketpool" - "github.com/rocket-pool/rocketpool-go/utils/eth" - rpstate "github.com/rocket-pool/rocketpool-go/utils/state" + "github.com/rocket-pool/smartnode/bindings/minipool" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/utils/eth" + rpstate "github.com/rocket-pool/smartnode/bindings/utils/state" "github.com/rocket-pool/smartnode/rocketpool/watchtower/utils" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/services/beacon" diff --git a/rocketpool/watchtower/check-solo-migrations.go b/rocketpool/watchtower/check-solo-migrations.go index d82625d85..1b141110c 100644 --- a/rocketpool/watchtower/check-solo-migrations.go +++ b/rocketpool/watchtower/check-solo-migrations.go @@ -9,10 +9,10 @@ import ( "github.com/rocket-pool/smartnode/rocketpool/watchtower/collectors" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/minipool" - "github.com/rocket-pool/rocketpool-go/rocketpool" - "github.com/rocket-pool/rocketpool-go/types" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/minipool" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/types" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/rocket-pool/smartnode/rocketpool/watchtower/utils" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/services/beacon" diff --git a/rocketpool/watchtower/dissolve-timed-out-megapool-validators.go b/rocketpool/watchtower/dissolve-timed-out-megapool-validators.go index 729fc1be3..963ca9a93 100644 --- a/rocketpool/watchtower/dissolve-timed-out-megapool-validators.go +++ b/rocketpool/watchtower/dissolve-timed-out-megapool-validators.go @@ -4,10 +4,10 @@ import ( "fmt" "time" - "github.com/rocket-pool/rocketpool-go/megapool" - "github.com/rocket-pool/rocketpool-go/rocketpool" - "github.com/rocket-pool/rocketpool-go/settings/protocol" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/megapool" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/settings/protocol" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/rocket-pool/smartnode/rocketpool/watchtower/utils" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/services/config" diff --git a/rocketpool/watchtower/dissolve-timed-out-minipools.go b/rocketpool/watchtower/dissolve-timed-out-minipools.go index 09937e2f1..f913fef4a 100644 --- a/rocketpool/watchtower/dissolve-timed-out-minipools.go +++ b/rocketpool/watchtower/dissolve-timed-out-minipools.go @@ -6,10 +6,10 @@ import ( "time" "github.com/ethereum/go-ethereum/accounts/abi/bind" - "github.com/rocket-pool/rocketpool-go/minipool" - "github.com/rocket-pool/rocketpool-go/rocketpool" - rptypes "github.com/rocket-pool/rocketpool-go/types" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/minipool" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + rptypes "github.com/rocket-pool/smartnode/bindings/types" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/rocketpool/watchtower/utils" diff --git a/rocketpool/watchtower/finalize-pdao-proposals.go b/rocketpool/watchtower/finalize-pdao-proposals.go index a918eab46..8af0ff94b 100644 --- a/rocketpool/watchtower/finalize-pdao-proposals.go +++ b/rocketpool/watchtower/finalize-pdao-proposals.go @@ -3,10 +3,10 @@ package watchtower import ( "fmt" - "github.com/rocket-pool/rocketpool-go/dao/protocol" - "github.com/rocket-pool/rocketpool-go/rocketpool" - "github.com/rocket-pool/rocketpool-go/types" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/dao/protocol" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/types" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/rocketpool/watchtower/utils" diff --git a/rocketpool/watchtower/generate-rewards-tree.go b/rocketpool/watchtower/generate-rewards-tree.go index a93b6d782..d712d0770 100644 --- a/rocketpool/watchtower/generate-rewards-tree.go +++ b/rocketpool/watchtower/generate-rewards-tree.go @@ -15,8 +15,8 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethclient" - "github.com/rocket-pool/rocketpool-go/rewards" - "github.com/rocket-pool/rocketpool-go/rocketpool" + "github.com/rocket-pool/smartnode/bindings/rewards" + "github.com/rocket-pool/smartnode/bindings/rocketpool" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/services/beacon" "github.com/rocket-pool/smartnode/shared/services/config" diff --git a/rocketpool/watchtower/process-penalties.go b/rocketpool/watchtower/process-penalties.go index 77f1c2a5a..9fca79137 100644 --- a/rocketpool/watchtower/process-penalties.go +++ b/rocketpool/watchtower/process-penalties.go @@ -13,12 +13,12 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" - "github.com/rocket-pool/rocketpool-go/dao/trustednode" - "github.com/rocket-pool/rocketpool-go/minipool" - "github.com/rocket-pool/rocketpool-go/network" - "github.com/rocket-pool/rocketpool-go/node" - "github.com/rocket-pool/rocketpool-go/rocketpool" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/dao/trustednode" + "github.com/rocket-pool/smartnode/bindings/minipool" + "github.com/rocket-pool/smartnode/bindings/network" + "github.com/rocket-pool/smartnode/bindings/node" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/rocket-pool/smartnode/shared/services/beacon" "github.com/rocket-pool/smartnode/shared/services/config" rpgas "github.com/rocket-pool/smartnode/shared/services/gas" diff --git a/rocketpool/watchtower/respond-challenges.go b/rocketpool/watchtower/respond-challenges.go index d958f36e7..009636f74 100644 --- a/rocketpool/watchtower/respond-challenges.go +++ b/rocketpool/watchtower/respond-challenges.go @@ -3,9 +3,9 @@ package watchtower import ( "fmt" - "github.com/rocket-pool/rocketpool-go/dao/trustednode" - "github.com/rocket-pool/rocketpool-go/rocketpool" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/dao/trustednode" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/rocketpool/watchtower/utils" diff --git a/rocketpool/watchtower/submit-network-balances.go b/rocketpool/watchtower/submit-network-balances.go index 951566e69..af9c7c402 100644 --- a/rocketpool/watchtower/submit-network-balances.go +++ b/rocketpool/watchtower/submit-network-balances.go @@ -10,12 +10,12 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" - "github.com/rocket-pool/rocketpool-go/megapool" - "github.com/rocket-pool/rocketpool-go/network" - "github.com/rocket-pool/rocketpool-go/rocketpool" - rptypes "github.com/rocket-pool/rocketpool-go/types" - "github.com/rocket-pool/rocketpool-go/utils/eth" - rpstate "github.com/rocket-pool/rocketpool-go/utils/state" + "github.com/rocket-pool/smartnode/bindings/megapool" + "github.com/rocket-pool/smartnode/bindings/network" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + rptypes "github.com/rocket-pool/smartnode/bindings/types" + "github.com/rocket-pool/smartnode/bindings/utils/eth" + rpstate "github.com/rocket-pool/smartnode/bindings/utils/state" "github.com/urfave/cli" "golang.org/x/sync/errgroup" diff --git a/rocketpool/watchtower/submit-rewards-tree-stateless.go b/rocketpool/watchtower/submit-rewards-tree-stateless.go index 1028b6453..d87bf9973 100644 --- a/rocketpool/watchtower/submit-rewards-tree-stateless.go +++ b/rocketpool/watchtower/submit-rewards-tree-stateless.go @@ -16,10 +16,10 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" - "github.com/rocket-pool/rocketpool-go/rewards" - "github.com/rocket-pool/rocketpool-go/rocketpool" - "github.com/rocket-pool/rocketpool-go/tokens" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/rewards" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/tokens" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/rocket-pool/smartnode/rocketpool/watchtower/utils" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/services/beacon" diff --git a/rocketpool/watchtower/submit-rpl-price.go b/rocketpool/watchtower/submit-rpl-price.go index 56a463da8..9f752ff0b 100644 --- a/rocketpool/watchtower/submit-rpl-price.go +++ b/rocketpool/watchtower/submit-rpl-price.go @@ -16,10 +16,10 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" - "github.com/rocket-pool/rocketpool-go/dao/trustednode" - "github.com/rocket-pool/rocketpool-go/network" - "github.com/rocket-pool/rocketpool-go/rocketpool" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/dao/trustednode" + "github.com/rocket-pool/smartnode/bindings/network" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/rocketpool/watchtower/utils" @@ -375,29 +375,30 @@ func (t *submitRplPrice) run(state *state.NetworkState) error { eth2Config := state.BeaconConfig var hasSubmittedPastBlock bool + var eventFound bool var nextSubmissionTime time.Time var targetBlockNumber uint64 var lastSubmissionSlotTimestamp uint64 // Check if the node has submitted prices for the latest block if lastSubmissionBlock != 0 { - found, lastSubmissionEvent, err := network.GetPriceUpdatedEvent(t.rp, lastSubmissionBlock, nil) + eventFound, lastSubmissionEvent, err := network.GetPriceUpdatedEvent(t.rp, lastSubmissionBlock, nil) if err != nil { t.log.Printlnf("Error getting price submission event for block %d", lastSubmissionBlock) return err } - if !found { + if !eventFound { t.log.Printlnf("No price submission event found for block %d", lastSubmissionBlock) - return fmt.Errorf("no price submission event found for block %d", lastSubmissionBlock) - } - lastSubmissionSlotTimestamp = lastSubmissionEvent.SlotTimestamp.Uint64() + } else { + lastSubmissionSlotTimestamp = lastSubmissionEvent.SlotTimestamp.Uint64() - hasSubmittedPastBlock, err = t.hasSubmittedSpecificBlockPrices(nodeAccount.Address, lastSubmissionBlock, lastSubmissionSlotTimestamp, state.NetworkDetails.RplPrice) - if err != nil { - t.log.Printlnf("Error checking if node has submitted prices for block %d: %s", lastSubmissionBlock, err.Error()) - return err + hasSubmittedPastBlock, err = t.hasSubmittedSpecificBlockPrices(nodeAccount.Address, lastSubmissionBlock, lastSubmissionSlotTimestamp, state.NetworkDetails.RplPrice) + if err != nil { + t.log.Printlnf("Error checking if node has submitted prices for block %d: %s", lastSubmissionBlock, err.Error()) + return err + } } } - if hasSubmittedPastBlock || lastSubmissionBlock == 0 { + if hasSubmittedPastBlock || lastSubmissionBlock == 0 || !eventFound { // If the node participated in consensus, find the next submission target var targetBlockHeader *types.Header _, nextSubmissionTime, targetBlockHeader, err = utils.FindNextSubmissionTarget(t.rp, eth2Config, t.bc, t.ec, lastSubmissionBlock, referenceTimestamp, submissionIntervalInSeconds) diff --git a/rocketpool/watchtower/submit-scrub-minipools.go b/rocketpool/watchtower/submit-scrub-minipools.go index 6508ecbeb..1bd1cff3b 100644 --- a/rocketpool/watchtower/submit-scrub-minipools.go +++ b/rocketpool/watchtower/submit-scrub-minipools.go @@ -11,12 +11,12 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/signing" prdeposit "github.com/prysmaticlabs/prysm/v5/contracts/deposit" - "github.com/rocket-pool/rocketpool-go/minipool" - "github.com/rocket-pool/rocketpool-go/rocketpool" - "github.com/rocket-pool/rocketpool-go/types" - rputils "github.com/rocket-pool/rocketpool-go/utils" - "github.com/rocket-pool/rocketpool-go/utils/eth" - rpstate "github.com/rocket-pool/rocketpool-go/utils/state" + "github.com/rocket-pool/smartnode/bindings/minipool" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/types" + rputils "github.com/rocket-pool/smartnode/bindings/utils" + "github.com/rocket-pool/smartnode/bindings/utils/eth" + rpstate "github.com/rocket-pool/smartnode/bindings/utils/state" "github.com/urfave/cli" ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1" diff --git a/rocketpool/watchtower/utils/utils.go b/rocketpool/watchtower/utils/utils.go index f08deb2bb..04b8dab1d 100644 --- a/rocketpool/watchtower/utils/utils.go +++ b/rocketpool/watchtower/utils/utils.go @@ -8,7 +8,7 @@ import ( "time" "github.com/ethereum/go-ethereum/core/types" - "github.com/rocket-pool/rocketpool-go/rocketpool" + "github.com/rocket-pool/smartnode/bindings/rocketpool" "github.com/rocket-pool/smartnode/shared/services/beacon" "github.com/rocket-pool/smartnode/shared/services/config" ) diff --git a/rocketpool/watchtower/watchtower.go b/rocketpool/watchtower/watchtower.go index cfec04d89..cf32a83aa 100644 --- a/rocketpool/watchtower/watchtower.go +++ b/rocketpool/watchtower/watchtower.go @@ -13,8 +13,8 @@ import ( "github.com/fatih/color" "github.com/urfave/cli" - "github.com/rocket-pool/rocketpool-go/dao/trustednode" - "github.com/rocket-pool/rocketpool-go/rocketpool" + "github.com/rocket-pool/smartnode/bindings/dao/trustednode" + "github.com/rocket-pool/smartnode/bindings/rocketpool" "github.com/rocket-pool/smartnode/rocketpool/watchtower/collectors" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/services/beacon" diff --git a/shared/go.mod b/shared/go.mod index e62298ff5..c2e5de344 100644 --- a/shared/go.mod +++ b/shared/go.mod @@ -33,7 +33,6 @@ require ( github.com/prysmaticlabs/go-bitfield v0.0.0-20210809151128-385d8c5e3fb7 github.com/prysmaticlabs/prysm/v5 v5.0.3 github.com/rivo/tview v0.0.0-20230208211350-7dfff1ce7854 - github.com/rocket-pool/rocketpool-go v1.8.4-0.20241122223132-c5f2be18f72b github.com/sethvargo/go-password v0.2.0 github.com/shirou/gopsutil/v3 v3.23.1 github.com/tyler-smith/go-bip39 v1.1.0 diff --git a/shared/services/bc-manager.go b/shared/services/bc-manager.go index 2c8b0777c..82966e2bb 100644 --- a/shared/services/bc-manager.go +++ b/shared/services/bc-manager.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/fatih/color" - "github.com/rocket-pool/rocketpool-go/types" + "github.com/rocket-pool/smartnode/bindings/types" "github.com/rocket-pool/smartnode/shared/services/beacon" "github.com/rocket-pool/smartnode/shared/services/beacon/client" "github.com/rocket-pool/smartnode/shared/services/config" diff --git a/shared/services/beacon/client.go b/shared/services/beacon/client.go index 9263a9bc9..4c277c640 100644 --- a/shared/services/beacon/client.go +++ b/shared/services/beacon/client.go @@ -6,7 +6,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/prysmaticlabs/go-bitfield" - "github.com/rocket-pool/rocketpool-go/types" + "github.com/rocket-pool/smartnode/bindings/types" ) // API request options diff --git a/shared/services/beacon/client/std-http-client.go b/shared/services/beacon/client/std-http-client.go index a3530eceb..9e054f63f 100644 --- a/shared/services/beacon/client/std-http-client.go +++ b/shared/services/beacon/client/std-http-client.go @@ -18,7 +18,7 @@ import ( "github.com/goccy/go-json" "github.com/prysmaticlabs/go-bitfield" "github.com/prysmaticlabs/prysm/v5/crypto/bls" - "github.com/rocket-pool/rocketpool-go/types" + "github.com/rocket-pool/smartnode/bindings/types" eth2types "github.com/wealdtech/go-eth2-types/v2" "golang.org/x/sync/errgroup" diff --git a/shared/services/eth1.go b/shared/services/eth1.go index e748aa3ec..f444a4dd3 100644 --- a/shared/services/eth1.go +++ b/shared/services/eth1.go @@ -3,7 +3,7 @@ package services import ( "context" - "github.com/rocket-pool/rocketpool-go/rocketpool" + "github.com/rocket-pool/smartnode/bindings/rocketpool" ) func GetEthClientLatestBlockTimestamp(ec rocketpool.ExecutionClient) (uint64, error) { diff --git a/shared/services/gas/gas.go b/shared/services/gas/gas.go index f2316b876..0db14e94b 100644 --- a/shared/services/gas/gas.go +++ b/shared/services/gas/gas.go @@ -5,8 +5,8 @@ import ( "math/big" "strconv" - "github.com/rocket-pool/rocketpool-go/rocketpool" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/rocket-pool/smartnode/shared/services/gas/etherchain" "github.com/rocket-pool/smartnode/shared/services/gas/etherscan" rpsvc "github.com/rocket-pool/smartnode/shared/services/rocketpool" diff --git a/shared/services/megapools.go b/shared/services/megapools.go index d788f0ba9..e8ae26ab9 100644 --- a/shared/services/megapools.go +++ b/shared/services/megapools.go @@ -12,14 +12,14 @@ import ( "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/signing" prdeposit "github.com/prysmaticlabs/prysm/v5/contracts/deposit" ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1" - "github.com/rocket-pool/rocketpool-go/megapool" - "github.com/rocket-pool/rocketpool-go/network" - "github.com/rocket-pool/rocketpool-go/node" - "github.com/rocket-pool/rocketpool-go/rocketpool" - "github.com/rocket-pool/rocketpool-go/settings/protocol" - "github.com/rocket-pool/rocketpool-go/storage" - "github.com/rocket-pool/rocketpool-go/types" - rptypes "github.com/rocket-pool/rocketpool-go/types" + "github.com/rocket-pool/smartnode/bindings/megapool" + "github.com/rocket-pool/smartnode/bindings/network" + "github.com/rocket-pool/smartnode/bindings/node" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/settings/protocol" + "github.com/rocket-pool/smartnode/bindings/storage" + "github.com/rocket-pool/smartnode/bindings/types" + rptypes "github.com/rocket-pool/smartnode/bindings/types" "github.com/rocket-pool/smartnode/shared/services/beacon" "github.com/rocket-pool/smartnode/shared/services/wallet" "github.com/rocket-pool/smartnode/shared/types/api" diff --git a/shared/services/proposals/network-tree-manager.go b/shared/services/proposals/network-tree-manager.go index 5b759f8ce..ceef5aa7b 100644 --- a/shared/services/proposals/network-tree-manager.go +++ b/shared/services/proposals/network-tree-manager.go @@ -9,7 +9,7 @@ import ( "github.com/blang/semver/v4" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/types" + "github.com/rocket-pool/smartnode/bindings/types" "github.com/rocket-pool/smartnode/shared/services/config" cfgtypes "github.com/rocket-pool/smartnode/shared/types/config" "github.com/rocket-pool/smartnode/shared/utils/log" diff --git a/shared/services/proposals/node-tree-manager.go b/shared/services/proposals/node-tree-manager.go index 7b9622e61..4ee7ed9c8 100644 --- a/shared/services/proposals/node-tree-manager.go +++ b/shared/services/proposals/node-tree-manager.go @@ -9,7 +9,7 @@ import ( "github.com/blang/semver/v4" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/types" + "github.com/rocket-pool/smartnode/bindings/types" "github.com/rocket-pool/smartnode/shared/services/config" cfgtypes "github.com/rocket-pool/smartnode/shared/types/config" "github.com/rocket-pool/smartnode/shared/utils/log" diff --git a/shared/services/proposals/proposal-manager.go b/shared/services/proposals/proposal-manager.go index 8fe4f4d57..71829e5af 100644 --- a/shared/services/proposals/proposal-manager.go +++ b/shared/services/proposals/proposal-manager.go @@ -5,9 +5,9 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/dao/protocol" - "github.com/rocket-pool/rocketpool-go/rocketpool" - "github.com/rocket-pool/rocketpool-go/types" + "github.com/rocket-pool/smartnode/bindings/dao/protocol" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/types" "github.com/rocket-pool/smartnode/shared/services/beacon" "github.com/rocket-pool/smartnode/shared/services/config" "github.com/rocket-pool/smartnode/shared/services/state" diff --git a/shared/services/proposals/vi-snapshot-manager.go b/shared/services/proposals/vi-snapshot-manager.go index 5b3ff56e6..1bb6c6d03 100644 --- a/shared/services/proposals/vi-snapshot-manager.go +++ b/shared/services/proposals/vi-snapshot-manager.go @@ -11,9 +11,9 @@ import ( "github.com/blang/semver/v4" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/network" - "github.com/rocket-pool/rocketpool-go/rocketpool" - "github.com/rocket-pool/rocketpool-go/types" + "github.com/rocket-pool/smartnode/bindings/network" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/types" "github.com/rocket-pool/smartnode/shared" "github.com/rocket-pool/smartnode/shared/services/config" cfgtypes "github.com/rocket-pool/smartnode/shared/types/config" diff --git a/shared/services/proposals/voting-tree.go b/shared/services/proposals/voting-tree.go index 8712572ce..0e1b06b80 100755 --- a/shared/services/proposals/voting-tree.go +++ b/shared/services/proposals/voting-tree.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" - "github.com/rocket-pool/rocketpool-go/types" + "github.com/rocket-pool/smartnode/bindings/types" "github.com/rocket-pool/smartnode/shared" cfgtypes "github.com/rocket-pool/smartnode/shared/types/config" ) diff --git a/shared/services/requirements.go b/shared/services/requirements.go index bbdf2cddc..7b72fa663 100644 --- a/shared/services/requirements.go +++ b/shared/services/requirements.go @@ -9,10 +9,10 @@ import ( "time" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/dao/security" - "github.com/rocket-pool/rocketpool-go/dao/trustednode" - "github.com/rocket-pool/rocketpool-go/node" - "github.com/rocket-pool/rocketpool-go/rocketpool" + "github.com/rocket-pool/smartnode/bindings/dao/security" + "github.com/rocket-pool/smartnode/bindings/dao/trustednode" + "github.com/rocket-pool/smartnode/bindings/node" + "github.com/rocket-pool/smartnode/bindings/rocketpool" "github.com/rocket-pool/smartnode/shared/services/alerting" "github.com/rocket-pool/smartnode/shared/services/config" "github.com/urfave/cli" diff --git a/shared/services/rewards/execution-client.go b/shared/services/rewards/execution-client.go index 418f825e0..ee3907634 100644 --- a/shared/services/rewards/execution-client.go +++ b/shared/services/rewards/execution-client.go @@ -8,9 +8,9 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" ethtypes "github.com/ethereum/go-ethereum/core/types" - "github.com/rocket-pool/rocketpool-go/rewards" - "github.com/rocket-pool/rocketpool-go/rocketpool" - "github.com/rocket-pool/rocketpool-go/settings/trustednode" + "github.com/rocket-pool/smartnode/bindings/rewards" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/settings/trustednode" ) // Interface assertion diff --git a/shared/services/rewards/generator-impl-v8.go b/shared/services/rewards/generator-impl-v8.go index e6c412fd2..ed03cc952 100644 --- a/shared/services/rewards/generator-impl-v8.go +++ b/shared/services/rewards/generator-impl-v8.go @@ -11,10 +11,10 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ipfs/go-cid" - "github.com/rocket-pool/rocketpool-go/rewards" - rptypes "github.com/rocket-pool/rocketpool-go/types" - "github.com/rocket-pool/rocketpool-go/utils/eth" - rpstate "github.com/rocket-pool/rocketpool-go/utils/state" + "github.com/rocket-pool/smartnode/bindings/rewards" + rptypes "github.com/rocket-pool/smartnode/bindings/types" + "github.com/rocket-pool/smartnode/bindings/utils/eth" + rpstate "github.com/rocket-pool/smartnode/bindings/utils/state" "github.com/rocket-pool/smartnode/shared/services/beacon" "github.com/rocket-pool/smartnode/shared/services/config" "github.com/rocket-pool/smartnode/shared/services/state" diff --git a/shared/services/rewards/generator-impl-v9-v10.go b/shared/services/rewards/generator-impl-v9-v10.go index 6d7630e36..a7e9e0b1d 100644 --- a/shared/services/rewards/generator-impl-v9-v10.go +++ b/shared/services/rewards/generator-impl-v9-v10.go @@ -12,9 +12,9 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ipfs/go-cid" - "github.com/rocket-pool/rocketpool-go/rewards" - rptypes "github.com/rocket-pool/rocketpool-go/types" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/rewards" + rptypes "github.com/rocket-pool/smartnode/bindings/types" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/rocket-pool/smartnode/shared/services/beacon" "github.com/rocket-pool/smartnode/shared/services/config" "github.com/rocket-pool/smartnode/shared/services/rewards/fees" diff --git a/shared/services/rewards/mock_v10_test.go b/shared/services/rewards/mock_v10_test.go index 9b6e9721e..2ae78e30e 100644 --- a/shared/services/rewards/mock_v10_test.go +++ b/shared/services/rewards/mock_v10_test.go @@ -13,7 +13,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/fatih/color" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/rocket-pool/smartnode/shared/services/beacon" "github.com/rocket-pool/smartnode/shared/services/rewards/test" "github.com/rocket-pool/smartnode/shared/services/rewards/test/assets" diff --git a/shared/services/rewards/rewards-file-v1.go b/shared/services/rewards/rewards-file-v1.go index bab73c94c..a0487c82c 100644 --- a/shared/services/rewards/rewards-file-v1.go +++ b/shared/services/rewards/rewards-file-v1.go @@ -8,8 +8,8 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/goccy/go-json" - "github.com/rocket-pool/rocketpool-go/types" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/types" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/wealdtech/go-merkletree" "github.com/wealdtech/go-merkletree/keccak256" ) diff --git a/shared/services/rewards/rewards-file-v2.go b/shared/services/rewards/rewards-file-v2.go index 522b251ca..b20c06e24 100644 --- a/shared/services/rewards/rewards-file-v2.go +++ b/shared/services/rewards/rewards-file-v2.go @@ -8,7 +8,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/goccy/go-json" - "github.com/rocket-pool/rocketpool-go/types" + "github.com/rocket-pool/smartnode/bindings/types" "github.com/wealdtech/go-merkletree" "github.com/wealdtech/go-merkletree/keccak256" ) diff --git a/shared/services/rewards/test/assets/assets.go b/shared/services/rewards/test/assets/assets.go index 7faf79d73..43a661c83 100644 --- a/shared/services/rewards/test/assets/assets.go +++ b/shared/services/rewards/test/assets/assets.go @@ -10,7 +10,7 @@ import ( "time" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/rewards" + "github.com/rocket-pool/smartnode/bindings/rewards" "github.com/rocket-pool/smartnode/shared/services/state" ) diff --git a/shared/services/rewards/test/beacon.go b/shared/services/rewards/test/beacon.go index 0e1cfbeac..0de602df8 100644 --- a/shared/services/rewards/test/beacon.go +++ b/shared/services/rewards/test/beacon.go @@ -8,7 +8,7 @@ import ( "testing" "github.com/prysmaticlabs/go-bitfield" - "github.com/rocket-pool/rocketpool-go/types" + "github.com/rocket-pool/smartnode/bindings/types" "github.com/rocket-pool/smartnode/shared/services/beacon" "github.com/rocket-pool/smartnode/shared/services/state" ) diff --git a/shared/services/rewards/test/mock.go b/shared/services/rewards/test/mock.go index fd30ded81..984c5d31f 100644 --- a/shared/services/rewards/test/mock.go +++ b/shared/services/rewards/test/mock.go @@ -6,10 +6,10 @@ import ( "time" "github.com/ethereum/go-ethereum/common" - rprewards "github.com/rocket-pool/rocketpool-go/rewards" - "github.com/rocket-pool/rocketpool-go/types" - "github.com/rocket-pool/rocketpool-go/utils/eth" - rpstate "github.com/rocket-pool/rocketpool-go/utils/state" + rprewards "github.com/rocket-pool/smartnode/bindings/rewards" + "github.com/rocket-pool/smartnode/bindings/types" + "github.com/rocket-pool/smartnode/bindings/utils/eth" + rpstate "github.com/rocket-pool/smartnode/bindings/utils/state" "github.com/rocket-pool/smartnode/shared/services/beacon" "github.com/rocket-pool/smartnode/shared/services/rewards/fees" "github.com/rocket-pool/smartnode/shared/services/state" diff --git a/shared/services/rewards/test/rocketpool.go b/shared/services/rewards/test/rocketpool.go index 5d123424d..d95ad93f8 100644 --- a/shared/services/rewards/test/rocketpool.go +++ b/shared/services/rewards/test/rocketpool.go @@ -9,8 +9,8 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" - "github.com/rocket-pool/rocketpool-go/rewards" - "github.com/rocket-pool/rocketpool-go/rocketpool" + "github.com/rocket-pool/smartnode/bindings/rewards" + "github.com/rocket-pool/smartnode/bindings/rocketpool" ) // MockRocketPool is a EC mock specifically for testing treegen. diff --git a/shared/services/rewards/types.go b/shared/services/rewards/types.go index 006cb2995..06791d0f9 100644 --- a/shared/services/rewards/types.go +++ b/shared/services/rewards/types.go @@ -10,8 +10,8 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" ethtypes "github.com/ethereum/go-ethereum/core/types" - "github.com/rocket-pool/rocketpool-go/rewards" - "github.com/rocket-pool/rocketpool-go/types" + "github.com/rocket-pool/smartnode/bindings/rewards" + "github.com/rocket-pool/smartnode/bindings/types" "github.com/rocket-pool/smartnode/shared/services/beacon" "github.com/wealdtech/go-merkletree" ) diff --git a/shared/services/rewards/utils.go b/shared/services/rewards/utils.go index c8c27a752..6c14e0764 100644 --- a/shared/services/rewards/utils.go +++ b/shared/services/rewards/utils.go @@ -16,9 +16,9 @@ import ( "github.com/goccy/go-json" "github.com/klauspost/compress/zstd" "github.com/mitchellh/go-homedir" - "github.com/rocket-pool/rocketpool-go/rewards" - "github.com/rocket-pool/rocketpool-go/rocketpool" - rpstate "github.com/rocket-pool/rocketpool-go/utils/state" + "github.com/rocket-pool/smartnode/bindings/rewards" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + rpstate "github.com/rocket-pool/smartnode/bindings/utils/state" "github.com/rocket-pool/smartnode/shared/services/beacon" "github.com/rocket-pool/smartnode/shared/services/config" cfgtypes "github.com/rocket-pool/smartnode/shared/types/config" diff --git a/shared/services/rocketpool/node.go b/shared/services/rocketpool/node.go index 8b0a7de91..a446bba18 100644 --- a/shared/services/rocketpool/node.go +++ b/shared/services/rocketpool/node.go @@ -10,7 +10,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/goccy/go-json" - "github.com/rocket-pool/rocketpool-go/types" + "github.com/rocket-pool/smartnode/bindings/types" "github.com/rocket-pool/smartnode/shared/types/api" utils "github.com/rocket-pool/smartnode/shared/utils/api" ) diff --git a/shared/services/rocketpool/pdao.go b/shared/services/rocketpool/pdao.go index e6e299148..9a45df31a 100644 --- a/shared/services/rocketpool/pdao.go +++ b/shared/services/rocketpool/pdao.go @@ -9,7 +9,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/goccy/go-json" - "github.com/rocket-pool/rocketpool-go/types" + "github.com/rocket-pool/smartnode/bindings/types" "github.com/rocket-pool/smartnode/shared/types/api" ) diff --git a/shared/services/services.go b/shared/services/services.go index 0edb98b5f..e50dccf08 100644 --- a/shared/services/services.go +++ b/shared/services/services.go @@ -9,8 +9,8 @@ import ( "github.com/docker/docker/client" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethclient" - "github.com/rocket-pool/rocketpool-go/rocketpool" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services/beacon" diff --git a/shared/services/state/cli/cli.go b/shared/services/state/cli/cli.go index 56d6b87dd..b9c8c8ba2 100644 --- a/shared/services/state/cli/cli.go +++ b/shared/services/state/cli/cli.go @@ -8,7 +8,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethclient" - "github.com/rocket-pool/rocketpool-go/rocketpool" + "github.com/rocket-pool/smartnode/bindings/rocketpool" "github.com/rocket-pool/smartnode/shared/services/beacon/client" "github.com/rocket-pool/smartnode/shared/services/config" "github.com/rocket-pool/smartnode/shared/services/state" diff --git a/shared/services/state/manager.go b/shared/services/state/manager.go index 25c856fb9..8af9747ac 100644 --- a/shared/services/state/manager.go +++ b/shared/services/state/manager.go @@ -7,7 +7,7 @@ import ( "time" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/rocketpool" + "github.com/rocket-pool/smartnode/bindings/rocketpool" "github.com/rocket-pool/smartnode/shared/services/beacon" "github.com/rocket-pool/smartnode/shared/services/config" "github.com/rocket-pool/smartnode/shared/utils/log" diff --git a/shared/services/state/network-state.go b/shared/services/state/network-state.go index 9ce0706b9..defcee7cf 100644 --- a/shared/services/state/network-state.go +++ b/shared/services/state/network-state.go @@ -8,12 +8,12 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/dao/protocol" - "github.com/rocket-pool/rocketpool-go/megapool" - "github.com/rocket-pool/rocketpool-go/rocketpool" - "github.com/rocket-pool/rocketpool-go/types" - "github.com/rocket-pool/rocketpool-go/utils/eth" - rpstate "github.com/rocket-pool/rocketpool-go/utils/state" + "github.com/rocket-pool/smartnode/bindings/dao/protocol" + "github.com/rocket-pool/smartnode/bindings/megapool" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/types" + "github.com/rocket-pool/smartnode/bindings/utils/eth" + rpstate "github.com/rocket-pool/smartnode/bindings/utils/state" "github.com/rocket-pool/smartnode/shared/services/beacon" "github.com/rocket-pool/smartnode/shared/services/config" "github.com/rocket-pool/smartnode/shared/utils/log" diff --git a/shared/services/state/update-checks.go b/shared/services/state/update-checks.go index c4bbb521d..57ab8ea7f 100644 --- a/shared/services/state/update-checks.go +++ b/shared/services/state/update-checks.go @@ -3,8 +3,8 @@ package state import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/hashicorp/go-version" - "github.com/rocket-pool/rocketpool-go/rocketpool" - "github.com/rocket-pool/rocketpool-go/utils" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/utils" ) // Check if Saturn has been deployed diff --git a/shared/services/state/utils.go b/shared/services/state/utils.go index d7bb23dd0..7b92c8e51 100644 --- a/shared/services/state/utils.go +++ b/shared/services/state/utils.go @@ -5,8 +5,8 @@ import ( "time" "github.com/ethereum/go-ethereum/accounts/abi/bind" - "github.com/rocket-pool/rocketpool-go/rewards" - "github.com/rocket-pool/rocketpool-go/rocketpool" + "github.com/rocket-pool/smartnode/bindings/rewards" + "github.com/rocket-pool/smartnode/bindings/rocketpool" ) // TODO: temp until rocketpool-go supports RocketStorage contract address lookups per block diff --git a/shared/services/wallet/keystore/keystore.go b/shared/services/wallet/keystore/keystore.go index b655ec7e6..123cf8353 100644 --- a/shared/services/wallet/keystore/keystore.go +++ b/shared/services/wallet/keystore/keystore.go @@ -1,7 +1,7 @@ package keystore import ( - "github.com/rocket-pool/rocketpool-go/types" + "github.com/rocket-pool/smartnode/bindings/types" "github.com/sethvargo/go-password/password" eth2types "github.com/wealdtech/go-eth2-types/v2" ) diff --git a/shared/services/wallet/keystore/lighthouse/keystore.go b/shared/services/wallet/keystore/lighthouse/keystore.go index 3877f60e8..f992514ec 100644 --- a/shared/services/wallet/keystore/lighthouse/keystore.go +++ b/shared/services/wallet/keystore/lighthouse/keystore.go @@ -7,7 +7,7 @@ import ( "github.com/goccy/go-json" "github.com/google/uuid" - "github.com/rocket-pool/rocketpool-go/types" + "github.com/rocket-pool/smartnode/bindings/types" eth2types "github.com/wealdtech/go-eth2-types/v2" eth2ks "github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4" diff --git a/shared/services/wallet/keystore/lodestar/keystore.go b/shared/services/wallet/keystore/lodestar/keystore.go index 932d10294..9290364b5 100644 --- a/shared/services/wallet/keystore/lodestar/keystore.go +++ b/shared/services/wallet/keystore/lodestar/keystore.go @@ -8,7 +8,7 @@ import ( "github.com/goccy/go-json" "github.com/google/uuid" - "github.com/rocket-pool/rocketpool-go/types" + "github.com/rocket-pool/smartnode/bindings/types" eth2types "github.com/wealdtech/go-eth2-types/v2" eth2ks "github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4" diff --git a/shared/services/wallet/keystore/nimbus/keystore.go b/shared/services/wallet/keystore/nimbus/keystore.go index 7090dc11e..864a4576e 100644 --- a/shared/services/wallet/keystore/nimbus/keystore.go +++ b/shared/services/wallet/keystore/nimbus/keystore.go @@ -7,7 +7,7 @@ import ( "github.com/goccy/go-json" "github.com/google/uuid" - "github.com/rocket-pool/rocketpool-go/types" + "github.com/rocket-pool/smartnode/bindings/types" eth2types "github.com/wealdtech/go-eth2-types/v2" eth2ks "github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4" diff --git a/shared/services/wallet/keystore/prysm/keystore.go b/shared/services/wallet/keystore/prysm/keystore.go index 527c07b7b..1410ea0e4 100644 --- a/shared/services/wallet/keystore/prysm/keystore.go +++ b/shared/services/wallet/keystore/prysm/keystore.go @@ -9,7 +9,7 @@ import ( "github.com/goccy/go-json" "github.com/google/uuid" - "github.com/rocket-pool/rocketpool-go/types" + "github.com/rocket-pool/smartnode/bindings/types" rpkeystore "github.com/rocket-pool/smartnode/shared/services/wallet/keystore" eth2types "github.com/wealdtech/go-eth2-types/v2" eth2ks "github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4" diff --git a/shared/services/wallet/keystore/teku/keystore.go b/shared/services/wallet/keystore/teku/keystore.go index 1a138d0e8..ca1361184 100644 --- a/shared/services/wallet/keystore/teku/keystore.go +++ b/shared/services/wallet/keystore/teku/keystore.go @@ -7,7 +7,7 @@ import ( "github.com/goccy/go-json" "github.com/google/uuid" - "github.com/rocket-pool/rocketpool-go/types" + "github.com/rocket-pool/smartnode/bindings/types" eth2types "github.com/wealdtech/go-eth2-types/v2" eth2ks "github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4" diff --git a/shared/services/wallet/validator.go b/shared/services/wallet/validator.go index b31be9dbc..b29e8ddb4 100644 --- a/shared/services/wallet/validator.go +++ b/shared/services/wallet/validator.go @@ -7,8 +7,8 @@ import ( "os" "strings" - "github.com/rocket-pool/rocketpool-go/types" - rptypes "github.com/rocket-pool/rocketpool-go/types" + "github.com/rocket-pool/smartnode/bindings/types" + rptypes "github.com/rocket-pool/smartnode/bindings/types" "github.com/rocket-pool/smartnode/shared/utils/validator" eth2types "github.com/wealdtech/go-eth2-types/v2" eth2util "github.com/wealdtech/go-eth2-util" diff --git a/shared/types/api/auction.go b/shared/types/api/auction.go index 440e45d5b..8e88e7a04 100644 --- a/shared/types/api/auction.go +++ b/shared/types/api/auction.go @@ -5,8 +5,8 @@ import ( "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/auction" - "github.com/rocket-pool/rocketpool-go/rocketpool" + "github.com/rocket-pool/smartnode/bindings/auction" + "github.com/rocket-pool/smartnode/bindings/rocketpool" ) type AuctionStatusResponse struct { diff --git a/shared/types/api/megapool.go b/shared/types/api/megapool.go index b91248f16..47e70c66a 100644 --- a/shared/types/api/megapool.go +++ b/shared/types/api/megapool.go @@ -5,11 +5,11 @@ import ( "time" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/megapool" - "github.com/rocket-pool/rocketpool-go/network" - "github.com/rocket-pool/rocketpool-go/rocketpool" - "github.com/rocket-pool/rocketpool-go/tokens" - "github.com/rocket-pool/rocketpool-go/types" + "github.com/rocket-pool/smartnode/bindings/megapool" + "github.com/rocket-pool/smartnode/bindings/network" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/tokens" + "github.com/rocket-pool/smartnode/bindings/types" "github.com/rocket-pool/smartnode/shared/services/beacon" ) diff --git a/shared/types/api/minipool.go b/shared/types/api/minipool.go index 91a54465b..b3756db81 100644 --- a/shared/types/api/minipool.go +++ b/shared/types/api/minipool.go @@ -6,10 +6,10 @@ import ( "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/minipool" - "github.com/rocket-pool/rocketpool-go/rocketpool" - "github.com/rocket-pool/rocketpool-go/tokens" - "github.com/rocket-pool/rocketpool-go/types" + "github.com/rocket-pool/smartnode/bindings/minipool" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/tokens" + "github.com/rocket-pool/smartnode/bindings/types" "github.com/rocket-pool/smartnode/shared/services/beacon" ) diff --git a/shared/types/api/node.go b/shared/types/api/node.go index bae19200c..78f484b91 100644 --- a/shared/types/api/node.go +++ b/shared/types/api/node.go @@ -7,9 +7,9 @@ import ( "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/rocketpool" - "github.com/rocket-pool/rocketpool-go/tokens" - rptypes "github.com/rocket-pool/rocketpool-go/types" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/tokens" + rptypes "github.com/rocket-pool/smartnode/bindings/types" "github.com/rocket-pool/smartnode/shared/services/rewards" "github.com/rocket-pool/smartnode/shared/utils/rp" ) diff --git a/shared/types/api/odao.go b/shared/types/api/odao.go index 454ada441..a6a94d2a7 100644 --- a/shared/types/api/odao.go +++ b/shared/types/api/odao.go @@ -4,9 +4,9 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/dao" - tn "github.com/rocket-pool/rocketpool-go/dao/trustednode" - "github.com/rocket-pool/rocketpool-go/rocketpool" + "github.com/rocket-pool/smartnode/bindings/dao" + tn "github.com/rocket-pool/smartnode/bindings/dao/trustednode" + "github.com/rocket-pool/smartnode/bindings/rocketpool" ) type TNDAOStatusResponse struct { diff --git a/shared/types/api/pdao.go b/shared/types/api/pdao.go index e672eabd3..8dea07a40 100644 --- a/shared/types/api/pdao.go +++ b/shared/types/api/pdao.go @@ -5,9 +5,9 @@ import ( "time" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/dao/protocol" - "github.com/rocket-pool/rocketpool-go/rocketpool" - "github.com/rocket-pool/rocketpool-go/types" + "github.com/rocket-pool/smartnode/bindings/dao/protocol" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/types" ) type PDAOProposalWithNodeVoteDirection struct { diff --git a/shared/types/api/queue.go b/shared/types/api/queue.go index 8967e36f4..30bfd9d7a 100644 --- a/shared/types/api/queue.go +++ b/shared/types/api/queue.go @@ -4,7 +4,7 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/rocketpool" + "github.com/rocket-pool/smartnode/bindings/rocketpool" ) type QueueStatusResponse struct { diff --git a/shared/types/api/security.go b/shared/types/api/security.go index 10595c6a5..f06f868f5 100644 --- a/shared/types/api/security.go +++ b/shared/types/api/security.go @@ -2,9 +2,9 @@ package api import ( "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/dao" - "github.com/rocket-pool/rocketpool-go/dao/security" - "github.com/rocket-pool/rocketpool-go/rocketpool" + "github.com/rocket-pool/smartnode/bindings/dao" + "github.com/rocket-pool/smartnode/bindings/dao/security" + "github.com/rocket-pool/smartnode/bindings/rocketpool" ) type SecurityStatusResponse struct { diff --git a/shared/types/api/wallet.go b/shared/types/api/wallet.go index e27cca158..4ecd032ea 100644 --- a/shared/types/api/wallet.go +++ b/shared/types/api/wallet.go @@ -3,8 +3,8 @@ package api import ( "github.com/ethereum/go-ethereum/common" "github.com/google/uuid" - "github.com/rocket-pool/rocketpool-go/rocketpool" - "github.com/rocket-pool/rocketpool-go/types" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/types" ) // Encrypted validator keystore following the EIP-2335 standard diff --git a/shared/utils/api/utils.go b/shared/utils/api/utils.go index 1d8d62aed..0309ff2e5 100644 --- a/shared/utils/api/utils.go +++ b/shared/utils/api/utils.go @@ -9,10 +9,10 @@ import ( "time" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/rocketpool" - "github.com/rocket-pool/rocketpool-go/settings/protocol" - "github.com/rocket-pool/rocketpool-go/utils" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/settings/protocol" + "github.com/rocket-pool/smartnode/bindings/utils" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/rocket-pool/smartnode/shared/services/config" "github.com/rocket-pool/smartnode/shared/utils/log" "github.com/rocket-pool/smartnode/shared/utils/math" diff --git a/shared/utils/cli/validation.go b/shared/utils/cli/validation.go index 6dbce4864..24a82cd21 100644 --- a/shared/utils/cli/validation.go +++ b/shared/utils/cli/validation.go @@ -13,7 +13,7 @@ import ( "github.com/tyler-smith/go-bip39" "github.com/urfave/cli" - "github.com/rocket-pool/rocketpool-go/types" + "github.com/rocket-pool/smartnode/bindings/types" "github.com/rocket-pool/smartnode/shared/services/passwords" hexutils "github.com/rocket-pool/smartnode/shared/utils/hex" ) diff --git a/shared/utils/eth1/eth1.go b/shared/utils/eth1/eth1.go index 4e7799e29..50044da42 100644 --- a/shared/utils/eth1/eth1.go +++ b/shared/utils/eth1/eth1.go @@ -10,7 +10,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethclient" - "github.com/rocket-pool/rocketpool-go/rocketpool" + "github.com/rocket-pool/smartnode/bindings/rocketpool" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/services/config" "github.com/urfave/cli" diff --git a/shared/utils/eth2/eth2.go b/shared/utils/eth2/eth2.go index d8c1d56b9..ee29f29b3 100644 --- a/shared/utils/eth2/eth2.go +++ b/shared/utils/eth2/eth2.go @@ -5,11 +5,11 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/minipool" - "github.com/rocket-pool/rocketpool-go/rocketpool" - "github.com/rocket-pool/rocketpool-go/types" - "github.com/rocket-pool/rocketpool-go/utils/eth" - rpstate "github.com/rocket-pool/rocketpool-go/utils/state" + "github.com/rocket-pool/smartnode/bindings/minipool" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/types" + "github.com/rocket-pool/smartnode/bindings/utils/eth" + rpstate "github.com/rocket-pool/smartnode/bindings/utils/state" "github.com/rocket-pool/smartnode/shared/services/beacon" "github.com/rocket-pool/smartnode/shared/services/state" rputils "github.com/rocket-pool/smartnode/shared/utils/rp" diff --git a/shared/utils/rp/fee-recipient.go b/shared/utils/rp/fee-recipient.go index 0d5d2d098..e5093c761 100644 --- a/shared/utils/rp/fee-recipient.go +++ b/shared/utils/rp/fee-recipient.go @@ -6,8 +6,8 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/node" - "github.com/rocket-pool/rocketpool-go/rocketpool" + "github.com/rocket-pool/smartnode/bindings/node" + "github.com/rocket-pool/smartnode/bindings/rocketpool" "github.com/rocket-pool/smartnode/shared/services/beacon" "github.com/rocket-pool/smartnode/shared/services/state" "golang.org/x/sync/errgroup" diff --git a/shared/utils/rp/minipools.go b/shared/utils/rp/minipools.go index e7eb8b4b4..a6c28c1a0 100644 --- a/shared/utils/rp/minipools.go +++ b/shared/utils/rp/minipools.go @@ -5,9 +5,9 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/minipool" - "github.com/rocket-pool/rocketpool-go/rocketpool" - "github.com/rocket-pool/rocketpool-go/types" + "github.com/rocket-pool/smartnode/bindings/minipool" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/types" "golang.org/x/sync/errgroup" "github.com/rocket-pool/smartnode/shared/services/beacon" diff --git a/shared/utils/rp/node.go b/shared/utils/rp/node.go index 7a5e4deda..353bd4823 100644 --- a/shared/utils/rp/node.go +++ b/shared/utils/rp/node.go @@ -9,11 +9,11 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/minipool" - "github.com/rocket-pool/rocketpool-go/node" - "github.com/rocket-pool/rocketpool-go/rocketpool" - tnsettings "github.com/rocket-pool/rocketpool-go/settings/trustednode" - "github.com/rocket-pool/rocketpool-go/types" + "github.com/rocket-pool/smartnode/bindings/minipool" + "github.com/rocket-pool/smartnode/bindings/node" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + tnsettings "github.com/rocket-pool/smartnode/bindings/settings/trustednode" + "github.com/rocket-pool/smartnode/bindings/types" "github.com/rocket-pool/smartnode/shared/services/beacon" "golang.org/x/sync/errgroup" ) diff --git a/shared/utils/validator/set-withdrawal-creds.go b/shared/utils/validator/set-withdrawal-creds.go index 5d9761ab2..2579d3b8b 100644 --- a/shared/utils/validator/set-withdrawal-creds.go +++ b/shared/utils/validator/set-withdrawal-creds.go @@ -6,7 +6,7 @@ import ( "strings" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/types" + "github.com/rocket-pool/smartnode/bindings/types" "github.com/rocket-pool/smartnode/shared/types/eth2/generic" eth2types "github.com/wealdtech/go-eth2-types/v2" ) diff --git a/shared/utils/validator/voluntary-exit.go b/shared/utils/validator/voluntary-exit.go index cccc1215a..69b466453 100644 --- a/shared/utils/validator/voluntary-exit.go +++ b/shared/utils/validator/voluntary-exit.go @@ -4,7 +4,7 @@ import ( "fmt" "strconv" - "github.com/rocket-pool/rocketpool-go/types" + "github.com/rocket-pool/smartnode/bindings/types" "github.com/rocket-pool/smartnode/shared/types/eth2/generic" eth2types "github.com/wealdtech/go-eth2-types/v2" ) diff --git a/shared/utils/wallet/recover-keys.go b/shared/utils/wallet/recover-keys.go index 2288af332..ead6d1d00 100644 --- a/shared/utils/wallet/recover-keys.go +++ b/shared/utils/wallet/recover-keys.go @@ -9,10 +9,10 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/goccy/go-json" - "github.com/rocket-pool/rocketpool-go/megapool" - "github.com/rocket-pool/rocketpool-go/minipool" - "github.com/rocket-pool/rocketpool-go/rocketpool" - "github.com/rocket-pool/rocketpool-go/types" + "github.com/rocket-pool/smartnode/bindings/megapool" + "github.com/rocket-pool/smartnode/bindings/minipool" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/types" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/services/config" "github.com/rocket-pool/smartnode/shared/services/wallet" diff --git a/treegen/go.mod b/treegen/go.mod index d4dbdb270..65baf0bd4 100644 --- a/treegen/go.mod +++ b/treegen/go.mod @@ -7,7 +7,6 @@ require ( github.com/fatih/color v1.14.1 github.com/felixge/fgprof v0.9.5 github.com/goccy/go-json v0.10.2 - github.com/rocket-pool/rocketpool-go v1.8.4-0.20241122223132-c5f2be18f72b github.com/urfave/cli/v2 v2.26.0 ) diff --git a/treegen/tree-gen.go b/treegen/tree-gen.go index c0692c649..76afd1e3d 100644 --- a/treegen/tree-gen.go +++ b/treegen/tree-gen.go @@ -17,9 +17,9 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethclient" "github.com/fatih/color" - "github.com/rocket-pool/rocketpool-go/rewards" - "github.com/rocket-pool/rocketpool-go/rocketpool" - "github.com/rocket-pool/rocketpool-go/utils/eth" + "github.com/rocket-pool/smartnode/bindings/rewards" + "github.com/rocket-pool/smartnode/bindings/rocketpool" + "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/rocket-pool/smartnode/shared/services/beacon" "github.com/rocket-pool/smartnode/shared/services/beacon/client" "github.com/rocket-pool/smartnode/shared/services/config"