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"