diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8b3fb0252..d10a7aebd 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -17,5 +17,4 @@ jobs: - uses: actions/setup-go@v4 with: go-version: 1.21.8 - - run: cd ${GITHUB_WORKSPACE}/rocketpool-cli && go build . - - run: cd ${GITHUB_WORKSPACE}/rocketpool && go build . + - run: make NO_DOCKER=true diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index eb58a30c0..be0c7e688 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -13,29 +13,41 @@ permissions: # Optional: allow read access to pull request. Use with `only-new-issues` option. # pull-requests: read jobs: - golangci: - name: lint + detect-modules: runs-on: ubuntu-latest + outputs: + modules: ${{ steps.set-modules.outputs.modules }} steps: - - uses: actions/setup-go@v4 + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 with: go-version: 1.21.8 - - uses: actions/checkout@v3 - - name: golangci-lint - uses: golangci/golangci-lint-action@v4 + - id: set-modules + run: echo "modules=$(go list -m -json | jq -s '.' | jq -c '[.[].Dir]')" >> $GITHUB_OUTPUT + golangci-lint: + needs: detect-modules + runs-on: ubuntu-latest + strategy: + max-parallel: 1 + matrix: + modules: ${{ fromJSON(needs.detect-modules.outputs.modules) }} + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version: 1.21.8 + - name: golangci-lint ${{ matrix.modules }} + uses: golangci/golangci-lint-action@v8 with: # Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version - version: latest + version: v2.1 # Optional: working directory, useful for monorepos - # working-directory: somedir + working-directory: ${{ matrix.modules }} # Optional: golangci-lint command line arguments. # args: --issues-exit-code=0 - # For now, Smart Node will only enforce goimports linting - args: --disable-all --enable goimports - # Optional: show only new issues if it's a pull request. The default value is `false`. # only-new-issues: true diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index ade236aef..7ffe73769 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -13,8 +13,9 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-go@v4 + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 with: go-version: 1.21.8 - - run: go test ./... -timeout 30m + cache-dependency-path: go.work.sum + - run: make test 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/.golangci.yml b/.golangci.yml new file mode 100644 index 000000000..5f7a6c0ea --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,13 @@ +version: "2" +linters: + default: none +formatters: + enable: + - goimports +run: + relative-path-mode: cfg + go: '1.21.8' +output: + formats: + text: + path: stderr diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..1ddca4d4a --- /dev/null +++ b/Makefile @@ -0,0 +1,161 @@ +VERSION=v$(shell cat shared/version.txt) +LOCAL_OS=$(shell go env GOOS)-$(shell go env GOARCH) + +BUILD_DIR=build +BIN_DIR=${BUILD_DIR}/${VERSION}/bin +DOCKER_DIR=${BUILD_DIR}/${VERSION}/docker + +CLI_TARGET_OOS:=linux darwin +ARCHS:=arm64 amd64 + +CLI_TARGET_STRINGS:=$(foreach oos,$(CLI_TARGET_OOS), $(foreach arch,$(ARCHS),${BIN_DIR}/rocketpool-cli-$(oos)-$(arch))) +DAEMON_TARGET_STRINGS:=$(foreach arch,$(ARCHS),${BIN_DIR}/rocketpool-daemon-linux-$(arch)) +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 +${BIN_DIR}/rocketpool-cli-$1-$2: ${bin_deps} + @echo "Building rocketpool-cli-$1-$2" +ifndef NO_DOCKER + docker run --rm -v ./:/src --user $(shell id -u):$(shell id -g) -e CGO_ENABLED=0 \ + -e GOARCH=$2 -e GOOS=$1 --workdir /src -v ~/.cache:/.cache rocketpool/smartnode-builder:${VERSION} \ + go build -o $$@ rocketpool-cli/rocketpool-cli.go +else + CGO_ENABLED=0 GOOS=$1 GOARCH=$2 go build -o $$@ ./rocketpool-cli/rocketpool-cli.go +endif +endef + +# Must be first- so `make` runs this. +.PHONY: default +default: ${BUILD_DIR}/rocketpool-cli ${BUILD_DIR}/rocketpool-daemon ${BUILD_DIR}/treegen lint + +.PHONY: all +all: ${BUILD_DIR}/rocketpool-cli ${BUILD_DIR}/rocketpool-daemon ${BUILD_DIR}/treegen lint + +.PHONY: release +release: ${CLI_TARGET_STRINGS} ${DAEMON_TARGET_STRINGS} ${TREEGEN_TARGET_STRINGS} ${BUILD_DIR}/rocketpool-cli ${BUILD_DIR}/rocketpool-daemon ${BUILD_DIR}/treegen + +# Target for build/rocketpool-cli which is a symlink to an os-specific build +${BUILD_DIR}/rocketpool-cli: ${BIN_DIR}/rocketpool-cli-${LOCAL_OS} + ln -sf $(shell pwd)/${BIN_DIR}/rocketpool-cli-${LOCAL_OS} ${BUILD_DIR}/rocketpool-cli + + +# Target for build/rocketpool-daemon which is a symlink to an os-specific build +${BUILD_DIR}/rocketpool-daemon: ${BIN_DIR}/rocketpool-daemon-${LOCAL_OS} + ln -sf $(shell pwd)/${BIN_DIR}/rocketpool-daemon-${LOCAL_OS} ${BUILD_DIR}/rocketpool-daemon + +# Target for build/treegen which is a symlink to a version-specific build +${BUILD_DIR}/treegen: ${BIN_DIR}/treegen-${LOCAL_OS} + ln -sf $(shell pwd)/${BIN_DIR}/treegen-${LOCAL_OS} ${BUILD_DIR}/treegen + +# docker-builder container +.PHONY: docker-builder +docker-builder: + VERSION=${VERSION} docker bake -f docker/daemon-bake.hcl builder + +bin_deps = ${BIN_DIR} +ifndef NO_DOCKER + bin_deps += docker-builder +endif + +docker_build_cmd_amd64 = docker run --rm -v ./:/src --user $(shell id -u):$(shell id -g) -e CGO_ENABLED=1 -e CGO_C_FLAGS="-O -D__BLST_PORTABLE__" \ + -e GOARCH=amd64 -e GOOS=linux --workdir /src -v ~/.cache:/.cache rocketpool/smartnode-builder:${VERSION} \ + go build +local_build_cmd_amd64 = CGO_ENABLED=1 CGO_C_FLAGS="-O -D__BLST_PORTABLE__" GOARCH=amd64 GOOS=linux go build +docker_build_cmd_arm64 = docker run --rm -v ./:/src --user $(shell id -u):$(shell id -g) -e CGO_ENABLED=1 -e CGO_C_FLAGS="-O -D__BLST_PORTABLE__" \ + -e CC=aarch64-linux-gnu-gcc -e CXX=aarch64-linux-gnu-cpp -e CGO_C_FLAGS="-O -D__BLST_PORTABLE__" -e GOARCH=arm64 -e GOOS=linux \ + --workdir /src -v ~/.cache:/.cache rocketpool/smartnode-builder:${VERSION} \ + go build +local_build_cmd_arm64 = CGO_ENABLED=1 CC=aarch64-linux-gnu-gcc CXX=aarch64-linux-gnu-cpp CGO_C_FLAGS="-O -D__BLST_PORTABLE__" GOARCH=arm64 GOOS=linux go build +# amd64 daemon build +.PHONY: ${BIN_DIR}/rocketpool-daemon-linux-amd64 +${BIN_DIR}/rocketpool-daemon-linux-amd64: ${bin_deps} +ifndef NO_DOCKER + ${docker_build_cmd_amd64} -o $@ rocketpool/rocketpool.go +else + ${local_build_cmd_amd64} -o $@ rocketpool/rocketpool.go +endif + +# arm64 daemon build +.PHONY: ${BIN_DIR}/rocketpool-daemon-linux-arm64 +${BIN_DIR}/rocketpool-daemon-linux-arm64: ${bin_deps} +ifndef NO_DOCKER + ${docker_build_cmd_arm64} -o $@ rocketpool/rocketpool.go +else + ${local_build_cmd_arm64} -o $@ rocketpool/rocketpool.go +endif + +${BIN_DIR}: + mkdir -p ${BIN_DIR} +${DOCKER_DIR}: + mkdir -p ${DOCKER_DIR} + +$(foreach oos,$(CLI_TARGET_OOS),$(foreach arch,$(ARCHS),$(eval $(call rocketpool-cli-template,$(oos),$(arch))))) + +# amd64 treegen build +.PHONY: ${BIN_DIR}/treegen-linux-amd64 +${BIN_DIR}/treegen-linux-amd64: ${bin_deps} +ifndef NO_DOCKER + ${docker_build_cmd_amd64} -o $@ ./treegen/. +else + ${local_build_cmd_amd64} -o $@ ./treegen/. +endif + +# arm64 treegen build +.PHONY: ${BIN_DIR}/treegen-linux-arm64 +${BIN_DIR}/treegen-linux-arm64: ${bin_deps} +ifndef NO_DOCKER + ${docker_build_cmd_arm64} -o $@ ./treegen/. +else + ${local_build_cmd_arm64} -o $@ ./treegen/. +endif + +# Docker containers +.PHONY: docker +docker: ${DOCKER_DIR} + VERSION=${VERSION} docker bake -f docker/daemon-bake.hcl smartnode + +.PHONY: docker-push +docker-push: docker + echo + echo -n "Publishing smartnode:${VERSION} containers. Continue? [yN]: " && read ans && if [ $${ans:-'N'} != 'y' ]; then exit 1; fi + rm -rf ~/.docker/manifests/docker.io_rocketpool_smartnode-${VERSION} + docker push rocketpool/smartnode:${VERSION}-amd64 + docker push rocketpool/smartnode:${VERSION}-arm64 + docker manifest create rocketpool/smartnode:${VERSION} --amend rocketpool/smartnode:${VERSION}-amd64 --amend rocketpool/smartnode:${VERSION}-arm64 + docker manifest push --purge rocketpool/smartnode:${VERSION} + echo "Done!" + +.PHONY: docker-latest +docker-latest: docker-push + echo + echo -n "Publishing smartnode:${VERSION} as latest. Continue? [yN]: " && read ans && if [ $${ans:-'N'} != 'y' ]; then exit 1; fi + rm -rf ~/.docker/manifests/docker.io_rocketpool_smartnode-latest + docker manifest create rocketpool/smartnode:latest --amend rocketpool/smartnode:${VERSION}-amd64 --amend rocketpool/smartnode:${VERSION}-arm64 + docker manifest push --purge rocketpool/smartnode:latest + +.PHONY: docker-prune +docker-prune: + docker system prune -af + docker buildx prune -af + +define lint-template +.PHONY: lint-$1 +lint-$1: + docker run -e GOCACHE=/go/.cache/go-build -e GOLANGCI_LINT_CACHE=/go/.cache/golangci-lint --user $(shell id -u):$(shell id -g) --rm -v ~/.cache:/go/.cache -v .:/smartnode --workdir /smartnode/$1 golangci/golangci-lint:v2.1-alpine golangci-lint fmt --diff +endef +$(foreach module,$(MODULES),$(eval $(call lint-template,$(module)))) +.PHONY: lint +lint: $(foreach module,$(MODULES),lint-$(module)) + +.PHONY: test +test: + go test -test.timeout 20m $(TEST_GLOBS) + +.PHONY: clean +clean: + rm -rf ${BUILD_DIR} diff --git a/README.md b/README.md index 92bc0e04e..b0c3d4923 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,29 @@ See the [Rocket Pool dockerhub](https://hub.docker.com/u/rocketpool) page for a See the [Smartnode Installer](https://github.com/rocket-pool/smartnode-install) repository for supported platforms and installation instructions. +## Development + +A [Makefile](./Makefile) is included for building, testing, and linting. + +* `make` or `make default` will build rocketpool-cli, rocketpool-daemon, treegen, and run the linter. + * build/rocketpool-cli and build/rocketpool-daemon will be symlinked to the version and architecture specific binaries in build/ +* `make all` will build rocketpool-cli, rocketpool-daemon, treegen, and run the linter. + * symlinks will be created for the first 3 binaries in build/ +* `make release` will build all architecture specific binaries as well as docker images and manifests + * It will tag docker images as latest as well as the version in `shared/version.txt` + * It will put cli and native mode binaries in build/\ +* `make build/rocketpool-cli` builds just the cli + * The build is done in docker, unless you run `make NO_DOCKER=true \` +* `make build/rocketpool-daemon` builds just the daemon + * The build is done in docker, unless you run `make NO_DOCKER=true \` +* `make build/treegen` builds just the treegen stand-alone binary. + * The build is done in docker, unless you run `make NO_DOCKER=true \` +* `make docker` builds the rocketpool/smartnode containers for all supported architectures and saves them in build/\/docker +* `make docker-push` builds, loads, pushes, creates a multi-arch manifest, and pushes the smartnode containers and manifest. +* `make docker-latest` does the same as docker-push, but tags latest which is also pushed. +* `make lint` runs the linter. +* `make test` runs all unit tests. +* `make clean` deletes any binaries. It does not clear your go caches. It does not clean up old docker images. ## CLI Commands diff --git a/addons/go.mod b/addons/go.mod new file mode 100644 index 000000000..27a123f4a --- /dev/null +++ b/addons/go.mod @@ -0,0 +1,14 @@ +module github.com/rocket-pool/smartnode/addons + +go 1.21 + +require ( + github.com/ethereum/go-ethereum v1.13.5 + google.golang.org/protobuf v1.34.2 +) + +require ( + github.com/google/go-cmp v0.6.0 // indirect + golang.org/x/crypto v0.26.0 // indirect + golang.org/x/sys v0.24.0 // indirect +) 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/build-builder.sh b/build-builder.sh deleted file mode 100755 index 3cd6eaa05..000000000 --- a/build-builder.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/bash - -# Print usage -usage() { - echo "Usage: build-release.sh -v " - echo "This script builds the Smartnode builder image used to build the daemon binaries." - exit 0 -} - -# ================= -# === Main Body === -# ================= - -# Get the version -while getopts "admv:" FLAG; do - case "$FLAG" in - v) VERSION="$OPTARG" ;; - *) usage ;; - esac -done -if [ -z "$VERSION" ]; then - usage -fi - -echo -n "Building Docker image... " -docker build -t rocketpool/smartnode-builder:$VERSION -f docker/smartnode-builder . -docker tag rocketpool/smartnode-builder:$VERSION rocketpool/smartnode-builder:latest -docker push rocketpool/smartnode-builder:$VERSION -docker push rocketpool/smartnode-builder:latest -echo "done!" \ No newline at end of file diff --git a/daemon-build.sh b/daemon-build.sh deleted file mode 100755 index 58e91b0b3..000000000 --- a/daemon-build.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash - -# Get the platform type and run the build script if possible -PLATFORM=$(uname -s) -if [ "$PLATFORM" = "Linux" ]; then - docker run --rm -v $PWD:/smartnode rocketpool/smartnode-builder:latest /smartnode/rocketpool/build.sh -else - echo "Platform ${PLATFORM} is not supported by this script, please build the daemon manually." - exit 1 -fi diff --git a/docker/daemon-bake.hcl b/docker/daemon-bake.hcl new file mode 100644 index 000000000..cba5a5c8f --- /dev/null +++ b/docker/daemon-bake.hcl @@ -0,0 +1,35 @@ +variable "VERSION" { + default = "$VERSION" +} + +group "default" { + targets = ["builder", "smartnode"] +} + +target "builder" { + dockerfile = "docker/rocketpool-dockerfile" + tags = [ + "rocketpool/smartnode-builder:${VERSION}", + "rocketpool/smartnode-builder:local" + ] + target = "smartnode_dependencies" + platforms = [ "linux/amd64" ] +} + +target "smartnode" { + name = "smartnode-${arch}" + dockerfile = "docker/rocketpool-dockerfile" + args = { + VERSION = "${VERSION}" + } + tags = [ + "rocketpool/smartnode:${VERSION}-${arch}", + "localhost/rocketpool/smartnode:${VERSION}-${arch}" + ] + matrix = { + arch = [ "amd64", "arm64" ] + } + target = "smartnode" + platform = "linux/${arch}" + output = [{ "type": "docker" }] +} diff --git a/docker/rocketpool-dockerfile b/docker/rocketpool-dockerfile index fa8f31859..b547756a1 100644 --- a/docker/rocketpool-dockerfile +++ b/docker/rocketpool-dockerfile @@ -1,9 +1,57 @@ -FROM debian:bookworm-slim +FROM golang:1.21.8-bookworm AS smartnode_dependencies +# Install build tools +RUN dpkg --add-architecture arm64 +RUN apt update && apt install -y \ + build-essential \ + gcc-aarch64-linux-gnu \ + libc6-dev-arm64-cross\ + g++-aarch64-linux-gnu \ + wget + +# Cache go dependencies +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 +COPY ./treegen/go.mod /src/treegen/go.mod +WORKDIR /src +RUN go mod download -x +WORKDIR / +RUN rm -rf /src + +FROM smartnode_dependencies AS build +ARG TARGETARCH +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/ +COPY ./treegen/ /src/treegen +COPY Makefile /src/Makefile +WORKDIR /src +RUN --mount=type=cache,target=/root/.cache/go-build make NO_DOCKER=true \ + build/${VERSION}/bin/rocketpool-daemon-linux-${TARGETARCH} \ + build/${VERSION}/bin/rocketpool-cli-linux-${TARGETARCH} \ + build/${VERSION}/bin/treegen-linux-${TARGETARCH} + +FROM debian:bookworm-slim AS smartnode ARG TARGETARCH -COPY ./rocketpool/rocketpool-daemon-linux-${TARGETARCH} /go/bin/rocketpool +ARG VERSION RUN apt update && apt install ca-certificates -y -# Container entry point +COPY --from=build /src/build/${VERSION}/bin/rocketpool-daemon-linux-${TARGETARCH} /go/bin/rocketpool +COPY --from=build /src/build/${VERSION}/bin/rocketpool-cli-linux-${TARGETARCH} /go/bin/rocketpool-cli +COPY --from=build /src/build/${VERSION}/bin/treegen-linux-${TARGETARCH} /go/bin/treegen + +ENV PATH="$PATH:/go/bin" + +# Default entry point ENTRYPOINT ["/go/bin/rocketpool"] diff --git a/docker/rocketpool-ec-migrator b/docker/rocketpool-ec-migrator deleted file mode 100644 index 5a37a7026..000000000 --- a/docker/rocketpool-ec-migrator +++ /dev/null @@ -1,11 +0,0 @@ -# Start from Alpine image -FROM alpine:latest - -# Install rsync -RUN apk add rsync - -# Copy the provisioning script -COPY rocketpool/ec_migrate.sh /srv - -# Container entry point -ENTRYPOINT ["/srv/ec_migrate.sh"] diff --git a/docker/rocketpool-prune-provision b/docker/rocketpool-prune-provision deleted file mode 100644 index 79c9c35e2..000000000 --- a/docker/rocketpool-prune-provision +++ /dev/null @@ -1,8 +0,0 @@ -# Start from Alpine image -FROM alpine:latest - -# Copy the provisioning script -COPY rocketpool/prune_provision.sh /srv - -# Container entry point -ENTRYPOINT ["/srv/prune_provision.sh"] diff --git a/docker/smartnode-builder b/docker/smartnode-builder deleted file mode 100644 index 79cc321c8..000000000 --- a/docker/smartnode-builder +++ /dev/null @@ -1,20 +0,0 @@ -# This image is used to build the Rocket Pool Smartnode and related artifacts - -FROM golang:1.21.8-bookworm - -# Install build tools -RUN dpkg --add-architecture arm64 -RUN apt update && apt install -y \ - build-essential \ - gcc-aarch64-linux-gnu \ - libc6-dev-arm64-cross\ - g++-aarch64-linux-gnu \ - wget - -# Cache go dependencies -ADD go.mod /src/go.mod -ADD go.sum /src/go.sum -WORKDIR /src -RUN go mod download all -WORKDIR / -RUN rm -rf /src diff --git a/go.work b/go.work new file mode 100644 index 000000000..8f8246b7c --- /dev/null +++ b/go.work @@ -0,0 +1,16 @@ +go 1.21.8 + +use ( + ./addons + ./bindings + ./rocketpool + ./rocketpool-cli + ./shared + ./treegen +) + +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.sum b/go.work.sum similarity index 51% rename from go.sum rename to go.work.sum index a09c99767..55b80d54d 100644 --- a/go.sum +++ b/go.work.sum @@ -18,55 +18,398 @@ cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmW cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= +cloud.google.com/go v0.110.0 h1:Zc8gqp3+a9/Eyph2KDmcGaPtbKRIoqq4YTlL4NMD0Ys= +cloud.google.com/go v0.110.0/go.mod h1:SJnCLqQ0FCFGSZMUNUf84MV3Aia54kn7pi8st7tMzaY= +cloud.google.com/go v0.110.2/go.mod h1:k04UEeEtb6ZBRTv3dZz4CeJC3jKGxyhl0sAiVVquxiw= +cloud.google.com/go/accessapproval v1.6.0 h1:x0cEHro/JFPd7eS4BlEWNTMecIj2HdXjOVB5BtvwER0= +cloud.google.com/go/accessapproval v1.6.0/go.mod h1:R0EiYnwV5fsRFiKZkPHr6mwyk2wxUJ30nL4j2pcFY2E= +cloud.google.com/go/accesscontextmanager v1.7.0 h1:MG60JgnEoawHJrbWw0jGdv6HLNSf6gQvYRiXpuzqgEA= +cloud.google.com/go/accesscontextmanager v1.7.0/go.mod h1:CEGLewx8dwa33aDAZQujl7Dx+uYhS0eay198wB/VumQ= +cloud.google.com/go/aiplatform v1.37.0 h1:zTw+suCVchgZyO+k847wjzdVjWmrAuehxdvcZvJwfGg= +cloud.google.com/go/aiplatform v1.37.0/go.mod h1:IU2Cv29Lv9oCn/9LkFiiuKfwrRTq+QQMbW+hPCxJGZw= +cloud.google.com/go/analytics v0.19.0 h1:LqAo3tAh2FU9+w/r7vc3hBjU23Kv7GhO/PDIW7kIYgM= +cloud.google.com/go/analytics v0.19.0/go.mod h1:k8liqf5/HCnOUkbawNtrWWc+UAzyDlW89doe8TtoDsE= +cloud.google.com/go/apigateway v1.5.0 h1:ZI9mVO7x3E9RK/BURm2p1aw9YTBSCQe3klmyP1WxWEg= +cloud.google.com/go/apigateway v1.5.0/go.mod h1:GpnZR3Q4rR7LVu5951qfXPJCHquZt02jf7xQx7kpqN8= +cloud.google.com/go/apigeeconnect v1.5.0 h1:sWOmgDyAsi1AZ48XRHcATC0tsi9SkPT7DA/+VCfkaeA= +cloud.google.com/go/apigeeconnect v1.5.0/go.mod h1:KFaCqvBRU6idyhSNyn3vlHXc8VMDJdRmwDF6JyFRqZ8= +cloud.google.com/go/apigeeregistry v0.6.0 h1:E43RdhhCxdlV+I161gUY2rI4eOaMzHTA5kNkvRsFXvc= +cloud.google.com/go/apigeeregistry v0.6.0/go.mod h1:BFNzW7yQVLZ3yj0TKcwzb8n25CFBri51GVGOEUcgQsc= +cloud.google.com/go/apikeys v0.6.0 h1:B9CdHFZTFjVti89tmyXXrO+7vSNo2jvZuHG8zD5trdQ= +cloud.google.com/go/apikeys v0.6.0/go.mod h1:kbpXu5upyiAlGkKrJgQl8A0rKNNJ7dQ377pdroRSSi8= +cloud.google.com/go/appengine v1.7.1 h1:aBGDKmRIaRRoWJ2tAoN0oVSHoWLhtO9aj/NvUyP4aYs= +cloud.google.com/go/appengine v1.7.1/go.mod h1:IHLToyb/3fKutRysUlFO0BPt5j7RiQ45nrzEJmKTo6E= +cloud.google.com/go/area120 v0.7.1 h1:ugckkFh4XkHJMPhTIx0CyvdoBxmOpMe8rNs4Ok8GAag= +cloud.google.com/go/area120 v0.7.1/go.mod h1:j84i4E1RboTWjKtZVWXPqvK5VHQFJRF2c1Nm69pWm9k= +cloud.google.com/go/artifactregistry v1.13.0 h1:o1Q80vqEB6Qp8WLEH3b8FBLNUCrGQ4k5RFj0sn/sgO8= +cloud.google.com/go/artifactregistry v1.13.0/go.mod h1:uy/LNfoOIivepGhooAUpL1i30Hgee3Cu0l4VTWHUC08= +cloud.google.com/go/asset v1.13.0 h1:YAsssO08BqZ6mncbb6FPlj9h6ACS7bJQUOlzciSfbNk= +cloud.google.com/go/asset v1.13.0/go.mod h1:WQAMyYek/b7NBpYq/K4KJWcRqzoalEsxz/t/dTk4THw= +cloud.google.com/go/assuredworkloads v1.10.0 h1:VLGnVFta+N4WM+ASHbhc14ZOItOabDLH1MSoDv+Xuag= +cloud.google.com/go/assuredworkloads v1.10.0/go.mod h1:kwdUQuXcedVdsIaKgKTp9t0UJkE5+PAVNhdQm4ZVq2E= +cloud.google.com/go/automl v1.12.0 h1:50VugllC+U4IGl3tDNcZaWvApHBTrn/TvyHDJ0wM+Uw= +cloud.google.com/go/automl v1.12.0/go.mod h1:tWDcHDp86aMIuHmyvjuKeeHEGq76lD7ZqfGLN6B0NuU= +cloud.google.com/go/baremetalsolution v0.5.0 h1:2AipdYXL0VxMboelTTw8c1UJ7gYu35LZYUbuRv9Q28s= +cloud.google.com/go/baremetalsolution v0.5.0/go.mod h1:dXGxEkmR9BMwxhzBhV0AioD0ULBmuLZI8CdwalUxuss= +cloud.google.com/go/batch v0.7.0 h1:YbMt0E6BtqeD5FvSv1d56jbVsWEzlGm55lYte+M6Mzs= +cloud.google.com/go/batch v0.7.0/go.mod h1:vLZN95s6teRUqRQ4s3RLDsH8PvboqBK+rn1oevL159g= +cloud.google.com/go/beyondcorp v0.5.0 h1:UkY2BTZkEUAVrgqnSdOJ4p3y9ZRBPEe1LkjgC8Bj/Pc= +cloud.google.com/go/beyondcorp v0.5.0/go.mod h1:uFqj9X+dSfrheVp7ssLTaRHd2EHqSL4QZmH4e8WXGGU= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/bigquery v1.50.0 h1:RscMV6LbnAmhAzD893Lv9nXXy2WCaJmbxYPWDLbGqNQ= +cloud.google.com/go/bigquery v1.50.0/go.mod h1:YrleYEh2pSEbgTBZYMJ5SuSr0ML3ypjRB1zgf7pvQLU= +cloud.google.com/go/billing v1.13.0 h1:JYj28UYF5w6VBAh0gQYlgHJ/OD1oA+JgW29YZQU+UHM= +cloud.google.com/go/billing v1.13.0/go.mod h1:7kB2W9Xf98hP9Sr12KfECgfGclsH3CQR0R08tnRlRbc= +cloud.google.com/go/binaryauthorization v1.5.0 h1:d3pMDBCCNivxt5a4eaV7FwL7cSH0H7RrEnFrTb1QKWs= +cloud.google.com/go/binaryauthorization v1.5.0/go.mod h1:OSe4OU1nN/VswXKRBmciKpo9LulY41gch5c68htf3/Q= +cloud.google.com/go/certificatemanager v1.6.0 h1:5C5UWeSt8Jkgp7OWn2rCkLmYurar/vIWIoSQ2+LaTOc= +cloud.google.com/go/certificatemanager v1.6.0/go.mod h1:3Hh64rCKjRAX8dXgRAyOcY5vQ/fE1sh8o+Mdd6KPgY8= +cloud.google.com/go/channel v1.12.0 h1:GpcQY5UJKeOekYgsX3QXbzzAc/kRGtBq43fTmyKe6Uw= +cloud.google.com/go/channel v1.12.0/go.mod h1:VkxCGKASi4Cq7TbXxlaBezonAYpp1GCnKMY6tnMQnLU= +cloud.google.com/go/cloudbuild v1.9.0 h1:GHQCjV4WlPPVU/j3Rlpc8vNIDwThhd1U9qSY/NPZdko= +cloud.google.com/go/cloudbuild v1.9.0/go.mod h1:qK1d7s4QlO0VwfYn5YuClDGg2hfmLZEb4wQGAbIgL1s= +cloud.google.com/go/clouddms v1.5.0 h1:E7v4TpDGUyEm1C/4KIrpVSOCTm0P6vWdHT0I4mostRA= +cloud.google.com/go/clouddms v1.5.0/go.mod h1:QSxQnhikCLUw13iAbffF2CZxAER3xDGNHjsTAkQJcQA= +cloud.google.com/go/cloudtasks v1.10.0 h1:uK5k6abf4yligFgYFnG0ni8msai/dSv6mDmiBulU0hU= +cloud.google.com/go/cloudtasks v1.10.0/go.mod h1:NDSoTLkZ3+vExFEWu2UJV1arUyzVDAiZtdWcsUyNwBs= +cloud.google.com/go/compute v1.18.0/go.mod h1:1X7yHxec2Ga+Ss6jPyjxRxpu2uu7PLgsOVXvgU0yacs= +cloud.google.com/go/compute v1.19.0/go.mod h1:rikpw2y+UMidAe9tISo04EHNOIf42RLYF/q8Bs93scU= +cloud.google.com/go/compute v1.19.1 h1:am86mquDUgjGNWxiGn+5PGLbmgiWXlE/yNWpIpNvuXY= +cloud.google.com/go/compute v1.19.1/go.mod h1:6ylj3a05WF8leseCdIf77NK0g1ey+nj5IKd5/kvShxE= +cloud.google.com/go/compute v1.19.3/go.mod h1:qxvISKp/gYnXkSAD1ppcSOveRAmzxicEv/JlizULFrI= +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= +cloud.google.com/go/container v1.15.0 h1:NKlY/wCDapfVZlbVVaeuu2UZZED5Dy1z4Zx1KhEzm8c= +cloud.google.com/go/container v1.15.0/go.mod h1:ft+9S0WGjAyjDggg5S06DXj+fHJICWg8L7isCQe9pQA= +cloud.google.com/go/containeranalysis v0.9.0 h1:EQ4FFxNaEAg8PqQCO7bVQfWz9NVwZCUKaM1b3ycfx3U= +cloud.google.com/go/containeranalysis v0.9.0/go.mod h1:orbOANbwk5Ejoom+s+DUCTTJ7IBdBQJDcSylAx/on9s= +cloud.google.com/go/datacatalog v1.13.0 h1:4H5IJiyUE0X6ShQBqgFFZvGGcrwGVndTwUSLP4c52gw= +cloud.google.com/go/datacatalog v1.13.0/go.mod h1:E4Rj9a5ZtAxcQJlEBTLgMTphfP11/lNaAshpoBgemX8= +cloud.google.com/go/dataflow v0.8.0 h1:eYyD9o/8Nm6EttsKZaEGD84xC17bNgSKCu0ZxwqUbpg= +cloud.google.com/go/dataflow v0.8.0/go.mod h1:Rcf5YgTKPtQyYz8bLYhFoIV/vP39eL7fWNcSOyFfLJE= +cloud.google.com/go/dataform v0.7.0 h1:Dyk+fufup1FR6cbHjFpMuP4SfPiF3LI3JtoIIALoq48= +cloud.google.com/go/dataform v0.7.0/go.mod h1:7NulqnVozfHvWUBpMDfKMUESr+85aJsC/2O0o3jWPDE= +cloud.google.com/go/datafusion v1.6.0 h1:sZjRnS3TWkGsu1LjYPFD/fHeMLZNXDK6PDHi2s2s/bk= +cloud.google.com/go/datafusion v1.6.0/go.mod h1:WBsMF8F1RhSXvVM8rCV3AeyWVxcC2xY6vith3iw3S+8= +cloud.google.com/go/datalabeling v0.7.0 h1:ch4qA2yvddGRUrlfwrNJCr79qLqhS9QBwofPHfFlDIk= +cloud.google.com/go/datalabeling v0.7.0/go.mod h1:WPQb1y08RJbmpM3ww0CSUAGweL0SxByuW2E+FU+wXcM= +cloud.google.com/go/dataplex v1.6.0 h1:RvoZ5T7gySwm1CHzAw7yY1QwwqaGswunmqEssPxU/AM= +cloud.google.com/go/dataplex v1.6.0/go.mod h1:bMsomC/aEJOSpHXdFKFGQ1b0TDPIeL28nJObeO1ppRs= +cloud.google.com/go/dataproc v1.12.0 h1:W47qHL3W4BPkAIbk4SWmIERwsWBaNnWm0P2sdx3YgGU= +cloud.google.com/go/dataproc v1.12.0/go.mod h1:zrF3aX0uV3ikkMz6z4uBbIKyhRITnxvr4i3IjKsKrw4= +cloud.google.com/go/dataqna v0.7.0 h1:yFzi/YU4YAdjyo7pXkBE2FeHbgz5OQQBVDdbErEHmVQ= +cloud.google.com/go/dataqna v0.7.0/go.mod h1:Lx9OcIIeqCrw1a6KdO3/5KMP1wAmTc0slZWwP12Qq3c= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/datastore v1.11.0 h1:iF6I/HaLs3Ado8uRKMvZRvF/ZLkWaWE9i8AiHzbC774= +cloud.google.com/go/datastore v1.11.0/go.mod h1:TvGxBIHCS50u8jzG+AW/ppf87v1of8nwzFNgEZU1D3c= +cloud.google.com/go/datastream v1.7.0 h1:BBCBTnWMDwwEzQQmipUXxATa7Cm7CA/gKjKcR2w35T0= +cloud.google.com/go/datastream v1.7.0/go.mod h1:uxVRMm2elUSPuh65IbZpzJNMbuzkcvu5CjMqVIUHrww= +cloud.google.com/go/deploy v1.8.0 h1:otshdKEbmsi1ELYeCKNYppwV0UH5xD05drSdBm7ouTk= +cloud.google.com/go/deploy v1.8.0/go.mod h1:z3myEJnA/2wnB4sgjqdMfgxCA0EqC3RBTNcVPs93mtQ= +cloud.google.com/go/dialogflow v1.32.0 h1:uVlKKzp6G/VtSW0E7IH1Y5o0H48/UOCmqksG2riYCwQ= +cloud.google.com/go/dialogflow v1.32.0/go.mod h1:jG9TRJl8CKrDhMEcvfcfFkkpp8ZhgPz3sBGmAUYJ2qE= +cloud.google.com/go/dlp v1.9.0 h1:1JoJqezlgu6NWCroBxr4rOZnwNFILXr4cB9dMaSKO4A= +cloud.google.com/go/dlp v1.9.0/go.mod h1:qdgmqgTyReTz5/YNSSuueR8pl7hO0o9bQ39ZhtgkWp4= +cloud.google.com/go/documentai v1.18.0 h1:KM3Xh0QQyyEdC8Gs2vhZfU+rt6OCPF0dwVwxKgLmWfI= +cloud.google.com/go/documentai v1.18.0/go.mod h1:F6CK6iUH8J81FehpskRmhLq/3VlwQvb7TvwOceQ2tbs= +cloud.google.com/go/domains v0.8.0 h1:2ti/o9tlWL4N+wIuWUNH+LbfgpwxPr8J1sv9RHA4bYQ= +cloud.google.com/go/domains v0.8.0/go.mod h1:M9i3MMDzGFXsydri9/vW+EWz9sWb4I6WyHqdlAk0idE= +cloud.google.com/go/edgecontainer v1.0.0 h1:O0YVE5v+O0Q/ODXYsQHmHb+sYM8KNjGZw2pjX2Ws41c= +cloud.google.com/go/edgecontainer v1.0.0/go.mod h1:cttArqZpBB2q58W/upSG++ooo6EsblxDIolxa3jSjbY= +cloud.google.com/go/errorreporting v0.3.0 h1:kj1XEWMu8P0qlLhm3FwcaFsUvXChV/OraZwA70trRR0= +cloud.google.com/go/errorreporting v0.3.0/go.mod h1:xsP2yaAp+OAW4OIm60An2bbLpqIhKXdWR/tawvl7QzU= +cloud.google.com/go/essentialcontacts v1.5.0 h1:gIzEhCoOT7bi+6QZqZIzX1Erj4SswMPIteNvYVlu+pM= +cloud.google.com/go/essentialcontacts v1.5.0/go.mod h1:ay29Z4zODTuwliK7SnX8E86aUF2CTzdNtvv42niCX0M= +cloud.google.com/go/eventarc v1.11.0 h1:fsJmNeqvqtk74FsaVDU6cH79lyZNCYP8Rrv7EhaB/PU= +cloud.google.com/go/eventarc v1.11.0/go.mod h1:PyUjsUKPWoRBCHeOxZd/lbOOjahV41icXyUY5kSTvVY= +cloud.google.com/go/filestore v1.6.0 h1:ckTEXN5towyTMu4q0uQ1Mde/JwTHur0gXs8oaIZnKfw= +cloud.google.com/go/filestore v1.6.0/go.mod h1:di5unNuss/qfZTw2U9nhFqo8/ZDSc466dre85Kydllg= +cloud.google.com/go/firestore v1.9.0 h1:IBlRyxgGySXu5VuW0RgGFlTtLukSnNkpDiEOMkQkmpA= +cloud.google.com/go/firestore v1.9.0/go.mod h1:HMkjKHNTtRyZNiMzu7YAsLr9K3X2udY2AMwDaMEQiiE= +cloud.google.com/go/functions v1.13.0 h1:pPDqtsXG2g9HeOQLoquLbmvmb82Y4Ezdo1GXuotFoWg= +cloud.google.com/go/functions v1.13.0/go.mod h1:EU4O007sQm6Ef/PwRsI8N2umygGqPBS/IZQKBQBcJ3c= +cloud.google.com/go/gaming v1.9.0 h1:7vEhFnZmd931Mo7sZ6pJy7uQPDxF7m7v8xtBheG08tc= +cloud.google.com/go/gaming v1.9.0/go.mod h1:Fc7kEmCObylSWLO334NcO+O9QMDyz+TKC4v1D7X+Bc0= +cloud.google.com/go/gkebackup v0.4.0 h1:za3QZvw6ujR0uyqkhomKKKNoXDyqYGPJies3voUK8DA= +cloud.google.com/go/gkebackup v0.4.0/go.mod h1:byAyBGUwYGEEww7xsbnUTBHIYcOPy/PgUWUtOeRm9Vg= +cloud.google.com/go/gkeconnect v0.7.0 h1:gXYKciHS/Lgq0GJ5Kc9SzPA35NGc3yqu6SkjonpEr2Q= +cloud.google.com/go/gkeconnect v0.7.0/go.mod h1:SNfmVqPkaEi3bF/B3CNZOAYPYdg7sU+obZ+QTky2Myw= +cloud.google.com/go/gkehub v0.12.0 h1:TqCSPsEBQ6oZSJgEYZ3XT8x2gUadbvfwI32YB0kuHCs= +cloud.google.com/go/gkehub v0.12.0/go.mod h1:djiIwwzTTBrF5NaXCGv3mf7klpEMcST17VBTVVDcuaw= +cloud.google.com/go/gkemulticloud v0.5.0 h1:8I84Q4vl02rJRsFiinBxl7WCozfdLlUVBQuSrqr9Wtk= +cloud.google.com/go/gkemulticloud v0.5.0/go.mod h1:W0JDkiyi3Tqh0TJr//y19wyb1yf8llHVto2Htf2Ja3Y= +cloud.google.com/go/grafeas v0.2.0/go.mod h1:KhxgtF2hb0P191HlY5besjYm6MqTSTj3LSI+M+ByZHc= +cloud.google.com/go/gsuiteaddons v1.5.0 h1:1mvhXqJzV0Vg5Fa95QwckljODJJfDFXV4pn+iL50zzA= +cloud.google.com/go/gsuiteaddons v1.5.0/go.mod h1:TFCClYLd64Eaa12sFVmUyG62tk4mdIsI7pAnSXRkcFo= +cloud.google.com/go/iam v0.13.0 h1:+CmB+K0J/33d0zSQ9SlFWUeCCEn5XJA0ZMZ3pHE9u8k= +cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCtaLZ/D0= +cloud.google.com/go/iap v1.7.1 h1:PxVHFuMxmSZyfntKXHXhd8bo82WJ+LcATenq7HLdVnU= +cloud.google.com/go/iap v1.7.1/go.mod h1:WapEwPc7ZxGt2jFGB/C/bm+hP0Y6NXzOYGjpPnmMS74= +cloud.google.com/go/ids v1.3.0 h1:fodnCDtOXuMmS8LTC2y3h8t24U8F3eKWfhi+3LY6Qf0= +cloud.google.com/go/ids v1.3.0/go.mod h1:JBdTYwANikFKaDP6LtW5JAi4gubs57SVNQjemdt6xV4= +cloud.google.com/go/iot v1.6.0 h1:39W5BFSarRNZfVG0eXI5LYux+OVQT8GkgpHCnrZL2vM= +cloud.google.com/go/iot v1.6.0/go.mod h1:IqdAsmE2cTYYNO1Fvjfzo9po179rAtJeVGUvkLN3rLE= +cloud.google.com/go/kms v1.10.1 h1:7hm1bRqGCA1GBRQUrp831TwJ9TWhP+tvLuP497CQS2g= +cloud.google.com/go/kms v1.10.1/go.mod h1:rIWk/TryCkR59GMC3YtHtXeLzd634lBbKenvyySAyYI= +cloud.google.com/go/language v1.9.0 h1:7Ulo2mDk9huBoBi8zCE3ONOoBrL6UXfAI71CLQ9GEIM= +cloud.google.com/go/language v1.9.0/go.mod h1:Ns15WooPM5Ad/5no/0n81yUetis74g3zrbeJBE+ptUY= +cloud.google.com/go/lifesciences v0.8.0 h1:uWrMjWTsGjLZpCTWEAzYvyXj+7fhiZST45u9AgasasI= +cloud.google.com/go/lifesciences v0.8.0/go.mod h1:lFxiEOMqII6XggGbOnKiyZ7IBwoIqA84ClvoezaA/bo= +cloud.google.com/go/logging v1.7.0 h1:CJYxlNNNNAMkHp9em/YEXcfJg+rPDg7YfwoRpMU+t5I= +cloud.google.com/go/logging v1.7.0/go.mod h1:3xjP2CjkM3ZkO73aj4ASA5wRPGGCRrPIAeNqVNkzY8M= +cloud.google.com/go/longrunning v0.4.1 h1:v+yFJOfKC3yZdY6ZUI933pIYdhyhV8S3NpWrXWmg7jM= +cloud.google.com/go/longrunning v0.4.1/go.mod h1:4iWDqhBZ70CvZ6BfETbvam3T8FMvLK+eFj0E6AaRQTo= +cloud.google.com/go/managedidentities v1.5.0 h1:ZRQ4k21/jAhrHBVKl/AY7SjgzeJwG1iZa+mJ82P+VNg= +cloud.google.com/go/managedidentities v1.5.0/go.mod h1:+dWcZ0JlUmpuxpIDfyP5pP5y0bLdRwOS4Lp7gMni/LA= +cloud.google.com/go/maps v0.7.0 h1:mv9YaczD4oZBZkM5XJl6fXQ984IkJNHPwkc8MUsdkBo= +cloud.google.com/go/maps v0.7.0/go.mod h1:3GnvVl3cqeSvgMcpRlQidXsPYuDGQ8naBis7MVzpXsY= +cloud.google.com/go/mediatranslation v0.7.0 h1:anPxH+/WWt8Yc3EdoEJhPMBRF7EhIdz426A+tuoA0OU= +cloud.google.com/go/mediatranslation v0.7.0/go.mod h1:LCnB/gZr90ONOIQLgSXagp8XUW1ODs2UmUMvcgMfI2I= +cloud.google.com/go/memcache v1.9.0 h1:8/VEmWCpnETCrBwS3z4MhT+tIdKgR1Z4Tr2tvYH32rg= +cloud.google.com/go/memcache v1.9.0/go.mod h1:8oEyzXCu+zo9RzlEaEjHl4KkgjlNDaXbCQeQWlzNFJM= +cloud.google.com/go/metastore v1.10.0 h1:QCFhZVe2289KDBQ7WxaHV2rAmPrmRAdLC6gbjUd3HPo= +cloud.google.com/go/metastore v1.10.0/go.mod h1:fPEnH3g4JJAk+gMRnrAnoqyv2lpUCqJPWOodSaf45Eo= +cloud.google.com/go/monitoring v1.13.0 h1:2qsrgXGVoRXpP7otZ14eE1I568zAa92sJSDPyOJvwjM= +cloud.google.com/go/monitoring v1.13.0/go.mod h1:k2yMBAB1H9JT/QETjNkgdCGD9bPF712XiLTVr+cBrpw= +cloud.google.com/go/networkconnectivity v1.11.0 h1:ZD6b4Pk1jEtp/cx9nx0ZYcL3BKqDa+KixNDZ6Bjs1B8= +cloud.google.com/go/networkconnectivity v1.11.0/go.mod h1:iWmDD4QF16VCDLXUqvyspJjIEtBR/4zq5hwnY2X3scM= +cloud.google.com/go/networkmanagement v1.6.0 h1:8KWEUNGcpSX9WwZXq7FtciuNGPdPdPN/ruDm769yAEM= +cloud.google.com/go/networkmanagement v1.6.0/go.mod h1:5pKPqyXjB/sgtvB5xqOemumoQNB7y95Q7S+4rjSOPYY= +cloud.google.com/go/networksecurity v0.8.0 h1:sOc42Ig1K2LiKlzG71GUVloeSJ0J3mffEBYmvu+P0eo= +cloud.google.com/go/networksecurity v0.8.0/go.mod h1:B78DkqsxFG5zRSVuwYFRZ9Xz8IcQ5iECsNrPn74hKHU= +cloud.google.com/go/notebooks v1.8.0 h1:Kg2K3K7CbSXYJHZ1aGQpf1xi5x2GUvQWf2sFVuiZh8M= +cloud.google.com/go/notebooks v1.8.0/go.mod h1:Lq6dYKOYOWUCTvw5t2q1gp1lAp0zxAxRycayS0iJcqQ= +cloud.google.com/go/optimization v1.3.1 h1:dj8O4VOJRB4CUwZXdmwNViH1OtI0WtWL867/lnYH248= +cloud.google.com/go/optimization v1.3.1/go.mod h1:IvUSefKiwd1a5p0RgHDbWCIbDFgKuEdB+fPPuP0IDLI= +cloud.google.com/go/orchestration v1.6.0 h1:Vw+CEXo8M/FZ1rb4EjcLv0gJqqw89b7+g+C/EmniTb8= +cloud.google.com/go/orchestration v1.6.0/go.mod h1:M62Bevp7pkxStDfFfTuCOaXgaaqRAga1yKyoMtEoWPQ= +cloud.google.com/go/orgpolicy v1.10.0 h1:XDriMWug7sd0kYT1QKofRpRHzjad0bK8Q8uA9q+XrU4= +cloud.google.com/go/orgpolicy v1.10.0/go.mod h1:w1fo8b7rRqlXlIJbVhOMPrwVljyuW5mqssvBtU18ONc= +cloud.google.com/go/osconfig v1.11.0 h1:PkSQx4OHit5xz2bNyr11KGcaFccL5oqglFPdTboyqwQ= +cloud.google.com/go/osconfig v1.11.0/go.mod h1:aDICxrur2ogRd9zY5ytBLV89KEgT2MKB2L/n6x1ooPw= +cloud.google.com/go/oslogin v1.9.0 h1:whP7vhpmc+ufZa90eVpkfbgzJRK/Xomjz+XCD4aGwWw= +cloud.google.com/go/oslogin v1.9.0/go.mod h1:HNavntnH8nzrn8JCTT5fj18FuJLFJc4NaZJtBnQtKFs= +cloud.google.com/go/phishingprotection v0.7.0 h1:l6tDkT7qAEV49MNEJkEJTB6vOO/onbSOcNtAT09HPuA= +cloud.google.com/go/phishingprotection v0.7.0/go.mod h1:8qJI4QKHoda/sb/7/YmMQ2omRLSLYSu9bU0EKCNI+Lk= +cloud.google.com/go/policytroubleshooter v1.6.0 h1:yKAGC4p9O61ttZUswaq9GAn1SZnEzTd0vUYXD7ZBT7Y= +cloud.google.com/go/policytroubleshooter v1.6.0/go.mod h1:zYqaPTsmfvpjm5ULxAyD/lINQxJ0DDsnWOP/GZ7xzBc= +cloud.google.com/go/privatecatalog v0.8.0 h1:EPEJ1DpEGXLDnmc7mnCAqFmkwUJbIsaLAiLHVOkkwtc= +cloud.google.com/go/privatecatalog v0.8.0/go.mod h1:nQ6pfaegeDAq/Q5lrfCQzQLhubPiZhSaNhIgfJlnIXs= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/pubsub v1.30.0 h1:vCge8m7aUKBJYOgrZp7EsNDf6QMd2CAlXZqWTn3yq6s= +cloud.google.com/go/pubsub v1.30.0/go.mod h1:qWi1OPS0B+b5L+Sg6Gmc9zD1Y+HaM0MdUr7LsupY1P4= +cloud.google.com/go/pubsublite v1.7.0 h1:cb9fsrtpINtETHiJ3ECeaVzrfIVhcGjhhJEjybHXHao= +cloud.google.com/go/pubsublite v1.7.0/go.mod h1:8hVMwRXfDfvGm3fahVbtDbiLePT3gpoiJYJY+vxWxVM= +cloud.google.com/go/recaptchaenterprise v1.3.1/go.mod h1:OdD+q+y4XGeAlxRaMn1Y7/GveP6zmq76byL6tjPE7d4= +cloud.google.com/go/recaptchaenterprise/v2 v2.7.0 h1:6iOCujSNJ0YS7oNymI64hXsjGq60T4FK1zdLugxbzvU= +cloud.google.com/go/recaptchaenterprise/v2 v2.7.0/go.mod h1:19wVj/fs5RtYtynAPJdDTb69oW0vNHYDBTbB4NvMD9c= +cloud.google.com/go/recommendationengine v0.7.0 h1:VibRFCwWXrFebEWKHfZAt2kta6pS7Tlimsnms0fjv7k= +cloud.google.com/go/recommendationengine v0.7.0/go.mod h1:1reUcE3GIu6MeBz/h5xZJqNLuuVjNg1lmWMPyjatzac= +cloud.google.com/go/recommender v1.9.0 h1:ZnFRY5R6zOVk2IDS1Jbv5Bw+DExCI5rFumsTnMXiu/A= +cloud.google.com/go/recommender v1.9.0/go.mod h1:PnSsnZY7q+VL1uax2JWkt/UegHssxjUVVCrX52CuEmQ= +cloud.google.com/go/redis v1.11.0 h1:JoAd3SkeDt3rLFAAxEvw6wV4t+8y4ZzfZcZmddqphQ8= +cloud.google.com/go/redis v1.11.0/go.mod h1:/X6eicana+BWcUda5PpwZC48o37SiFVTFSs0fWAJ7uQ= +cloud.google.com/go/resourcemanager v1.7.0 h1:NRM0p+RJkaQF9Ee9JMnUV9BQ2QBIOq/v8M+Pbv/wmCs= +cloud.google.com/go/resourcemanager v1.7.0/go.mod h1:HlD3m6+bwhzj9XCouqmeiGuni95NTrExfhoSrkC/3EI= +cloud.google.com/go/resourcesettings v1.5.0 h1:8Dua37kQt27CCWHm4h/Q1XqCF6ByD7Ouu49xg95qJzI= +cloud.google.com/go/resourcesettings v1.5.0/go.mod h1:+xJF7QSG6undsQDfsCJyqWXyBwUoJLhetkRMDRnIoXA= +cloud.google.com/go/retail v1.12.0 h1:1Dda2OpFNzIb4qWgFZjYlpP7sxX3aLeypKG6A3H4Yys= +cloud.google.com/go/retail v1.12.0/go.mod h1:UMkelN/0Z8XvKymXFbD4EhFJlYKRx1FGhQkVPU5kF14= +cloud.google.com/go/run v0.9.0 h1:ydJQo+k+MShYnBfhaRHSZYeD/SQKZzZLAROyfpeD9zw= +cloud.google.com/go/run v0.9.0/go.mod h1:Wwu+/vvg8Y+JUApMwEDfVfhetv30hCG4ZwDR/IXl2Qg= +cloud.google.com/go/scheduler v1.9.0 h1:NpQAHtx3sulByTLe2dMwWmah8PWgeoieFPpJpArwFV0= +cloud.google.com/go/scheduler v1.9.0/go.mod h1:yexg5t+KSmqu+njTIh3b7oYPheFtBWGcbVUYF1GGMIc= +cloud.google.com/go/secretmanager v1.10.0 h1:pu03bha7ukxF8otyPKTFdDz+rr9sE3YauS5PliDXK60= +cloud.google.com/go/secretmanager v1.10.0/go.mod h1:MfnrdvKMPNra9aZtQFvBcvRU54hbPD8/HayQdlUgJpU= +cloud.google.com/go/security v1.13.0 h1:PYvDxopRQBfYAXKAuDpFCKBvDOWPWzp9k/H5nB3ud3o= +cloud.google.com/go/security v1.13.0/go.mod h1:Q1Nvxl1PAgmeW0y3HTt54JYIvUdtcpYKVfIB8AOMZ+0= +cloud.google.com/go/securitycenter v1.19.0 h1:AF3c2s3awNTMoBtMX3oCUoOMmGlYxGOeuXSYHNBkf14= +cloud.google.com/go/securitycenter v1.19.0/go.mod h1:LVLmSg8ZkkyaNy4u7HCIshAngSQ8EcIRREP3xBnyfag= +cloud.google.com/go/servicecontrol v1.11.1 h1:d0uV7Qegtfaa7Z2ClDzr9HJmnbJW7jn0WhZ7wOX6hLE= +cloud.google.com/go/servicecontrol v1.11.1/go.mod h1:aSnNNlwEFBY+PWGQ2DoM0JJ/QUXqV5/ZD9DOLB7SnUk= +cloud.google.com/go/servicedirectory v1.9.0 h1:SJwk0XX2e26o25ObYUORXx6torSFiYgsGkWSkZgkoSU= +cloud.google.com/go/servicedirectory v1.9.0/go.mod h1:29je5JjiygNYlmsGz8k6o+OZ8vd4f//bQLtvzkPPT/s= +cloud.google.com/go/servicemanagement v1.8.0 h1:fopAQI/IAzlxnVeiKn/8WiV6zKndjFkvi+gzu+NjywY= +cloud.google.com/go/servicemanagement v1.8.0/go.mod h1:MSS2TDlIEQD/fzsSGfCdJItQveu9NXnUniTrq/L8LK4= +cloud.google.com/go/serviceusage v1.6.0 h1:rXyq+0+RSIm3HFypctp7WoXxIA563rn206CfMWdqXX4= +cloud.google.com/go/serviceusage v1.6.0/go.mod h1:R5wwQcbOWsyuOfbP9tGdAnCAc6B9DRwPG1xtWMDeuPA= +cloud.google.com/go/shell v1.6.0 h1:wT0Uw7ib7+AgZST9eCDygwTJn4+bHMDtZo5fh7kGWDU= +cloud.google.com/go/shell v1.6.0/go.mod h1:oHO8QACS90luWgxP3N9iZVuEiSF84zNyLytb+qE2f9A= +cloud.google.com/go/spanner v1.45.0 h1:7VdjZ8zj4sHbDw55atp5dfY6kn1j9sam9DRNpPQhqR4= +cloud.google.com/go/spanner v1.45.0/go.mod h1:FIws5LowYz8YAE1J8fOS7DJup8ff7xJeetWEo5REA2M= +cloud.google.com/go/speech v1.15.0 h1:JEVoWGNnTF128kNty7T4aG4eqv2z86yiMJPT9Zjp+iw= +cloud.google.com/go/speech v1.15.0/go.mod h1:y6oH7GhqCaZANH7+Oe0BhgIogsNInLlz542tg3VqeYI= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0 h1:STgFzyU5/8miMl0//zKh2aQeTyeaUH3WN9bSUiJ09bA= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +cloud.google.com/go/storage v1.29.0/go.mod h1:4puEjyTKnku6gfKoTfNOU/W+a9JyuVNxjpS5GBrB8h4= +cloud.google.com/go/storagetransfer v1.8.0 h1:5T+PM+3ECU3EY2y9Brv0Sf3oka8pKmsCfpQ07+91G9o= +cloud.google.com/go/storagetransfer v1.8.0/go.mod h1:JpegsHHU1eXg7lMHkvf+KE5XDJ7EQu0GwNJbbVGanEw= +cloud.google.com/go/talent v1.5.0 h1:nI9sVZPjMKiO2q3Uu0KhTDVov3Xrlpt63fghP9XjyEM= +cloud.google.com/go/talent v1.5.0/go.mod h1:G+ODMj9bsasAEJkQSzO2uHQWXHHXUomArjWQQYkqK6c= +cloud.google.com/go/texttospeech v1.6.0 h1:H4g1ULStsbVtalbZGktyzXzw6jP26RjVGYx9RaYjBzc= +cloud.google.com/go/texttospeech v1.6.0/go.mod h1:YmwmFT8pj1aBblQOI3TfKmwibnsfvhIBzPXcW4EBovc= +cloud.google.com/go/tpu v1.5.0 h1:/34T6CbSi+kTv5E19Q9zbU/ix8IviInZpzwz3rsFE+A= +cloud.google.com/go/tpu v1.5.0/go.mod h1:8zVo1rYDFuW2l4yZVY0R0fb/v44xLh3llq7RuV61fPM= +cloud.google.com/go/trace v1.9.0 h1:olxC0QHC59zgJVALtgqfD9tGk0lfeCP5/AGXL3Px/no= +cloud.google.com/go/trace v1.9.0/go.mod h1:lOQqpE5IaWY0Ixg7/r2SjixMuc6lfTFeO4QGM4dQWOk= +cloud.google.com/go/translate v1.7.0 h1:GvLP4oQ4uPdChBmBaUSa/SaZxCdyWELtlAaKzpHsXdA= +cloud.google.com/go/translate v1.7.0/go.mod h1:lMGRudH1pu7I3n3PETiOB2507gf3HnfLV8qlkHZEyos= +cloud.google.com/go/video v1.15.0 h1:upIbnGI0ZgACm58HPjAeBMleW3sl5cT84AbYQ8PWOgM= +cloud.google.com/go/video v1.15.0/go.mod h1:SkgaXwT+lIIAKqWAJfktHT/RbgjSuY6DobxEp0C5yTQ= +cloud.google.com/go/videointelligence v1.10.0 h1:Uh5BdoET8XXqXX2uXIahGb+wTKbLkGH7s4GXR58RrG8= +cloud.google.com/go/videointelligence v1.10.0/go.mod h1:LHZngX1liVtUhZvi2uNS0VQuOzNi2TkY1OakiuoUOjU= +cloud.google.com/go/vision v1.2.0/go.mod h1:SmNwgObm5DpFBme2xpyOyasvBc1aPdjvMk2bBk0tKD0= +cloud.google.com/go/vision/v2 v2.7.0 h1:8C8RXUJoflCI4yVdqhTy9tRyygSHmp60aP363z23HKg= +cloud.google.com/go/vision/v2 v2.7.0/go.mod h1:H89VysHy21avemp6xcf9b9JvZHVehWbET0uT/bcuY/0= +cloud.google.com/go/vmmigration v1.6.0 h1:Azs5WKtfOC8pxvkyrDvt7J0/4DYBch0cVbuFfCCFt5k= +cloud.google.com/go/vmmigration v1.6.0/go.mod h1:bopQ/g4z+8qXzichC7GW1w2MjbErL54rk3/C843CjfY= +cloud.google.com/go/vmwareengine v0.3.0 h1:b0NBu7S294l0gmtrT0nOJneMYgZapr5x9tVWvgDoVEM= +cloud.google.com/go/vmwareengine v0.3.0/go.mod h1:wvoyMvNWdIzxMYSpH/R7y2h5h3WFkx6d+1TIsP39WGY= +cloud.google.com/go/vpcaccess v1.6.0 h1:FOe6CuiQD3BhHJWt7E8QlbBcaIzVRddupwJlp7eqmn4= +cloud.google.com/go/vpcaccess v1.6.0/go.mod h1:wX2ILaNhe7TlVa4vC5xce1bCnqE3AeH27RV31lnmZes= +cloud.google.com/go/webrisk v1.8.0 h1:IY+L2+UwxcVm2zayMAtBhZleecdIFLiC+QJMzgb0kT0= +cloud.google.com/go/webrisk v1.8.0/go.mod h1:oJPDuamzHXgUc+b8SiHRcVInZQuybnvEW72PqTc7sSg= +cloud.google.com/go/websecurityscanner v1.5.0 h1:AHC1xmaNMOZtNqxI9Rmm87IJEyPaRkOxeI0gpAacXGk= +cloud.google.com/go/websecurityscanner v1.5.0/go.mod h1:Y6xdCPy81yi0SQnDY1xdNTNpfY1oAgXUlcfN3B3eSng= +cloud.google.com/go/workflows v1.10.0 h1:FfGp9w0cYnaKZJhUOMqCOJCYT/WlvYBfTQhFWV3sRKI= +cloud.google.com/go/workflows v1.10.0/go.mod h1:fZ8LmRmZQWacon9UCX1r/g/DfAXx5VcPALq2CxzdePw= contrib.go.opencensus.io/exporter/jaeger v0.2.1 h1:yGBYzYMewVL0yO9qqJv3Z5+IRhPdU7e9o/2oKpX4YvI= contrib.go.opencensus.io/exporter/jaeger v0.2.1/go.mod h1:Y8IsLgdxqh1QxYxPC5IgXVmBaeLUeQFfBeBi9PbeZd0= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9 h1:VpgP7xuJadIUuKccphEpTJnWhS2jkQyMt6Y7pJCD7fY= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= +github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 h1:cTp8I5+VIoKjsnZuH8vjyaysT/ses3EvZeaV/1UkF2M= +github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.0 h1:8q4SaHjFsClSvuVne0ID/5Ka8u3fcIHyqkLjcFpNRHQ= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.0/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0/go.mod h1:OQeznEEkTZ9OrhHJoDD8ZDq51FHgXjqtP9z6bEwBq9U= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 h1:sXr+ck84g/ZlZUOZiNELInmMgOsuGwdjjVkEIde0OtY= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0/go.mod h1:okt5dMMTOFjX/aovMlrjvvXoPMBVSPzk9185BT0+eZM= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.2.0/go.mod h1:c+Lifp3EDEamAkPVzMooRNOK6CZjNSdEnf1A7jsI9u4= +github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.2.0 h1:gggzg0SUMs6SQbEw+3LoSsYf9YMjkupeAnHMX8O9mmY= +github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.2.0/go.mod h1:+6KLcKIVgxoBDMqMO/Nvy7bZ9a0nbU3I1DtFQK3YvB4= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0/go.mod h1:kgDmCTgBzIEPFElEF+FK0SdjAor06dRq2Go927dnQ6o= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +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/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ= +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/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= -github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= +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.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= -github.com/VictoriaMetrics/fastcache v1.12.2 h1:N0y9ASrJ0F6h0QaC3o6uJb3NIZ9VKLjCM7NQbSmF7WI= +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/go.mod h1:AmC+Nzz1+3G2eCPapF6UcsnkThDcMsQicp4xDukwJYI= +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/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= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc= 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/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= +github.com/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU= +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/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= +github.com/aws/aws-sdk-go-v2 v1.21.2/go.mod h1:ErQhvNuEMhJjweavOYhxVkn2RUx7kQXVATHrjKtxIpM= +github.com/aws/aws-sdk-go-v2/config v1.18.45 h1:Aka9bI7n8ysuwPeFdm77nfbyHCAKQ3z9ghB3S/38zes= +github.com/aws/aws-sdk-go-v2/config v1.18.45/go.mod h1:ZwDUgFnQgsazQTnWfeLWk5GjeqTQTL8lMkoE1UXzxdE= +github.com/aws/aws-sdk-go-v2/credentials v1.13.43 h1:LU8vo40zBlo3R7bAvBVy/ku4nxGEyZe9N8MqAeFTzF8= +github.com/aws/aws-sdk-go-v2/credentials v1.13.43/go.mod h1:zWJBz1Yf1ZtX5NGax9ZdNjhhI4rgjfgsyk6vTY1yfVg= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.13 h1:PIktER+hwIG286DqXyvVENjgLTAwGgoeriLDD5C+YlQ= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.13/go.mod h1:f/Ib/qYjhV2/qdsf79H3QP/eRE4AkVyEf6sk7XfZ1tg= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.43 h1:nFBQlGtkbPzp/NjZLuFxRqmT91rLJkgvsEQs68h962Y= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.43/go.mod h1:auo+PiyLl0n1l8A0e8RIeR8tOzYPfZZH/JNlrJ8igTQ= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.37 h1:JRVhO25+r3ar2mKGP7E0LDl8K9/G36gjlqca5iQbaqc= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.37/go.mod h1:Qe+2KtKml+FEsQF/DHmDV+xjtche/hwoF75EG4UlHW8= +github.com/aws/aws-sdk-go-v2/internal/ini v1.3.45 h1:hze8YsjSh8Wl1rYa1CJpRmXP21BvOBuc76YhW0HsuQ4= +github.com/aws/aws-sdk-go-v2/internal/ini v1.3.45/go.mod h1:lD5M20o09/LCuQ2mE62Mb/iSdSlCNuj6H5ci7tW7OsE= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.37 h1:WWZA/I2K4ptBS1kg0kV1JbBtG/umed0vwHRrmcr9z7k= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.37/go.mod h1:vBmDnwWXWxNPFRMmG2m/3MKOe+xEcMDo1tanpaWCcck= +github.com/aws/aws-sdk-go-v2/service/route53 v1.30.2 h1:/RPQNjh1sDIezpXaFIkZb7MlXnSyAqjVdAwcJuGYTqg= +github.com/aws/aws-sdk-go-v2/service/route53 v1.30.2/go.mod h1:TQZBt/WaQy+zTHoW++rnl8JBrmZ0VO6EUbVua1+foCA= +github.com/aws/aws-sdk-go-v2/service/sso v1.15.2 h1:JuPGc7IkOP4AaqcZSIcyqLpFSqBWK32rM9+a1g6u73k= +github.com/aws/aws-sdk-go-v2/service/sso v1.15.2/go.mod h1:gsL4keucRCgW+xA85ALBpRFfdSLH4kHOVSnLMSuBECo= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.17.3 h1:HFiiRkf1SdaAmV3/BHOFZ9DjFynPHj8G/UIO1lQS+fk= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.17.3/go.mod h1:a7bHA82fyUXOm+ZSWKU6PIoBxrjSprdLoM8xPYvzYVg= +github.com/aws/aws-sdk-go-v2/service/sts v1.23.2 h1:0BkLfgeDjfZnZ+MhB3ONb01u9pwFYTCZVhlsSSBvlbU= +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= github.com/bazelbuild/rules_go v0.23.2/go.mod h1:MC23Dc/wkXEyk3Wpq6lCqz0ZAYOZDw2DR5y3N1q2i7M= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= -github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +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/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bits-and-blooms/bitset v1.11.0 h1:RMyy2mBBShArUAhfVRZJ2xyBO58KCBCtZFShw3umo6k= +github.com/bits-and-blooms/bitset v1.7.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= 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= @@ -77,77 +420,163 @@ github.com/btcsuite/btcd v0.23.4 h1:IzV6qqkfwbItOS/sg/aDfPDsjPP8twrCOE2R93hxMlQ= github.com/btcsuite/btcd v0.23.4/go.mod h1:0QJIIN1wwIXF/3G/m87gIwGniDMDQqjVn4SZgnFpsYY= 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.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U= +github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU= 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= +github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d h1:yJzD/yFppdVCf6ApMkVy8cUxV0XrxdP9rVf6D87/Mng= github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= +github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd h1:R/opQEbFEy9JGkIguV40SvRY1uliPX8ifOvi6ICsFCw= github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= +github.com/btcsuite/goleveldb v1.0.0 h1:Tvd0BfvqX9o823q1j2UZ/epQo09eJh6dTcRp79ilIN4= github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I= github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= +github.com/btcsuite/snappy-go v1.0.0 h1:ZxaA6lo2EpxGddsA8JwWOcxlzRybb444sgmeJQMJGQE= github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= +github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 h1:R8vQdOQdZ9Y3SkEwmHoWBmX1DNXhXZqlTpq6s4tyJGc= github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= +github.com/btcsuite/winsvc v1.0.0 h1:J9B4L7e3oqhXOcm+2IuNApwzQec85lE+QaikUcCs+dk= github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= +github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= +github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/cp v1.1.1 h1:nCb6ZLdB7NRaqsm91JtQTAme2SKJzXVsdPIPkyJr1MU= +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/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= +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/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 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= +github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI= +github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk= +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= +github.com/cloudflare/cloudflare-go v0.79.0 h1:ErwCYDjFCYppDJlDJ/5WhsSmzegAUe2+K9qgFyQDg3M= +github.com/cloudflare/cloudflare-go v0.79.0/go.mod h1:gkHQf9xEubaQPEuerBuoinR9P8bf8a05Lq0X6WKy1Oc= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cockroachdb/errors v1.11.1 h1:xSEW75zKaKCWzR3OfxXUxgrk/NtT4G1MiOv5lWZazG8= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe h1:QQ3GSy+MqSHxm/d8nCtnAiZdYFd45cYZPs8vOOIYKfk= +github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +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/go.mod h1:8MUxA3Gi6b25tYlFEBGLf+D8aISL+M4MIpiWMSNRfxw= -github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= +github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= 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.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30= +github.com/cockroachdb/redact v1.0.8/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= -github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= +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/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= -github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ= +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/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= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf h1:iW4rZ826su+pqaw19uhpSCzhj44qo35pNgKFGqzDKkU= +github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk= +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-kzg-4844 v0.7.0 h1:C0vgZRk4q4EZ/JgPfzuSoxdCq3C3mOZMBShovmncxvA= +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/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= +github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/cskr/pubsub v1.0.2 h1:vlOzMhl6PFn60gRlTQQsIfVwaPB/B/8MziK8FhEPt/0= github.com/cskr/pubsub v1.0.2/go.mod h1:/8MzYXk/NJAz782G8RPkFzXTZVu63VotefPnR9TIRis= +github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= github.com/d4l3k/messagediff v1.2.1 h1:ZcAIMYsUg0EAp9X+tt8/enBE/Q8Yd5kzPynLyKptt9U= 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/v2 v2.5.0 h1:hn6cEZtQ0h3J8kFrHR/NrzyOoTnjgW1+FmNJzQ7y/sA= +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/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/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= +github.com/deepmap/oapi-codegen v1.6.0/go.mod h1:ryDa9AgbELGeB+YEXE1dR53yAjHwFvE9iAUlWl9Al3M= +github.com/deepmap/oapi-codegen v1.8.2 h1:SegyeYGcdi0jLLrpbCMoJxnUUn8GBXHsvr4rbzjuhfU= +github.com/deepmap/oapi-codegen v1.8.2/go.mod h1:YLgSKSDv/bZQB7N4ws6luhozi3cEdRktEqrX88CvjIw= +github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= +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= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/djherbis/atime v1.1.0/go.mod h1:28OF6Y8s3NQWwacXc5eZTsEsiMzp7LF8MbXE+XJPdBE= +github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= +github.com/dlclark/regexp2 v1.7.0 h1:7lJfhqlPssTb1WQx4yvTHN0uElPEv52sbaECrAQxjAo= +github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v23.0.3+incompatible h1:9GhVsShNWz1hO//9BNg/dpMnZW25KydO4wtVxWAIbho= @@ -156,28 +585,59 @@ github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKoh github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dop251/goja v0.0.0-20211022113120-dc8c55024d06/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk= +github.com/dop251/goja v0.0.0-20230806174421-c933cf95e127 h1:qwcF+vdFrvPSEUDSX5RVoRccG8a5DhOdWdQ4zN62zzo= +github.com/dop251/goja v0.0.0-20230806174421-c933cf95e127/go.mod h1:QMWlm50DNe14hD7t24KEqZuUdC9sOTy8W6XbCU1mlw4= +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/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= -github.com/emicklei/dot v1.6.2 h1:08GN+DD79cy/tzN6uLCT84+2Wk9u+wvqP+Hkx/dIR8A= +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/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s= +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= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= +github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= +github.com/envoyproxy/go-control-plane v0.11.1-0.20230524094728-9239064ad72f h1:7T++XKzy4xg7PKy+bM+Sa9/oe1OC88yz2hXQUISoXfA= +github.com/envoyproxy/go-control-plane v0.11.1-0.20230524094728-9239064ad72f/go.mod h1:sfYdkwUW4BA3PbKjySwjJy+O4Pu0h62rlqCMHNk+K+Q= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/ethereum/c-kzg-4844 v0.4.0 h1:3MS1s4JtA868KpJxroZoepdV0ZKBp3u/O5HcZ7R3nlY= +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/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= -github.com/ethereum/go-ethereum v1.13.5 h1:U6TCRciCqZRe4FPXmy1sMGxTfuk8P7u2UoinF3VbaFk= +github.com/ethereum/go-ethereum v1.10.26/go.mod h1:EYFyF19u3ezGLD4RqOkLq+ZCXzYbLoNDdZlMt7kyKFg= github.com/ethereum/go-ethereum v1.13.5/go.mod h1:yMTu38GSuyxaYzQMViqNmQ1s3cE84abZexQmTgenWk0= -github.com/fatih/color v1.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w= +github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8= +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/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= github.com/ferranbt/fastssz v0.0.0-20210905181407-59cf6761a7d5/go.mod h1:S8yiDeAXy8f88W4Ul+0dBMPx49S05byYbmZD6Uv94K4= -github.com/ferranbt/fastssz v0.1.4 h1:OCDB+dYDEQDvAgtAGnTSidK1Pe2tW3nFV40XyMkTeDY= +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/go.mod h1:Ea3+oeoRGGLGm5shYAeDgu6PGUlcvQhE2fILyD9+tGg= -github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 h1:FtmdgXiUlNeRsoNMFlKLDt+S+6hbjVMEW6RGQ7aUf7c= +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/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 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= @@ -188,34 +648,77 @@ 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/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 h1:f6D9Hr8xV8uYKlyuj8XIruxlh9WjVjdh1gIicAS7ays= +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= +github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61 h1:IZqZOB2fydHte3kUgxrzK5E1fW7RQGeDwE8F/ZZnUYc= +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/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= github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko= github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= github.com/gdamore/tcell/v2 v2.6.0 h1:OKbluoP9VYmJwZwq/iLb4BxwKcwGthaa1YNBJIyCySg= github.com/gdamore/tcell/v2 v2.6.0/go.mod h1:be9omFATkdr0D9qewWW3d+MEvl5dha+Etb5y65J2H8Y= -github.com/getsentry/sentry-go v0.25.0 h1:q6Eo+hS+yoJlTO3uu/azhQadsD8V+jQn2D8VvX1eOyI= +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/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/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/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E= +github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= +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= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4 h1:WtGNWLvXpe6ZudgnXrq0barxBImvnnJoMEhXAzcbM0I= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= +github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA= +github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +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= github.com/go-openapi/errors v0.21.0 h1:FhChC/duCnfoLj1gZ0BgaBmzhJC2SL/sJr8a2vAobSY= github.com/go-openapi/errors v0.21.0/go.mod h1:jxNTMUxRCKj65yb/okJGEtahVd7uvWnuWfj53bse4ho= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.20.2 h1:mQc3nmndL8ZBzStEo3JYF8wzmeWffDH4VbXz58sAx6Q= github.com/go-openapi/jsonpointer v0.20.2/go.mod h1:bHen+N0u1KEO3YlmqOjTT9Adn1RfD91Ar825/PuiRVs= github.com/go-openapi/jsonreference v0.20.4 h1:bKlDxQxQJgwpUSgOENiMPzCTBVuc7vTdXSSgNeAhojU= @@ -228,25 +731,62 @@ github.com/go-openapi/spec v0.20.14 h1:7CBlRnw+mtjFGlPDRZmAMnq35cRzI91xj03HVyUi/ github.com/go-openapi/spec v0.20.14/go.mod h1:8EOhTpBoFiask8rrgwbLC3zmJfz4zsCUueRuPM6GNkw= github.com/go-openapi/strfmt v0.22.0 h1:Ew9PnEYc246TwrEspvBdDHS4BVKXy/AOVsfqGDgAcaI= github.com/go-openapi/strfmt v0.22.0/go.mod h1:HzJ9kokGIju3/K6ap8jL+OlGAbjpSv27135Yr9OivU4= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.22.9 h1:XX2DssF+mQKM2DHsbgZK74y/zj4mo9I99+89xUmuZCE= github.com/go-openapi/swag v0.22.9/go.mod h1:3/OXnFfnMAwBD099SwYRk7GD3xOrr1iL7d/XNLXVVwE= github.com/go-openapi/validate v0.23.0 h1:2l7PJLzCis4YUGEoW6eoQw3WhyM65WSIcjX6SQnlfDw= github.com/go-openapi/validate v0.23.0/go.mod h1:EeiAZ5bmpSIOJV1WLfyYF9qp/B1ZgSaEpHTJHtN5cbE= -github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= +github.com/go-playground/validator/v10 v10.13.0 h1:cFRQdfaSMCOSfGCCLB20MHvuoHb/s5G8L5pu2ppK5AQ= +github.com/go-playground/validator/v10 v10.13.0/go.mod h1:dwu7+CG8/CtBiJFZDz4e+5Upb6OLw04gtBYw0mcG/z4= +github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= +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/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= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= +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/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= +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/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= -github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +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/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= +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/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= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE= github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -262,6 +802,8 @@ github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= +github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -284,10 +826,13 @@ 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= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/flatbuffers v2.0.8+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -300,16 +845,25 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= +github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= +github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0 h1:wCKgOCHuUEVfsaQLpPSJb7VdYCdTVZQAuOdYm1yc/60= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= @@ -322,47 +876,121 @@ github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20230207041349-798e818bf904/go.mod h1:uglQLonpP8qtYCYyzA+8c/9qtqgA3qsXGYqCPKARAFg= github.com/google/pprof v0.0.0-20240207164012-fb44976bdcd5 h1:E/LAvt58di64hlYjx7AsNS6C/ysHWYo+2qPCZKTQhRo= github.com/google/pprof v0.0.0-20240207164012-fb44976bdcd5/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= +github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= +github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 h1:FKHo8hFI3A+7w0aUQuYXQ+6EN5stWmeY/AZqtM8xk9k= +github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo= +github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/s2a-go v0.1.0/go.mod h1:OJpEgntRZo8ugHpF9hkoLJbS5dSI20XZeXJ9JVywLlM= +github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= +github.com/google/subcommands v1.2.0 h1:vWQspBTo2nEqTUFita5/KeEWlUL8kQObDFbub/EN9oE= github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 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/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= +github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/gax-go/v2 v2.7.1/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI= +github.com/googleapis/gax-go/v2 v2.8.0/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI= +github.com/googleapis/gax-go/v2 v2.10.0/go.mod h1:4UOEnMCrxsSqQ940WnTiD6qJ63le2ev3xfyagutxiPw= +github.com/googleapis/gax-go/v2 v2.11.0/go.mod h1:DxmR61SGKkGLa2xigwuZIQpkCI2S5iydzRfb3peWZJI= 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= +github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= +github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +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/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= +github.com/graph-gophers/graphql-go v1.3.0 h1:Eb9x/q6MFpCLz7jBCiP/WTxjSDrYLR1QY41SORZyNJ0= +github.com/graph-gophers/graphql-go v1.3.0/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= +github.com/grpc-ecosystem/go-grpc-middleware v1.2.2 h1:FlFbCRLd5Jr4iYXZufAvgWN6Ao0JrI5chLINnUXDDr0= +github.com/grpc-ecosystem/go-grpc-middleware v1.2.2/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/grpc-ecosystem/grpc-gateway/v2 v2.0.1 h1:X2vfSnm1WC8HEo0MBHZg2TcuDUHJj6kd1TmEAQncnSA= github.com/grpc-ecosystem/grpc-gateway/v2 v2.0.1/go.mod h1:oVMjMN64nzEcepv1kdZKgx1qNYt4Ro0Gqefiq2JWdis= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3 h1:lLT7ZLSzGLI08vc9cpd+tYmNWjdKDqyr/2L+f6U12Fk= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w= +github.com/guptarohit/asciigraph v0.5.5/go.mod h1:dYl5wwK4gNsnFf9Zp+l06rFiDZ5YtXM6x7SRWZ3KGag= +github.com/gxed/hashland/keccakpg v0.0.1 h1:wrk3uMNaMxbXiHibbPO4S0ymqJMm41WiudyFSs7UnsU= github.com/gxed/hashland/keccakpg v0.0.1/go.mod h1:kRzw3HkwxFU1mpmPP8v1WyQzwdGfmKFJ6tItnhQ67kU= +github.com/gxed/hashland/murmur3 v0.0.1 h1:SheiaIt0sda5K+8FLz952/1iWS9zrnKsEJaOJu4ZbSc= github.com/gxed/hashland/murmur3 v0.0.1/go.mod h1:KjXop02n4/ckmZSnY2+HKcLud/tcmvhST0bie/0lS48= -github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE= +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/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0= -github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= +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= +github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= +github.com/hashicorp/go-hclog v1.2.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +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/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= github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d h1:dg1dEPuWpEqDnvIw251EVy4zlP8gWbsGj4BsUKCRpYs= 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/uint256 v1.2.4 h1:jUc4Nk8fm9jZabQuqr2JzednajVmBpC+oiTiXZJEApU= +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/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= +github.com/ianlancetaylor/cgosymbolizer v0.0.0-20200424224625-be1b05b0b279/go.mod h1:a5aratAVTWyz+nJMmDsN8O4XTfaLfdAsB1ysCmZX5Bw= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639 h1:mV02weKRL81bEnm8A0HT1/CAelMQDBuQIfLw8n+d6xI= 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/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= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/influxdata/influxdb v1.8.3/go.mod h1:JugdFhsvvI8gadxOI6noqNeeBHvWNTbfYGtiAn+2jhI= +github.com/influxdata/influxdb-client-go/v2 v2.4.0 h1:HGBfZYStlx3Kqvsv1h2pJixbCl/jhnFtxpKFAv9Tu5k= +github.com/influxdata/influxdb-client-go/v2 v2.4.0/go.mod h1:vLNHdxTJkIf2mSLvGrpj8TCcISApPoXkaxP8g9uRlW8= +github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c h1:qSHzRbhzK8RdXOsAdfDgO49TtqC1oZ+acxPrkfTxcCs= +github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= +github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo= +github.com/influxdata/line-protocol v0.0.0-20210311194329-9aa0e372d097 h1:vilfsDSy7TDxedi9gyBkMvAirat/oRcL0lFdJBf6tdM= +github.com/influxdata/line-protocol v0.0.0-20210311194329-9aa0e372d097/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo= github.com/ipfs/bbloom v0.0.4 h1:Gi+8EGJ2y5qiD5FbsbpX/TMNcJw8gSqr7eyjHa4Fhvs= github.com/ipfs/bbloom v0.0.4/go.mod h1:cS9YprKXpoZ9lT0n/Mw/a6/aFV6DTjTLYHeA+gyqMG0= github.com/ipfs/boxo v0.8.0 h1:UdjAJmHzQHo/j3g3b1bAcAXCj/GM6iTwvSlBDvPBNBs= @@ -381,16 +1009,30 @@ github.com/ipfs/go-cid v0.0.6/go.mod h1:6Ux9z5e+HpkQdckYoX1PG/6xqKspzlEIR5SDmgqg github.com/ipfs/go-cid v0.0.7/go.mod h1:6Ux9z5e+HpkQdckYoX1PG/6xqKspzlEIR5SDmgqgC/I= github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s= github.com/ipfs/go-cid v0.4.1/go.mod h1:uQHwDeX4c6CtyrFwdqyhpNcxVewur1M7l7fNU7LKwZk= +github.com/ipfs/go-cidutil v0.1.0 h1:RW5hO7Vcf16dplUU60Hs0AKDkQAVPVplr7lk97CFL+Q= +github.com/ipfs/go-cidutil v0.1.0/go.mod h1:e7OEVBMIv9JaOxt9zaGEmAoSlXW9jdFZ5lP/0PwcfpA= github.com/ipfs/go-datastore v0.6.0 h1:JKyz+Gvz1QEZw0LsX1IBn+JFCJQH4SJVFtM4uWU0Myk= github.com/ipfs/go-datastore v0.6.0/go.mod h1:rt5M3nNbSO/8q1t4LNkLyUwRs8HupMeN/8O4Vn9YAT8= github.com/ipfs/go-detect-race v0.0.1 h1:qX/xay2W3E4Q1U7d9lNs1sU9nvguX0a7319XbyQ6cOk= github.com/ipfs/go-detect-race v0.0.1/go.mod h1:8BNT7shDZPo99Q74BpGMK+4D8Mn4j46UU0LZ723meps= +github.com/ipfs/go-ds-badger v0.3.0 h1:xREL3V0EH9S219kFFueOYJJTcjgNSZ2HY1iSvN7U1Ro= +github.com/ipfs/go-ds-badger v0.3.0/go.mod h1:1ke6mXNqeV8K3y5Ak2bAA0osoTfmxUdupVCGm4QUIek= +github.com/ipfs/go-ds-leveldb v0.5.0 h1:s++MEBbD3ZKc9/8/njrn4flZLnCuY9I79v94gBUNumo= +github.com/ipfs/go-ds-leveldb v0.5.0/go.mod h1:d3XG9RUDzQ6V4SHi8+Xgj9j1XuEk1z82lquxrVbml/Q= +github.com/ipfs/go-ipfs-blockstore v1.3.0 h1:m2EXaWgwTzAfsmt5UdJ7Is6l4gJcaM/A12XwJyvYvMM= +github.com/ipfs/go-ipfs-blockstore v1.3.0/go.mod h1:KgtZyc9fq+P2xJUiCAzbRdhhqJHvsw8u2Dlqy2MyRTE= github.com/ipfs/go-ipfs-blocksutil v0.0.1 h1:Eh/H4pc1hsvhzsQoMEP3Bke/aW5P5rVM1IWFJMcGIPQ= github.com/ipfs/go-ipfs-blocksutil v0.0.1/go.mod h1:Yq4M86uIOmxmGPUHv/uI7uKqZNtLb449gwKqXjIsnRk= +github.com/ipfs/go-ipfs-chunker v0.0.5 h1:ojCf7HV/m+uS2vhUGWcogIIxiO5ubl5O57Q7NapWLY8= +github.com/ipfs/go-ipfs-chunker v0.0.5/go.mod h1:jhgdF8vxRHycr00k13FM8Y0E+6BoalYeobXmUyTreP8= github.com/ipfs/go-ipfs-delay v0.0.1 h1:r/UXYyRcddO6thwOnhiznIAiSvxMECGgtv35Xs1IeRQ= github.com/ipfs/go-ipfs-delay v0.0.1/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw= +github.com/ipfs/go-ipfs-ds-help v1.1.0 h1:yLE2w9RAsl31LtfMt91tRZcrx+e61O5mDxFRR994w4Q= +github.com/ipfs/go-ipfs-ds-help v1.1.0/go.mod h1:YR5+6EaebOhfcqVCyqemItCLthrpVNot+rsOU/5IatU= github.com/ipfs/go-ipfs-pq v0.0.3 h1:YpoHVJB+jzK15mr/xsWC574tyDLkezVrDNeaalQBsTE= github.com/ipfs/go-ipfs-pq v0.0.3/go.mod h1:btNw5hsHBpRcSSgZtiNm/SLj5gYIZ18AKtv3kERkRb4= +github.com/ipfs/go-ipfs-redirects-file v0.1.1 h1:Io++k0Vf/wK+tfnhEh63Yte1oQK5VGT2hIEYpD0Rzx8= +github.com/ipfs/go-ipfs-redirects-file v0.1.1/go.mod h1:tAwRjCV0RjLTjH8DR/AU7VYvfQECg+lpUy2Mdzv7gyk= github.com/ipfs/go-ipfs-util v0.0.1/go.mod h1:spsl5z8KUnrve+73pOhSVZND1SIxPW5RyBCNzQxlJBc= github.com/ipfs/go-ipfs-util v0.0.2 h1:59Sswnk1MFaiq+VcaknX7aYEyGyGDAA73ilhEK2POp8= github.com/ipfs/go-ipfs-util v0.0.2/go.mod h1:CbPtkWJzjLdEcezDns2XYaehFVNXG9zrdrtMecczcsQ= @@ -402,6 +1044,8 @@ github.com/ipfs/go-ipld-format v0.4.0 h1:yqJSaJftjmjc9jEOFYlpkwOLVKv68OD27jFLlSg github.com/ipfs/go-ipld-format v0.4.0/go.mod h1:co/SdBE8h99968X0hViiw1MNlh6fvxxnHpvVLnH7jSM= github.com/ipfs/go-ipld-legacy v0.1.1 h1:BvD8PEuqwBHLTKqlGFTHSwrwFOMkVESEvwIYwR2cdcc= github.com/ipfs/go-ipld-legacy v0.1.1/go.mod h1:8AyKFCjgRPsQFf15ZQgDB8Din4DML/fOmKZkkFkrIEg= +github.com/ipfs/go-ipns v0.3.0 h1:ai791nTgVo+zTuq2bLvEGmWP1M0A6kGTXUsgv/Yq67A= +github.com/ipfs/go-ipns v0.3.0/go.mod h1:3cLT2rbvgPZGkHJoPO1YMJeh6LtkxopCkKFcio/wE24= github.com/ipfs/go-log v1.0.5 h1:2dOuUCB1Z7uoczMWgAyDck5JLb72zHzrMnGnCNNbvY8= github.com/ipfs/go-log v1.0.5/go.mod h1:j0b8ZoR+7+R99LD9jZ6+AJsrzkPbSXbZfGakb5JPtIo= github.com/ipfs/go-log/v2 v2.1.3/go.mod h1:/8d0SH3Su5Ooc31QlL1WysJhvyOTDCjcCZ9Axpmri6g= @@ -411,118 +1055,297 @@ github.com/ipfs/go-metrics-interface v0.0.1 h1:j+cpbjYvu4R8zbleSs36gvB7jR+wsL2fG github.com/ipfs/go-metrics-interface v0.0.1/go.mod h1:6s6euYU4zowdslK0GKHmqaIZ3j/b/tL7HTWtJ4VPgWY= github.com/ipfs/go-peertaskqueue v0.8.1 h1:YhxAs1+wxb5jk7RvS0LHdyiILpNmRIRnZVztekOF0pg= github.com/ipfs/go-peertaskqueue v0.8.1/go.mod h1:Oxxd3eaK279FxeydSPPVGHzbwVeHjatZ2GA8XD+KbPU= +github.com/ipfs/go-unixfs v0.4.5 h1:wj8JhxvV1G6CD7swACwSKYa+NgtdWC1RUit+gFnymDU= +github.com/ipfs/go-unixfs v0.4.5/go.mod h1:BIznJNvt/gEx/ooRMI4Us9K8+qeGO7vx1ohnbk8gjFg= +github.com/ipfs/go-unixfsnode v1.6.0 h1:JOSA02yaLylRNi2rlB4ldPr5VcZhcnaIVj5zNLcOjDo= +github.com/ipfs/go-unixfsnode v1.6.0/go.mod h1:PVfoyZkX1B34qzT3vJO4nsLUpRCyhnMuHBznRcXirlk= +github.com/ipld/go-car/v2 v2.9.1-0.20230325062757-fff0e4397a3d h1:22g+x1tgWSXK34i25qjs+afr7basaneEkHaglBshd2g= +github.com/ipld/go-car/v2 v2.9.1-0.20230325062757-fff0e4397a3d/go.mod h1:SH2pi/NgfGBsV/CGBAQPxMfghIgwzbh5lQ2N+6dNRI8= github.com/ipld/go-codec-dagpb v1.6.0 h1:9nYazfyu9B1p3NAgfVdpRco3Fs2nFC72DqVsMj6rOcc= github.com/ipld/go-codec-dagpb v1.6.0/go.mod h1:ANzFhfP2uMJxRBr8CE+WQWs5UsNa0pYtmKZ+agnUw9s= github.com/ipld/go-ipld-prime v0.9.1-0.20210324083106-dc342a9917db/go.mod h1:KvBLMr4PX1gWptgkzRjVZCrLmSGcZCb/jioOQwCqZN8= github.com/ipld/go-ipld-prime v0.20.0 h1:Ud3VwE9ClxpO2LkCYP7vWPc0Fo+dYdYzgxUJZ3uRG4g= github.com/ipld/go-ipld-prime v0.20.0/go.mod h1:PzqZ/ZR981eKbgdr3y2DJYeD/8bgMawdGVlJDE8kK+M= -github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= +github.com/ipld/go-ipld-prime/storage/bsadapter v0.0.0-20230102063945-1a409dc236dd h1:gMlw/MhNr2Wtp5RwGdsW23cs+yCuj9k2ON7i9MiJlRo= +github.com/ipld/go-ipld-prime/storage/bsadapter v0.0.0-20230102063945-1a409dc236dd/go.mod h1:wZ8hH8UxeryOs4kJEJaiui/s00hDSbE37OKsL47g+Sw= +github.com/iris-contrib/blackfriday v2.0.0+incompatible/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI= +github.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod h1:iz2lgM/1UnEf1kP0L/+fafWORmlnuysV2EMP8MW+qe0= +github.com/iris-contrib/httpexpect/v2 v2.12.1/go.mod h1:7+RB6W5oNClX7PTwJgJnsQP3ZuUUYB3u61KCqeSgZ88= +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/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/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= github.com/jbenet/goprocess v0.1.4 h1:DRGOFReOMqqDNXwW70QkacFW0YN9QnwLV0Vqk+3oU0o= github.com/jbenet/goprocess v0.1.4/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4= +github.com/jedib0t/go-pretty/v6 v6.5.4 h1:gOGo0613MoqUcf0xCj+h/V3sHDaZasfv152G6/5l91s= +github.com/jedib0t/go-pretty/v6 v6.5.4/go.mod h1:5LQIxa52oJ/DlDSLv0HEkWOFMDGoWkJb9ss5KqPpJBg= +github.com/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267 h1:TMtDYDHKYY15rFihtRfck/bfFqNfvcabqvXAFQfAUpY= +github.com/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267/go.mod h1:h1nSAbGFqGVzn6Jyl1R/iCcBUHN4g+gW1u9CoBTrb9E= 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 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= +github.com/joonix/log v0.0.0-20200409080653-9c1d2ceb5f1d h1:k+SfYbN66Ev/GDVq39wYOXVW5RNd5kzzairbCe9dK5Q= +github.com/joonix/log v0.0.0-20200409080653-9c1d2ceb5f1d/go.mod h1:fS54ONkjDV71zS9CDx3V9K21gJg7byKSvI4ajuWFNJw= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +github.com/jrick/logrotate v1.0.0 h1:lQ1bL/n9mBNeIXoTUoYRlK4dHuNJVofX9oWqBtPnSzI= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a h1:FaWFmfWdAUKbSCtOU2QjDaorUexogfaMgbipgYATUMU= +github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a/go.mod h1:UJSiEoRfvx3hP73CvoARgeLjaIOjybY9vj8PUPPFGeU= +github.com/juju/errors v0.0.0-20181118221551-089d3ea4e4d5/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q= +github.com/juju/loggo v0.0.0-20180524022052-584905176618/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U= +github.com/juju/testing v0.0.0-20180920084828-472a3e8b2073/go.mod h1:63prj8cnj0tU0S9OHjGJn+b1h0ZghCndfnbQolrYTwA= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= +github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 h1:qGQQKEcAR99REcMpsXCp3lJ03zYT1PkRd3kQGPn9GVg= +github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw= +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 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= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +github.com/kisielk/errcheck v1.5.0 h1:e8esj/e4R+SAOwFwN+n3zr0nYeCyeweozKfO23MvHzY= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23 h1:FOOIBWrEkLgmlgGfMuZT83xIwfPDxEI2OHu6xUmJMFE= github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= +github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE= +github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/compress v1.9.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4= +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/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= github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +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/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= github.com/koron/go-ssdp v0.0.4/go.mod h1:oDXq+E5IL5q0U8uSBcoAXzTzInwy5lEgC91HoKtbmZk= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= 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.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= 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/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= +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/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= +github.com/leodido/go-urn v1.2.3/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= github.com/libp2p/go-buffer-pool v0.0.2/go.mod h1:MvaB6xw5vOrDl8rYZGLFdKAuk/hRoRZd1Vi32+RXyFM= github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= +github.com/libp2p/go-cidranger v1.1.0 h1:ewPN8EZ0dd1LSnrtuwd4709PXVcITVeuwbag38yPW7c= +github.com/libp2p/go-cidranger v1.1.0/go.mod h1:KWZTfSr+r9qEo9OkI9/SIEeAtw+NNoU0dXIXt15Okic= +github.com/libp2p/go-doh-resolver v0.4.0 h1:gUBa1f1XsPwtpE1du0O+nnZCUqtG7oYi7Bb+0S7FQqw= +github.com/libp2p/go-doh-resolver v0.4.0/go.mod h1:v1/jwsFusgsWIGX/c6vCRrnJ60x7bhTiq/fs2qt0cAg= +github.com/libp2p/go-flow-metrics v0.1.0 h1:0iPhMI8PskQwzh57jB9WxIuIOQ0r+15PChFGkx3Q3WM= +github.com/libp2p/go-flow-metrics v0.1.0/go.mod h1:4Xi8MX8wj5aWNDAZttg6UPmc0ZrnFNsMtpsYUClFtro= github.com/libp2p/go-libp2p v0.33.1 h1:tvJl9b9M6nSLBtZSXSguq+/lRhRj2oLRkyhBmQNMFLA= github.com/libp2p/go-libp2p v0.33.1/go.mod h1:zOUTMjG4I7TXwMndNyOBn/CNtVBLlvBlnxfi+8xzx+E= github.com/libp2p/go-libp2p-asn-util v0.4.1 h1:xqL7++IKD9TBFMgnLPZR6/6iYhawHKHl950SO9L6n94= github.com/libp2p/go-libp2p-asn-util v0.4.1/go.mod h1:d/NI6XZ9qxw67b4e+NgpQexCIiFYJjErASrYW4PFDN8= +github.com/libp2p/go-libp2p-kad-dht v0.21.1 h1:xpfp8/t9+X2ip1l8Umap1/UGNnJ3RHJgKGAEsnRAlTo= +github.com/libp2p/go-libp2p-kad-dht v0.21.1/go.mod h1:Oy8wvbdjpB70eS5AaFaI68tOtrdo3KylTvXDjikxqFo= +github.com/libp2p/go-libp2p-kbucket v0.5.0 h1:g/7tVm8ACHDxH29BGrpsQlnNeu+6OF1A9bno/4/U1oA= +github.com/libp2p/go-libp2p-kbucket v0.5.0/go.mod h1:zGzGCpQd78b5BNTDGHNDLaTt9aDK/A02xeZp9QeFC4U= +github.com/libp2p/go-libp2p-mplex v0.9.0 h1:R58pDRAmuBXkYugbSSXR9wrTX3+1pFM1xP2bLuodIq8= +github.com/libp2p/go-libp2p-mplex v0.9.0/go.mod h1:ro1i4kuwiFT+uMPbIDIFkcLs1KRbNp0QwnUXM+P64Og= +github.com/libp2p/go-libp2p-pubsub v0.10.0 h1:wS0S5FlISavMaAbxyQn3dxMOe2eegMfswM471RuHJwA= +github.com/libp2p/go-libp2p-pubsub v0.10.0/go.mod h1:1OxbaT/pFRO5h+Dpze8hdHQ63R0ke55XTs6b6NwLLkw= github.com/libp2p/go-libp2p-record v0.2.0 h1:oiNUOCWno2BFuxt3my4i1frNrt7PerzB3queqa1NkQ0= github.com/libp2p/go-libp2p-record v0.2.0/go.mod h1:I+3zMkvvg5m2OcSdoL0KPljyJyvNDFGKX7QdlpYUcwk= +github.com/libp2p/go-libp2p-routing-helpers v0.4.0 h1:b7y4aixQ7AwbqYfcOQ6wTw8DQvuRZeTAA0Od3YYN5yc= +github.com/libp2p/go-libp2p-routing-helpers v0.4.0/go.mod h1:dYEAgkVhqho3/YKxfOEGdFMIcWfAFNlZX8iAIihYA2E= github.com/libp2p/go-libp2p-testing v0.12.0 h1:EPvBb4kKMWO29qP4mZGyhVzUyR25dvfUIK5WDu6iPUA= github.com/libp2p/go-libp2p-testing v0.12.0/go.mod h1:KcGDRXyN7sQCllucn1cOOS+Dmm7ujhfEyXQL5lvkcPg= +github.com/libp2p/go-mplex v0.7.0 h1:BDhFZdlk5tbr0oyFq/xv/NPGfjbnrsDam1EvutpBDbY= +github.com/libp2p/go-mplex v0.7.0/go.mod h1:rW8ThnRcYWft/Jb2jeORBmPd6xuG3dGxWN/W168L9EU= github.com/libp2p/go-msgio v0.3.0 h1:mf3Z8B1xcFN314sWX+2vOTShIE0Mmn2TXn3YCUQGNj0= github.com/libp2p/go-msgio v0.3.0/go.mod h1:nyRM819GmVaF9LX3l03RMh10QdOroF++NBbxAb0mmDM= github.com/libp2p/go-nat v0.2.0 h1:Tyz+bUFAYqGyJ/ppPPymMGbIgNRH+WqC5QrT5fKrrGk= github.com/libp2p/go-nat v0.2.0/go.mod h1:3MJr+GRpRkyT65EpVPBstXLvOlAPzUVlG6Pwg9ohLJk= github.com/libp2p/go-netroute v0.2.1 h1:V8kVrpD8GK0Riv15/7VN6RbUQ3URNZVosw7H2v9tksU= github.com/libp2p/go-netroute v0.2.1/go.mod h1:hraioZr0fhBjG0ZRXJJ6Zj2IVEVNx6tDTFQfSmcq7mQ= +github.com/libp2p/go-reuseport v0.4.0 h1:nR5KU7hD0WxXCJbmw7r2rhRYruNRl2koHw8fQscQm2s= +github.com/libp2p/go-reuseport v0.4.0/go.mod h1:ZtI03j/wO5hZVDFo2jKywN6bYKWLOy8Se6DrI2E1cLU= +github.com/libp2p/go-yamux/v4 v4.0.1 h1:FfDR4S1wj6Bw2Pqbc8Uz7pCxeRBPbwsBbEdfwiCypkQ= +github.com/libp2p/go-yamux/v4 v4.0.1/go.mod h1:NWjl8ZTLOGlozrXSOZ/HlfG++39iKNnM5wwmtQP1YB4= +github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8= +github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= +github.com/lunixbochs/vtclean v1.0.0 h1:xu2sLAri4lGiovBDQKxl5mrXyESr3gUr5m5SM5+LVb8= +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= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/manifoldco/promptui v0.7.0 h1:3l11YT8tm9MnwGFQ4kETwkzpAwY2Jt9lCrumCUW4+z4= +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.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.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/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.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.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 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.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +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= +github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg= +github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k= +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/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= +github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b h1:z78hV3sbSMAUoyUMM0I83AUIT6Hu17AWfgjzIbtrYFc= +github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b/go.mod h1:lxPUiZwKoFL8DUUmalo2yJJUCxbPKtm8OKfqr2/FTNU= +github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc h1:PTfri+PuQmWDqERdnNMiD9ZejrlswWrCpBEZgWOiTrc= +github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc/go.mod h1:cGKTAVKx4SxOuR/czcZ/E2RSJ3sfHs8FpHhQ5CWMf9s= +github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY= +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/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g= +github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE= 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/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +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/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= github.com/moby/term v0.0.0-20221205130635-1aeaba878587 h1:HfkjXDfhgVaN5rmueG8cL8KKeFNecRCXFhaJ2qZ5SKA= github.com/moby/term v0.0.0-20221205130635-1aeaba878587/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= +github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6fbMZ9s0scYfZQ84/6SPL6zC8ACM2oIL0= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/mr-tron/base58 v1.1.3/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= @@ -559,20 +1382,36 @@ 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/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= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/naoina/go-stringutil v0.1.0 h1:rCUeRUHjBjGTSHl0VC00jUPLz8/F9dDzYI70Hzifhks= +github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0= +github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416 h1:shk/vn9oCoOTmwcouEdwIeOtOGA/ELRUw/GwvxwfT+0= +github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E= +github.com/nats-io/jwt/v2 v2.3.0/go.mod h1:0tqz9Hlu6bCBFLWAASKhE5vUA4c24L9KPUUgvwumE/k= +github.com/nats-io/nats.go v1.8.1/go.mod h1:BrFz9vVn0fU3AcH9Vn4Kd7W0NpJ651tD5omQ3M8LwxM= +github.com/nats-io/nats.go v1.23.0/go.mod h1:ki/Scsa23edbh8IRZbCuNXR9TDcbvfaSijKtaqQgw+Q= +github.com/nats-io/nkeys v0.0.2/go.mod h1:dab7URMsZm6Z/jp9Z5UGa87Uutgc2mVpXLC4B7TDb/4= +github.com/nats-io/nkeys v0.3.0/go.mod h1:gvUNGjVcM2IPr5rCsRsC6Wb3Hr2CQAm08dsxtV6A5y4= +github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +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/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= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +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= github.com/onsi/ginkgo/v2 v2.15.0 h1:79HwNRBAZHOEwrczrgSOPy+eFTTlIGELKy5as+ClttY= github.com/onsi/ginkgo/v2 v2.15.0/go.mod h1:HlxMHtYF57y6Dpf+mc5529KKmSq9h2FpCF+/ZkwUxKM= github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= @@ -581,20 +1420,45 @@ 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/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= github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM= github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/runtime-spec v1.2.0 h1:z97+pHb3uELt/yiAWD691HNHQIF07bE7dzrbT927iTk= +github.com/opencontainers/runtime-spec v1.2.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +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= +github.com/paulbellamy/ratecounter v0.2.0 h1:2L/RhJq+HA8gBQImDXtLPrDXK5qAj6ozWVK/zFXVJGs= +github.com/paulbellamy/ratecounter v0.2.0/go.mod h1:Hfx1hDpSGoqxkVVpBi/IlYD7kChlfo5C6hzIHwPqfFE= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y= +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= +github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0= +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= @@ -602,73 +1466,126 @@ github.com/polydawn/refmt v0.89.0 h1:ADJTApkvkeBZsN0tBTx8QjpD9JkmxbKp0cxfr9qszm4 github.com/polydawn/refmt v0.89.0/go.mod h1:/zvteZs/GwLtCgZ4BL6CBsk9IKIlexP43ObX9AxTqTw= 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/go.mod h1:+o04FW4GNL2vPr/35yxMV/8eXjhsdNBBPMVVDOOTLec= +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= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_golang v1.12.0/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= 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/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= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.1-0.20210607210712-147c58e9608a/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= +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/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= +github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= +github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= +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/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= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +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/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= +github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/protolambda/bls12-381-util v0.0.0-20220416220906-d8552aa452c7 h1:cZC+usqsYgHtlBaGulVnZ1hfKAi8iWtujBnRLQE698c= +github.com/protolambda/bls12-381-util v0.0.0-20220416220906-d8552aa452c7/go.mod h1:IToEjHuttnUzwZI5KBSM/LOOW3qLbbrHOEfp3SbECGY= +github.com/protolambda/zssz v0.1.5 h1:7fjJjissZIIaa2QcvmhS/pZISMX21zVITt49sW1ouek= +github.com/protolambda/zssz v0.1.5/go.mod h1:a4iwOX5FE7/JkKA+J/PH0Mjo9oXftN6P8NZyL28gpag= github.com/prysmaticlabs/fastssz v0.0.0-20221107182844-78142813af44 h1:c3p3UzV4vFA7xaCDphnDWOjpxcadrQ26l5b+ypsvyxo= github.com/prysmaticlabs/fastssz v0.0.0-20221107182844-78142813af44/go.mod h1:MA5zShstUwCQaE9faGHgCGvEWUbG87p4SAXINhmCkvg= +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/gohashtree v0.0.4-beta h1:H/EbCuXPeTV3lpKeXGPpEV9gsUpkqOOVnWapUyeWro4= +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/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/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= github.com/prysmaticlabs/protoc-gen-go-cast v0.0.0-20230228205207-28762a7b9294 h1:q9wE0ZZRdTUAAeyFP/w0SwBEnCqlVy2+on6X2/e+eAU= github.com/prysmaticlabs/protoc-gen-go-cast v0.0.0-20230228205207-28762a7b9294/go.mod h1:ZVEbRdnMkGhp/pu35zq4SXxtvUwWK0J1MATtekZpH2Y= github.com/prysmaticlabs/prysm/v5 v5.0.3 h1:hUi0gu6v7aXmMQkl2GbrLoWcMhDNIbkVxRwrZchKbxU= github.com/prysmaticlabs/prysm/v5 v5.0.3/go.mod h1:v5Oz4A4cWljfxUmW7SDk/VBzoYnei+lzwJogvSqUZVs= github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= +github.com/quic-go/qtls-go1-19 v0.2.1 h1:aJcKNMkH5ASEJB9FXNeZCyTEIHU1J7MmHyz1Q1TSG1A= +github.com/quic-go/qtls-go1-19 v0.2.1/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI= +github.com/quic-go/qtls-go1-20 v0.1.1 h1:KbChDlg82d3IHqaj2bn6GfKRj84Per2VGf5XV3wSwQk= +github.com/quic-go/qtls-go1-20 v0.1.1/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM= github.com/quic-go/quic-go v0.42.0 h1:uSfdap0eveIl8KXnipv9K7nlwZ5IqLlYOpJ58u5utpM= github.com/quic-go/quic-go v0.42.0/go.mod h1:132kz4kL3F9vxhW3CtQJLDVwcFe5wdWeJXXijhsO57M= github.com/quic-go/webtransport-go v0.6.0 h1:CvNsKqc4W2HljHJnoT+rMmbRJybShZ0YPFDD3NxaZLY= github.com/quic-go/webtransport-go v0.6.0/go.mod h1:9KjU4AEBqEQidGHNDkZrb8CAa1abRaosM2yGOyiikEc= +github.com/raulk/go-watchdog v1.3.0 h1:oUmdlHxdkXRJlwfG0O9omj8ukerm8MEQavSiDTEtBsk= +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.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.20250513050821-a194ed87d86d h1:+k/7TBnnqNdJ08FU7HQMuOIxt9uGRqL/kq+S1NdLCAA= -github.com/rocket-pool/rocketpool-go v1.8.4-0.20250513050821-a194ed87d86d/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.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= -github.com/rs/cors v1.8.2 h1:KCooALfAYGs415Cwu5ABvv9n9509fSiG5SQJn/AQo4U= github.com/rs/cors v1.8.2/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= 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/samber/lo v1.36.0/go.mod h1:HLeWcJRRyLKp3+/XBJvOrerCQn9mhdKMHyd7IRlgeQ8= +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/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= 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.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/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -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 h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N3yZFZkDFs= 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 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hgR6gDIPg= 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/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA= +github.com/spf13/afero v1.10.0/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= +github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM= +github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= @@ -678,35 +1595,39 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbeoEm4= +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/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 h1:vfofYNRScrDdvS342BElfbETmL1Aiz3i2t0zfRj16Hs= github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= +github.com/tdewolff/minify/v2 v2.12.4/go.mod h1:h+SRvSIX3kwgwTFOpSckvSxgax3uy8kZTSF1Ojrr3bk= +github.com/tdewolff/parse/v2 v2.6.4/go.mod h1:woz0cgbLwFdtbjJu8PIKxhW05KplTFQkOdX78o+Jgrs= 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.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI= -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.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4= -github.com/tklauser/numcpus v0.7.0 h1:yjuerZP127QG9m5Zh/mSO4wqurYil27tHrqwRoRjpr4= github.com/tklauser/numcpus v0.7.0/go.mod h1:bb6dMVcj8A42tSE7i32fsIUCbQNllK5iDguyOZRUzAY= -github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= +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/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= -github.com/uber/jaeger-client-go v2.25.0+incompatible h1:IxcNZ7WRY1Y3G4poYlx24szfsn/3LvK9QHCq9oQw8+U= 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= +github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= +github.com/umbracle/gohashtree v0.0.2-alpha.0.20230207094856-5b775a815c10/go.mod h1:x/Pa0FF5Te9kdrlZKJK82YmAkvL8+f989USgz6Jiw7M= github.com/urfave/cli v1.22.10/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= 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.26.0 h1:3f3AMg3HpThFNT4I++TKOejZO8yU55t3JnnSr4S4QEI= github.com/urfave/cli/v2 v2.26.0/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= -github.com/warpfork/go-testmark v0.11.0 h1:J6LnV8KpceDvo7spaNU4+DauH2n1x+6RaO2rJrmpQ9U= +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= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasthttp v1.40.0/go.mod h1:t/G+3rLek+CyY9bnIE+YlMRddxVAAGjhxndDB4i4C0I= +github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= +github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc= +github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= github.com/warpfork/go-testmark v0.11.0/go.mod h1:jhEf8FVxd+F17juRubpmut64NEG6I2rgkUhlcqqXwE0= github.com/warpfork/go-wish v0.0.0-20180510122957-5ad1f5abf436/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw= github.com/warpfork/go-wish v0.0.0-20200122115046-b9ea61034e4a/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw= -github.com/warpfork/go-wish v0.0.0-20220906213052-39a1cc7a02d0 h1:GDDkbFiaK8jsSDJfjId/PEGEShv6ugrt4kYsC5UIDaQ= github.com/warpfork/go-wish v0.0.0-20220906213052-39a1cc7a02d0/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw= github.com/wealdtech/go-bytesutil v1.2.0 h1:GEIzvAZEIgqOoRfnEAaMRNL73gl8e+YlQzqxhFyR30Y= github.com/wealdtech/go-bytesutil v1.2.0/go.mod h1:FHQSlwhzfSZGffu1osaUGdnNtl5C8tBKwmqvPdB66pM= @@ -719,22 +1640,26 @@ github.com/wealdtech/go-eth2-util v1.8.0 h1:hhs3h2y3Ldty18lppFdpe46nZpdDAMbY7Qqi github.com/wealdtech/go-eth2-util v1.8.0/go.mod h1:rSuE0v5zX+uyZrqW/iUmXOxeDpB7lTvhcZvAVh0KlMU= github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4 v1.3.0 h1:3Kx2QvKU/4PP0d7+e3+q9G+QiXQprNQPeAXsWOcfKRI= github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4 v1.3.0/go.mod h1:qqIU42c9sXcNYsiEjUQoOOWYZfZDL1zmyLtz3t+wN2s= -github.com/wealdtech/go-eth2-wallet-types/v2 v2.9.0 h1:XqWgsONVqsPvciuEXxM/QU4hYouBVk0+5/pGqDMGUHQ= github.com/wealdtech/go-eth2-wallet-types/v2 v2.9.0/go.mod h1:7Ad2xp27vOQRQWQsIeHBdU/YiyEt6klBeh5gwnNnlwE= github.com/wealdtech/go-multicodec v1.4.0 h1:iq5PgxwssxnXGGPTIK1srvt6U5bJwIp7k6kBrudIWxg= github.com/wealdtech/go-multicodec v1.4.0/go.mod h1:aedGMaTeYkIqi/KCPre1ho5rTb3hGpu/snBOS3GQLw4= -github.com/wealdtech/go-string2eth v1.1.0 h1:USJQmysUrBYYmZs7d45pMb90hRSyEwizP7lZaOZLDAw= github.com/wealdtech/go-string2eth v1.1.0/go.mod h1:RUzsLjJtbZaJ/3UKn9kY19a/vCCUHtEWoUW3uiK6yGU= +github.com/whyrusleeping/base32 v0.0.0-20170828182744-c30ac30633cc/go.mod h1:r45hJU7yEoA81k6MWNhpMj/kms0n14dkzkxYHoB96UM= +github.com/whyrusleeping/cbor v0.0.0-20171005072247-63513f603b11/go.mod h1:Wlo/SzPmxVp6vXpGt/zaXhHH0fn4IxgqZc82aKg6bpQ= github.com/whyrusleeping/cbor-gen v0.0.0-20200123233031-1cdf64d27158/go.mod h1:Xj/M2wWU+QdTdRbu/L/1dIZY8/Wb2K9pAhtroQuxJJI= github.com/whyrusleeping/cbor-gen v0.0.0-20230126041949-52956bd4c9aa h1:EyA027ZAkuaCLoxVX4r1TZMPy1d31fM6hbfQ4OU4I5o= github.com/whyrusleeping/cbor-gen v0.0.0-20230126041949-52956bd4c9aa/go.mod h1:fgkXqYy7bV2cFeIEOkVTZS/WjXARfBqSH6Q2qHL33hQ= github.com/whyrusleeping/chunker v0.0.0-20181014151217-fe64bd25879f h1:jQa4QT2UP9WYv2nzyawpKMOCl+Z/jW7djv2/J50lj9E= github.com/whyrusleeping/chunker v0.0.0-20181014151217-fe64bd25879f/go.mod h1:p9UJB6dDgdPgMJZs7UjUOdulKyRr9fqkS+6JKAInPy8= +github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1/go.mod h1:8UvriyWtv5Q5EOgjHaSseUEdkQfvwFv1I/In/O2M9gc= +github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE= +github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4= github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= -github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= +github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= +github.com/yosssi/ace v0.0.5/go.mod h1:ALfIzm2vT7t5ZE7uoIZqF3TQ7SAOyupFZnkrF5id+K0= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -743,8 +1668,8 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= 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 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +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= @@ -754,24 +1679,26 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 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 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= 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/metric v1.17.0 h1:iG6LGVz5Gh+IuO0jmgvpTB6YVrCGngi8QGm+pMd8Pdc= +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/go.mod h1:h4skoxdZI17AxwITdmdZjjYJQH5nzijUUjm+wtPph5o= -go.opentelemetry.io/otel/sdk v1.17.0 h1:FLN2X66Ke/k5Sg3V623Q7h7nt3cHXaW1FOvKKrW0IpE= +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.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= go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/automaxprocs v1.5.2/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0= +go.uber.org/dig v1.17.1/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE= +go.uber.org/fx v1.20.1/go.mod h1:iSYNbHf2y55acNCwCXKx7LbWb5WG1Bnue5RDXz1OREg= go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= -go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= @@ -793,8 +1720,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -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/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= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -805,8 +1732,9 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 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-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/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= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -832,8 +1760,10 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 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.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8= +golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -877,8 +1807,9 @@ golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -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/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= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -891,8 +1822,7 @@ golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210413134643-5e61552d6c78/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ= -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/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= @@ -905,13 +1835,16 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 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.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/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= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190219092855-153ac476189d/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -928,6 +1861,7 @@ golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7w 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-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -962,8 +1896,10 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -973,14 +1909,20 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= +golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 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/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +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.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/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= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -992,12 +1934,12 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -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/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= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -1053,8 +1995,10 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f 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.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1083,7 +2027,6 @@ google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34q google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= -google.golang.org/api v0.45.0 h1:pqMffJFLBVUDIoYsHcqtxgQVTsmxMDpYLOc5MT4Jrww= google.golang.org/api v0.45.0/go.mod h1:ISLIJCedJolbZvDfAk+Ctuq5hf+aJ33WgtUsfyFoLXA= 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= @@ -1091,7 +2034,6 @@ google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7 google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -1171,33 +2113,32 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -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/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= 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-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +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/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= 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= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/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 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o= 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= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -1206,22 +2147,19 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/apimachinery v0.20.0 h1:jjzbTJRXk0unNS71L7h3lxGDH/2HPxMPaQY+MjECKL8= +honnef.co/go/tools v0.5.0-0.dev.0.20231205170804-aef76f4feee2/go.mod h1:J8YyqAvNy0yWpeKUOCONA1m2G4hH2CqUSo/5ZO2/5UA= k8s.io/apimachinery v0.20.0/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= -k8s.io/client-go v0.20.0 h1:Xlax8PKbZsjX4gFvNtt4F5MoJ1V5prDvCuoq9B7iax0= k8s.io/client-go v0.20.0/go.mod h1:4KWh/g+Ocd8KkCwKF8vUNnmqgv+EVnQDK4MBF4oB5tY= -k8s.io/klog/v2 v2.80.0 h1:lyJt0TWMPaGoODa8B8bUuxgHS3W/m/bNr2cca3brA/g= k8s.io/klog/v2 v2.80.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/utils v0.0.0-20201110183641-67b214c5f920 h1:CbnUZsM497iRC5QMVkHwyl8s2tB3g7yaSHkYPkpgelw= 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= +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 h1:YHQV7Dajm86OuqnIR6zAelnDWBRjo+YhYV9PmGrh1s8= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= 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/build.sh b/rocketpool-cli/build.sh deleted file mode 100755 index 6b7f21c56..000000000 --- a/rocketpool-cli/build.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash - -export CGO_ENABLED=0 -cd /smartnode/rocketpool-cli - -# Build x64 version -GOOS=linux GOARCH=amd64 go build -o rocketpool-cli-linux-amd64 rocketpool-cli.go -GOOS=darwin GOARCH=amd64 go build -o rocketpool-cli-darwin-amd64 rocketpool-cli.go - -# Build the arm64 version -GOOS=linux GOARCH=arm64 go build -o rocketpool-cli-linux-arm64 rocketpool-cli.go -GOOS=darwin GOARCH=arm64 go build -o rocketpool-cli-darwin-arm64 rocketpool-cli.go diff --git a/go.mod b/rocketpool-cli/go.mod similarity index 93% rename from go.mod rename to rocketpool-cli/go.mod index 70ea4a5d5..1c63cafdb 100644 --- a/go.mod +++ b/rocketpool-cli/go.mod @@ -1,4 +1,4 @@ -module github.com/rocket-pool/smartnode +module github.com/rocket-pool/smartnode/rocketpool-cli go 1.21 @@ -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.20250513050821-a194ed87d86d 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 @@ -172,11 +171,3 @@ require ( lukechampine.com/blake3 v1.2.1 // indirect rsc.io/tmplfunc v0.0.3 // indirect ) - -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/web3-storage/go-w3s-client => github.com/rocket-pool/go-w3s-client v0.0.0-20221006052217-dbd9938d11d8 - -// replace github.com/wealdtech/go-eth2-types/v2 => github.com/rocket-pool/go-eth2-types/v2 v2.0.0-20230130220714-d88838162252 - -// replace github.com/rocket-pool/rocketpool-go => ../rocketpool-go 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 e5713672c..74b1ca68c 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 3b8ce87a3..515c9afd4 100644 --- a/rocketpool-cli/node/claim-rewards.go +++ b/rocketpool-cli/node/claim-rewards.go @@ -3,11 +3,12 @@ package node import ( "fmt" "math/big" + "slices" "strconv" "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" @@ -150,13 +151,7 @@ func nodeClaimRewards(c *cli.Context) error { seenIndices := map[uint64]bool{} for _, element := range elements { - found := false - for _, validIndex := range validIndices { - if validIndex == element { - found = true - break - } - } + found := slices.Contains(validIndices, element) if !found { fmt.Printf("'%s' is an invalid index.\nValid indices are: %s\n", element, strings.Join(validIndices, ",")) allValid = false 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/primary-withdrawal-address.go b/rocketpool-cli/node/primary-withdrawal-address.go index ff4c97d41..49893e182 100644 --- a/rocketpool-cli/node/primary-withdrawal-address.go +++ b/rocketpool-cli/node/primary-withdrawal-address.go @@ -8,7 +8,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/urfave/cli" - "github.com/rocket-pool/rocketpool-go/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" @@ -73,8 +72,7 @@ func setPrimaryWithdrawalAddress(c *cli.Context, withdrawalAddressOrENS string) if err != nil { return fmt.Errorf("Invalid test amount '%s': %w\n", inputAmount, err) } - amountWei := eth.EthToWei(testAmount) - canSendResponse, err := rp.CanNodeSend(amountWei, "eth", withdrawalAddress) + canSendResponse, err := rp.CanNodeSend(testAmount, "eth", withdrawalAddress) if err != nil { return err } @@ -90,7 +88,7 @@ func setPrimaryWithdrawalAddress(c *cli.Context, withdrawalAddressOrENS string) return nil } - sendResponse, err := rp.NodeSend(amountWei, "eth", withdrawalAddress) + sendResponse, err := rp.NodeSend(testAmount, "eth", withdrawalAddress) if err != nil { return err } diff --git a/rocketpool-cli/node/rpl-withdrawal-address.go b/rocketpool-cli/node/rpl-withdrawal-address.go index 09932736f..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" @@ -84,8 +84,7 @@ func setRPLWithdrawalAddress(c *cli.Context, withdrawalAddressOrENS string) erro if err != nil { return fmt.Errorf("Invalid test amount '%s': %w\n", inputAmount, err) } - amountWei := eth.EthToWei(testAmount) - canSendResponse, err := rp.CanNodeSend(amountWei, "eth", withdrawalAddress) + canSendResponse, err := rp.CanNodeSend(testAmount, "eth", withdrawalAddress) if err != nil { return err } @@ -101,7 +100,7 @@ func setRPLWithdrawalAddress(c *cli.Context, withdrawalAddressOrENS string) erro return nil } - sendResponse, err := rp.NodeSend(amountWei, "eth", withdrawalAddress) + sendResponse, err := rp.NodeSend(testAmount, "eth", withdrawalAddress) if err != nil { return err } diff --git a/rocketpool-cli/node/send.go b/rocketpool-cli/node/send.go index b59260ea6..6bbb849b2 100644 --- a/rocketpool-cli/node/send.go +++ b/rocketpool-cli/node/send.go @@ -5,17 +5,15 @@ import ( "strings" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/utils/eth" "github.com/urfave/cli" "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" "github.com/rocket-pool/smartnode/shared/utils/cli/prompt" - "github.com/rocket-pool/smartnode/shared/utils/math" ) -func nodeSend(c *cli.Context, amount float64, token string, toAddressOrENS string) error { +func nodeSend(c *cli.Context, amountRaw float64, token string, toAddressOrENS string) error { // Get RP client rp, err := rocketpool.NewClientFromCtx(c).WithReady() @@ -24,9 +22,6 @@ func nodeSend(c *cli.Context, amount float64, token string, toAddressOrENS strin } defer rp.Close() - // Get amount in wei - amountWei := eth.EthToWei(amount) - // Get the recipient var toAddress common.Address var toAddressString string @@ -46,7 +41,7 @@ func nodeSend(c *cli.Context, amount float64, token string, toAddressOrENS strin } // Check tokens can be sent - canSend, err := rp.CanNodeSend(amountWei, token, toAddress) + canSend, err := rp.CanNodeSend(amountRaw, token, toAddress) if err != nil { return err } @@ -69,16 +64,16 @@ func nodeSend(c *cli.Context, amount float64, token string, toAddressOrENS strin fmt.Printf("Token address: %s\n", token) fmt.Printf("Token name: %s\n", canSend.TokenName) fmt.Printf("Token symbol: %s\n", canSend.TokenSymbol) - fmt.Printf("Node balance: %.6f %s\n\n", eth.WeiToEth(canSend.Balance), canSend.TokenSymbol) + fmt.Printf("Node balance: %.8f %s\n\n", canSend.Balance, canSend.TokenSymbol) fmt.Printf("%sWARNING: Please confirm that the above token is the one you intend to send before confirming below!%s\n\n", colorYellow, colorReset) - if !(c.Bool("yes") || prompt.Confirm(fmt.Sprintf("Are you sure you want to send %.6f of %s to %s? This action cannot be undone!", math.RoundDown(eth.WeiToEth(amountWei), 6), tokenString, toAddressString))) { + if !(c.Bool("yes") || prompt.Confirm(fmt.Sprintf("Are you sure you want to send %.8f of %s to %s? This action cannot be undone!", amountRaw, tokenString, toAddressString))) { fmt.Println("Cancelled.") return nil } } else { - fmt.Printf("Node balance: %.6f %s\n\n", eth.WeiToEth(canSend.Balance), token) - if !(c.Bool("yes") || prompt.Confirm(fmt.Sprintf("Are you sure you want to send %.6f %s to %s? This action cannot be undone!", math.RoundDown(eth.WeiToEth(amountWei), 6), token, toAddressString))) { + fmt.Printf("Node balance: %.8f %s\n\n", canSend.Balance, token) + if !(c.Bool("yes") || prompt.Confirm(fmt.Sprintf("Are you sure you want to send %.8f %s to %s? This action cannot be undone!", amountRaw, token, toAddressString))) { fmt.Println("Cancelled.") return nil } @@ -91,7 +86,7 @@ func nodeSend(c *cli.Context, amount float64, token string, toAddressOrENS strin } // Send tokens - response, err := rp.NodeSend(amountWei, token, toAddress) + response, err := rp.NodeSend(amountRaw, token, toAddress) if err != nil { return err } @@ -108,9 +103,9 @@ func nodeSend(c *cli.Context, amount float64, token string, toAddressOrENS strin // Log & return if strings.HasPrefix(token, "0x") { - fmt.Printf("Successfully sent %.6f of %s to %s.\n", math.RoundDown(eth.WeiToEth(amountWei), 6), tokenString, toAddressString) + fmt.Printf("Successfully sent %.6f of %s to %s.\n", amountRaw, tokenString, toAddressString) } else { - fmt.Printf("Successfully sent %.6f %s to %s.\n", math.RoundDown(eth.WeiToEth(amountWei), 6), token, toAddressString) + fmt.Printf("Successfully sent %.6f %s to %s.\n", amountRaw, token, toAddressString) } return nil 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 b95defab9..5779be39f 100644 --- a/rocketpool-cli/node/status.go +++ b/rocketpool-cli/node/status.go @@ -9,7 +9,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/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 25308a192..195799912 100644 --- a/rocketpool-cli/node/utils.go +++ b/rocketpool-cli/node/utils.go @@ -6,6 +6,7 @@ import ( "net/http" "os" "path/filepath" + "slices" "sort" "strconv" "strings" @@ -17,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" @@ -146,11 +147,8 @@ func promptTimezone() string { isUpper := unicode.IsUpper(rune(filename[0])) // Must start with an upper case letter if !isSymlink && isDir && isUpper { isValid := true - for _, invalidCountry := range invalidCountries { - if invalidCountry == filename { - isValid = false - break - } + if slices.Contains(invalidCountries, filename) { + isValid = false } if isValid { countryNames = append(countryNames, filename) @@ -192,13 +190,7 @@ func promptTimezone() string { timezone = "" country = prompt.Prompt("Please enter a country / continent from the list above:", "^.+$", "Please enter a country / continent from the list above:") - exists := false - for _, candidate := range countryNames { - if candidate == country { - exists = true - break - } - } + exists := slices.Contains(countryNames, country) if !exists { fmt.Printf("%s is not a valid country or continent. Please see the list above for valid countries and continents.\n\n", country) @@ -253,13 +245,7 @@ func promptTimezone() string { timezone = "" region = prompt.Prompt("Please enter a region from the list above:", "^.+$", "Please enter a region from the list above:") - exists := false - for _, candidate := range regionNames { - if candidate == region { - exists = true - break - } - } + exists := slices.Contains(regionNames, region) if !exists { fmt.Printf("%s is not a valid country or continent. Please see the list above for valid countries and continents.\n\n", region) 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 5981c39f7..a8c72ae56 100644 --- a/rocketpool-cli/node/withdraw-rpl.go +++ b/rocketpool-cli/node/withdraw-rpl.go @@ -6,7 +6,7 @@ import ( "strconv" "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" 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 7dd87f3ca..e4c65e008 100644 --- a/rocketpool-cli/odao/proposals.go +++ b/rocketpool-cli/odao/proposals.go @@ -4,10 +4,11 @@ import ( "bytes" "encoding/hex" "fmt" + "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" @@ -22,14 +23,7 @@ func filterProposalState(state string, stateFilter string) bool { // Check comma separated list for the state filterStates := strings.Split(stateFilter, ",") - for _, fs := range filterStates { - if fs == state { - return false - } - } - - // Not found - return true + return !slices.Contains(filterStates, state) } func getProposals(c *cli.Context, stateFilter string) error { 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 51de0dcb9..a41bd6c68 100644 --- a/rocketpool-cli/pdao/proposals.go +++ b/rocketpool-cli/pdao/proposals.go @@ -4,12 +4,13 @@ import ( "encoding/hex" "fmt" "math" + "slices" "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" @@ -26,14 +27,7 @@ func filterProposalState(state string, stateFilter string) bool { // Check comma separated list for the state filterStates := strings.Split(stateFilter, ",") - for _, fs := range filterStates { - if fs == state { - return false - } - } - - // Not found - return true + return !slices.Contains(filterStates, state) } func getProposals(c *cli.Context, stateFilter string) error { 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/rocketpool-cli.go b/rocketpool-cli/rocketpool-cli.go index 35deab887..3a117028c 100644 --- a/rocketpool-cli/rocketpool-cli.go +++ b/rocketpool-cli/rocketpool-cli.go @@ -27,17 +27,12 @@ func main() { // Add logo to application help template cli.AppHelpTemplate = fmt.Sprintf(` -______ _ _ ______ _ -| ___ \ | | | | | ___ \ | | -| |_/ /___ ___| | _____| |_ | |_/ /__ ___ | | -| // _ \ / __| |/ / _ \ __| | __/ _ \ / _ \| | -| |\ \ (_) | (__| < __/ |_ | | | (_) | (_) | | -\_| \_\___/ \___|_|\_\___|\__| \_| \___/ \___/|_| +%s Authored by the Rocket Pool Core Team A special thanks to the Rocket Pool community for all their contributions. -%s`, cli.AppHelpTemplate) +%s`, shared.Logo(), cli.AppHelpTemplate) // Initialise application app := cli.NewApp() @@ -45,7 +40,7 @@ A special thanks to the Rocket Pool community for all their contributions. // Set application info app.Name = "rocketpool" app.Usage = "Rocket Pool CLI" - app.Version = shared.RocketPoolVersion + app.Version = shared.RocketPoolVersion() app.Copyright = "(c) 2024 Rocket Pool Pty Ltd" // Initialize app metadata 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 c2b37725e..a084356ae 100644 --- a/rocketpool-cli/security/proposals.go +++ b/rocketpool-cli/security/proposals.go @@ -4,10 +4,11 @@ import ( "bytes" "encoding/hex" "fmt" + "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" @@ -22,14 +23,7 @@ func filterProposalState(state string, stateFilter string) bool { // Check comma separated list for the state filterStates := strings.Split(stateFilter, ",") - for _, fs := range filterStates { - if fs == state { - return false - } - } - - // Not found - return true + return !slices.Contains(filterStates, state) } func getProposals(c *cli.Context, stateFilter string) error { 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/service/commands.go b/rocketpool-cli/service/commands.go index 197540737..62efd0c6b 100644 --- a/rocketpool-cli/service/commands.go +++ b/rocketpool-cli/service/commands.go @@ -2,6 +2,7 @@ package service import ( "fmt" + "os" "strings" "github.com/urfave/cli" @@ -131,7 +132,6 @@ func RegisterCommands(app *cli.App, name string, aliases []string) { cli.StringFlag{ Name: "version, v", Usage: "The smart node package version to install", - Value: fmt.Sprintf("v%s", shared.RocketPoolVersion), }, }, Action: func(c *cli.Context) error { @@ -141,6 +141,11 @@ func RegisterCommands(app *cli.App, name string, aliases []string) { return err } + if c.String("version") != "" { + fmt.Fprintf(os.Stderr, "--version/-v is no longer supported. Instead, download the correct version of the `rocketpool` binary and install that. Current version: %s\n", shared.RocketPoolVersion()) + os.Exit(1) + } + // Run command return installService(c) @@ -417,8 +422,7 @@ func RegisterCommands(app *cli.App, name string, aliases []string) { }, cli.StringFlag{ Name: "version, v", - Usage: "The update tracker package version to install", - Value: fmt.Sprintf("v%s", shared.RocketPoolVersion), + Usage: "DEPRECATED: The update tracker package version to install", }, }, Action: func(c *cli.Context) error { @@ -428,6 +432,11 @@ func RegisterCommands(app *cli.App, name string, aliases []string) { return err } + if c.String("version") != "" { + fmt.Fprintf(os.Stderr, "--version/-v is no longer supported. Instead, download the correct version of the `rocketpool` binary and install the update tracker from there. Current version: %s\n", shared.RocketPoolVersion()) + os.Exit(1) + } + // Run command return installUpdateTracker(c) diff --git a/rocketpool-cli/service/config/main-display.go b/rocketpool-cli/service/config/main-display.go index 5904e077b..d362599b3 100644 --- a/rocketpool-cli/service/config/main-display.go +++ b/rocketpool-cli/service/config/main-display.go @@ -53,7 +53,7 @@ func NewMainDisplay(app *tview.Application, previousConfig *config.RocketPoolCon grid.SetBackgroundColor(NonInteractiveBackgroundColor) grid.SetBorder(true). - SetTitle(fmt.Sprintf(" Rocket Pool Smartnode %s Configuration ", shared.RocketPoolVersion)). + SetTitle(fmt.Sprintf(" Rocket Pool Smartnode %s Configuration ", shared.RocketPoolVersion())). SetBorderColor(tcell.ColorOrange). SetTitleColor(tcell.ColorOrange). SetBackgroundColor(NonInteractiveBackgroundColor) diff --git a/rocketpool-cli/service/config/review-page.go b/rocketpool-cli/service/config/review-page.go index 8dbdc2183..c9085de91 100644 --- a/rocketpool-cli/service/config/review-page.go +++ b/rocketpool-cli/service/config/review-page.go @@ -56,7 +56,7 @@ func NewReviewPage(md *mainDisplay, oldConfig *config.RocketPoolConfig, newConfi if newConfig.ExecutionClientMode.Value.(cfgtypes.Mode) == cfgtypes.Mode_Local && newConfig.ExecutionClient.Value.(cfgtypes.ExecutionClient) != cfgtypes.ExecutionClient_Geth { totalAffectedContainers[cfgtypes.ContainerID_Eth1] = true } - builder.WriteString(fmt.Sprintf("Updated to Smartnode v%s (will affect several containers)\n\n", shared.RocketPoolVersion)) + builder.WriteString(fmt.Sprintf("Updated to Smartnode v%s (will affect several containers)\n\n", shared.RocketPoolVersion())) } for categoryName, changedSettingsList := range changedSettings { diff --git a/rocketpool-cli/service/config/standard-layout.go b/rocketpool-cli/service/config/standard-layout.go index 2ecb29d6f..7547290fc 100644 --- a/rocketpool-cli/service/config/standard-layout.go +++ b/rocketpool-cli/service/config/standard-layout.go @@ -2,6 +2,7 @@ package config import ( "fmt" + "slices" "github.com/gdamore/tcell/v2" "github.com/rivo/tview" @@ -181,11 +182,8 @@ func (layout *standardLayout) addFormItemsWithCommonParams(commonParams []*param // Add the common params if they aren't in the unsupported list for _, commonParam := range commonParams { isSupported := true - for _, unsupportedParam := range unsupportedCommonParams { - if commonParam.parameter.ID == unsupportedParam { - isSupported = false - break - } + if slices.Contains(unsupportedCommonParams, commonParam.parameter.ID) { + isSupported = false } if isSupported { diff --git a/rocketpool-cli/service/config/step-native-welcome.go b/rocketpool-cli/service/config/step-native-welcome.go index 20045ce83..6e5435e5d 100644 --- a/rocketpool-cli/service/config/step-native-welcome.go +++ b/rocketpool-cli/service/config/step-native-welcome.go @@ -8,7 +8,7 @@ import ( func createNativeWelcomeStep(wiz *wizard, currentStep int, totalSteps int) *choiceWizardStep { - helperText := fmt.Sprintf("%s\n\nWelcome to the Smartnode configuration wizard!\n\nWe've detected that you're running in Native mode. We'll keep it simple and only show you the settings that are relevant to you.\n\nIf you're upgrading from a previous version, your settings have been migrated.", shared.Logo) + helperText := fmt.Sprintf("%s\n\nWelcome to the Smartnode configuration wizard!\n\nWe've detected that you're running in Native mode. We'll keep it simple and only show you the settings that are relevant to you.\n\nIf you're upgrading from a previous version, your settings have been migrated.", shared.Logo()) show := func(modal *choiceModalLayout) { wiz.md.setPage(modal.page) diff --git a/rocketpool-cli/service/config/step-welcome.go b/rocketpool-cli/service/config/step-welcome.go index 886d6ebec..2b2a51945 100644 --- a/rocketpool-cli/service/config/step-welcome.go +++ b/rocketpool-cli/service/config/step-welcome.go @@ -15,7 +15,7 @@ func createWelcomeStep(wiz *wizard, currentStep int, totalSteps int) *choiceWiza intro = "You've already configured Rocket Pool, so we'll highlight all of the settings you're already using for convenience. You're welcome to make changes as you go through the wizard." } - helperText := fmt.Sprintf("%s\n\nWelcome to the Smartnode configuration wizard!\n\n%s\n\n", shared.Logo, intro) + helperText := fmt.Sprintf("%s\n\nWelcome to the Smartnode configuration wizard!\n\n%s\n\n", shared.Logo(), intro) show := func(modal *choiceModalLayout) { wiz.md.setPage(modal.page) diff --git a/rocketpool-cli/service/service.go b/rocketpool-cli/service/service.go index 744f0b74c..38b77ff53 100644 --- a/rocketpool-cli/service/service.go +++ b/rocketpool-cli/service/service.go @@ -21,7 +21,6 @@ import ( "github.com/rocket-pool/smartnode/shared/services/config" "github.com/rocket-pool/smartnode/shared/services/rocketpool" cfgtypes "github.com/rocket-pool/smartnode/shared/types/config" - sharedConfig "github.com/rocket-pool/smartnode/shared/types/config" cliutils "github.com/rocket-pool/smartnode/shared/utils/cli" "github.com/rocket-pool/smartnode/shared/utils/cli/prompt" "github.com/shirou/gopsutil/v3/disk" @@ -33,12 +32,10 @@ const ( ValidatorContainerSuffix string = "_validator" BeaconContainerSuffix string = "_eth2" ExecutionContainerSuffix string = "_eth1" - PruneStarterContainerSuffix string = "_nm_prune_starter" NodeContainerSuffix string = "_node" ApiContainerSuffix string = "_api" WatchtowerContainerSuffix string = "_watchtower" PruneProvisionerContainerSuffix string = "_prune_provisioner" - EcMigratorContainerSuffix string = "_ec_migrator" clientDataVolumeName string = "/ethclient" dataFolderVolumeName string = "/.rocketpool/data" @@ -59,8 +56,8 @@ func installService(c *cli.Context) error { // Prompt for confirmation if !(c.Bool("yes") || prompt.Confirm(fmt.Sprintf( - "The Rocket Pool service will be installed --Version: %s\n\n%sIf you're upgrading, your existing configuration will be backed up and preserved.\nAll of your previous settings will be migrated automatically.%s\nAre you sure you want to continue?", - c.String("version"), colorGreen, colorReset, + "The Rocket Pool %s service will be installed.\n\n%sIf you're upgrading, your existing configuration will be backed up and preserved.\nAll of your previous settings will be migrated automatically.%s\nAre you sure you want to continue?", + shared.RocketPoolVersion(), colorGreen, colorReset, ))) { fmt.Println("Cancelled.") return nil @@ -84,7 +81,7 @@ func installService(c *cli.Context) error { } // Install service - err = rp.InstallService(c.Bool("verbose"), c.Bool("no-deps"), c.String("version"), c.String("path"), dataPath) + err = rp.InstallService(c.Bool("verbose"), c.Bool("no-deps"), c.String("path"), dataPath) if err != nil { return err } @@ -119,20 +116,14 @@ func installService(c *cli.Context) error { // TODO: get this from an external source and don't hardcode it into the CLI func printPatchNotes(c *cli.Context) { - fmt.Print(` -______ _ _ ______ _ -| ___ \ | | | | | ___ \ | | -| |_/ /___ ___| | _____| |_ | |_/ /__ ___ | | -| // _ \ / __| |/ / _ \ __| | __/ _ \ / _ \| | -| |\ \ (_) | (__| < __/ |_ | | | (_) | (_) | | -\_| \_\___/ \___|_|\_\___|\__| \_| \___/ \___/|_| - -`) - fmt.Printf("%s=== Smart Node v%s ===%s\n\n", colorGreen, shared.RocketPoolVersion, colorReset) - fmt.Printf("Changes you should be aware of before starting:\n\n") - - fmt.Println("") - + fmt.Print(shared.Logo()) + fmt.Println() + fmt.Println() + fmt.Printf("%s=== Smart Node v%s ===%s\n", colorGreen, shared.RocketPoolVersion(), colorReset) + fmt.Println() + fmt.Printf("Changes you should be aware of before starting:\n") + fmt.Println() + fmt.Println() } // Install the Rocket Pool update tracker for the metrics dashboard @@ -151,7 +142,7 @@ func installUpdateTracker(c *cli.Context) error { defer rp.Close() // Install service - err := rp.InstallUpdateTracker(c.Bool("verbose"), c.String("version")) + err := rp.InstallUpdateTracker(c.Bool("verbose")) if err != nil { return err } @@ -408,7 +399,7 @@ func updateConfigParamFromCliArg(c *cli.Context, sectionName string, param *cfgt func changeNetworks(c *cli.Context, rp *rocketpool.Client, apiContainerName string) error { // Stop all of the containers - fmt.Print("Stopping containers... ") + fmt.Println("Stopping containers... ") err := rp.PauseService(getComposeFiles(c)) if err != nil { return fmt.Errorf("error stopping service: %w", err) @@ -416,7 +407,7 @@ func changeNetworks(c *cli.Context, rp *rocketpool.Client, apiContainerName stri fmt.Println("done") // Restart the API container - fmt.Print("Starting API container... ") + fmt.Println("Starting API container... ") output, err := rp.StartContainer(apiContainerName) if err != nil { return fmt.Errorf("error starting API container: %w", err) @@ -427,7 +418,7 @@ func changeNetworks(c *cli.Context, rp *rocketpool.Client, apiContainerName stri fmt.Println("done") // Get the path of the user's data folder - fmt.Print("Retrieving data folder path... ") + fmt.Println("Retrieving data folder path... ") volumePath, err := rp.GetClientVolumeSource(apiContainerName, dataFolderVolumeName) if err != nil { return fmt.Errorf("error getting data folder path: %w", err) @@ -435,7 +426,7 @@ func changeNetworks(c *cli.Context, rp *rocketpool.Client, apiContainerName stri fmt.Printf("done, data folder = %s\n", volumePath) // Delete the data folder - fmt.Print("Removing data folder... ") + fmt.Println("Removing data folder... ") _, err = rp.TerminateDataFolder() if err != nil { return err @@ -443,7 +434,7 @@ func changeNetworks(c *cli.Context, rp *rocketpool.Client, apiContainerName stri fmt.Println("done") // Terminate the current setup - fmt.Print("Removing old installation... ") + fmt.Println("Removing old installation... ") err = rp.StopService(getComposeFiles(c)) if err != nil { return fmt.Errorf("error terminating old installation: %w", err) @@ -451,14 +442,14 @@ func changeNetworks(c *cli.Context, rp *rocketpool.Client, apiContainerName stri fmt.Println("done") // Create new validator folder - fmt.Print("Recreating data folder... ") + fmt.Println("Recreating data folder... ") err = os.MkdirAll(filepath.Join(volumePath, "validators"), 0775) if err != nil { return fmt.Errorf("error recreating data folder: %w", err) } // Start the service - fmt.Print("Starting Rocket Pool... ") + fmt.Println("Starting Rocket Pool... ") err = rp.StartService(getComposeFiles(c)) if err != nil { return fmt.Errorf("error starting service: %w", err) @@ -976,28 +967,26 @@ func pruneExecutionClient(c *cli.Context) error { fmt.Println("You are using Native Mode.\nThe Smart Node cannot prune your Execution client for you, you'll have to do it manually.") } selectedEc := cfg.ExecutionClient.Value.(cfgtypes.ExecutionClient) - switch selectedEc { - case cfgtypes.ExecutionClient_Besu: - if cfg.Besu.ArchiveMode.Value == true { - fmt.Println("You are using Besu as an archive node.\nArchive nodes should not be pruned. Aborting.") - return nil - } + + // Don't prune besu if it's in archive mode + if selectedEc == cfgtypes.ExecutionClient_Besu && cfg.Besu.ArchiveMode.Value == true { + fmt.Println("You are using Besu as an archive node.\nArchive nodes should not be pruned. Aborting.") + return nil } - if selectedEc == cfgtypes.ExecutionClient_Geth || selectedEc == cfgtypes.ExecutionClient_Besu { - if selectedEc == cfgtypes.ExecutionClient_Geth { - fmt.Printf("%sGeth has a new feature that renders pruning obsolete. However, as this is a new feature you may have to resync with `rocketpool service resync-eth1` before this takes effect.%s\n", colorYellow, colorReset) - } + // Print the appropriate warnings before pruning + if selectedEc == cfgtypes.ExecutionClient_Geth { + fmt.Printf("%sGeth has a new feature that renders pruning obsolete. However, as this is a new feature you may have to resync with `rocketpool service resync-eth1` before this takes effect.%s\n", colorYellow, colorReset) fmt.Println("This will shut down your main execution client and prune its database, freeing up disk space.") if cfg.UseFallbackClients.Value == false { fmt.Printf("%sYou do not have a fallback execution client configured.\nYour node will no longer be able to perform any validation duties (attesting or proposing blocks) until pruning is done.\nPlease configure a fallback client with `rocketpool service config` before running this.%s\n", colorRed, colorReset) } else { fmt.Println("You have fallback clients enabled. Rocket Pool (and your consensus client) will use that while the main client is pruning.") } + fmt.Println("Once pruning is complete, your execution client will restart automatically.") } else { fmt.Println("This will request your main execution client to prune its database, freeing up disk space. This is a resource intensive operation and may lead to an increase in missed attestations until it finishes.") } - fmt.Println("Once pruning is complete, your execution client will restart automatically.") fmt.Println() // Get the container prefix @@ -1012,14 +1001,9 @@ func pruneExecutionClient(c *cli.Context) error { return nil } - // Get the prune provisioner image - pruneProvisioner := cfg.Smartnode.GetPruneProvisionerContainerTag() - // Get the execution container name executionContainerName := prefix + ExecutionContainerSuffix - pruneStarterContainerName := prefix + PruneStarterContainerSuffix - // Check for enough free space volumePath, err := rp.GetClientVolumeSource(executionContainerName, clientDataVolumeName) if err != nil { @@ -1052,7 +1036,7 @@ func pruneExecutionClient(c *cli.Context) error { if selectedEc == cfgtypes.ExecutionClient_Nethermind { // Restarting NM is not needed anymore - err = rp.RunNethermindPruneStarter(executionContainerName, pruneStarterContainerName) + err = rp.RunNethermindPruneStarter(executionContainerName) if err != nil { return fmt.Errorf("Error starting Nethermind prune starter: %w", err) } @@ -1075,7 +1059,7 @@ func pruneExecutionClient(c *cli.Context) error { // Run the prune provisioner fmt.Printf("Provisioning pruning on volume %s...\n", volume) - err = rp.RunPruneProvisioner(prefix+PruneProvisionerContainerSuffix, volume, pruneProvisioner) + err = rp.RunPruneProvisioner(prefix+PruneProvisionerContainerSuffix, volume) if err != nil { return fmt.Errorf("Error running prune provisioner: %w", err) } @@ -1395,7 +1379,7 @@ func serviceVersion(c *cli.Context) error { var mevBoostString string if cfg.EnableMevBoost.Value.(bool) { - if cfg.MevBoost.Mode.Value.(sharedConfig.Mode) == sharedConfig.Mode_Local { + if cfg.MevBoost.Mode.Value.(cfgtypes.Mode) == cfgtypes.Mode_Local { mevBoostString = fmt.Sprintf("Enabled (Local Mode)\n\tImage: %s", cfg.MevBoost.ContainerTag.Value.(string)) } else { mevBoostString = "Enabled (External Mode)" 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/beacon_state.go b/rocketpool/api/debug/beacon_state.go index badfc310d..e68a272b1 100644 --- a/rocketpool/api/debug/beacon_state.go +++ b/rocketpool/api/debug/beacon_state.go @@ -3,14 +3,19 @@ package debug import ( "encoding/json" "fmt" + "math/big" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" + "github.com/rocket-pool/smartnode/shared/types/eth2" + "github.com/rocket-pool/smartnode/shared/types/eth2/generic" hexutil "github.com/rocket-pool/smartnode/shared/utils/hex" ) +const MAX_WITHDRAWAL_SLOT_DISTANCE = 432000 // 60 days. + func getBeaconStateForSlot(c *cli.Context, slot uint64, validatorIndex uint64) error { // Create a new response response := api.BeaconStateResponse{} @@ -25,7 +30,12 @@ func getBeaconStateForSlot(c *cli.Context, slot uint64, validatorIndex uint64) e } // Get beacon state - beaconState, err := bc.GetBeaconState(slot) + beaconStateResponse, err := bc.GetBeaconStateSSZ(slot) + if err != nil { + return err + } + + beaconState, err := eth2.NewBeaconState(beaconStateResponse.Data, beaconStateResponse.Fork) if err != nil { return err } @@ -50,3 +60,149 @@ func getBeaconStateForSlot(c *cli.Context, slot uint64, validatorIndex uint64) e return nil } + +func getWithdrawalProofForSlot(c *cli.Context, slot uint64, validatorIndex uint64) error { + // Create a new response + response := api.WithdrawalProofResponse{} + response.ValidatorIndex = validatorIndex + response.Slot = slot + // Get services + if err := services.RequireNodeRegistered(c); err != nil { + return err + } + bc, err := services.GetBeaconClient(c) + if err != nil { + return err + } + + // Find the most recent withdrawal to slot. + // Keep track of 404s- if we get 24 missing slots in a row, assume we don't have full history. + notFounds := 0 + var block eth2.BeaconBlock + for candidateSlot := slot; candidateSlot >= slot-MAX_WITHDRAWAL_SLOT_DISTANCE; candidateSlot-- { + // Get the block at the candidate slot. + blockResponse, found, err := bc.GetBeaconBlockSSZ(candidateSlot) + if err != nil { + return err + } + if !found { + notFounds++ + if notFounds >= 64 { + return fmt.Errorf("2 epochs of missing slots detected. It is likely that the Beacon Client was checkpoint synced after the most recent withdrawal to slot %d, and does not have the history required to generate a withdrawal proof", slot) + } + continue + } else { + notFounds = 0 + } + + beaconBlock, err := eth2.NewBeaconBlock(blockResponse.Data, blockResponse.Fork) + if err != nil { + return err + } + + if !beaconBlock.HasExecutionPayload() { + continue + } + + foundWithdrawal := false + + // Check the block for a withdrawal for the given validator index. + for i, withdrawal := range beaconBlock.Withdrawals() { + if withdrawal.ValidatorIndex != validatorIndex { + continue + } + response.WithdrawalSlot = candidateSlot + response.Amount = big.NewInt(0).SetUint64(withdrawal.Amount) + foundWithdrawal = true + response.IndexInWithdrawalsArray = uint(i) + response.WithdrawalIndex = withdrawal.Index + response.WithdrawalAddress = withdrawal.Address + break + } + + if foundWithdrawal { + block = beaconBlock + break + } + } + + if response.Slot == 0 { + return fmt.Errorf("no withdrawal found for validator index %d within %d slots of slot %d", validatorIndex, MAX_WITHDRAWAL_SLOT_DISTANCE, slot) + } + + // Start by proving from the withdrawal to the block_root + proof, err := block.ProveWithdrawal(uint64(response.IndexInWithdrawalsArray)) + if err != nil { + return err + } + + // Get beacon state + stateResponse, err := bc.GetBeaconStateSSZ(slot) + if err != nil { + return err + } + + state, err := eth2.NewBeaconState(stateResponse.Data, stateResponse.Fork) + if err != nil { + return err + } + + var summaryProof [][]byte + + var stateProof [][]byte + if response.WithdrawalSlot+generic.SlotsPerHistoricalRoot > state.GetSlot() { + stateProof, err = state.BlockRootProof(response.WithdrawalSlot) + if err != nil { + return err + } + } else { + stateProof, err = state.HistoricalSummaryProof(response.WithdrawalSlot) + if err != nil { + return err + } + + // Additionally, we need to prove from the block_root in the historical summary + // up to the beginning of the above proof, which is the entry in the historical summaries vector. + blockRootsStateSlot := generic.SlotsPerHistoricalRoot + ((response.WithdrawalSlot / generic.SlotsPerHistoricalRoot) * generic.SlotsPerHistoricalRoot) + // get the state that has the block roots tree + blockRootsStateResponse, err := bc.GetBeaconStateSSZ(blockRootsStateSlot) + if err != nil { + return err + } + blockRootsState, err := eth2.NewBeaconState(blockRootsStateResponse.Data, blockRootsStateResponse.Fork) + if err != nil { + return err + } + summaryProof, err = blockRootsState.HistoricalSummaryBlockRootProof(int(response.WithdrawalSlot)) + if err != nil { + return err + } + + } + + // Convert the proof to a list of 0x-prefixed hex strings + response.Proof = make([]string, 0, len(proof)+len(stateProof)+len(summaryProof)) + // First we prove from the withdrawal to the block_root + for _, hash := range proof { + response.Proof = append(response.Proof, hexutil.EncodeToString(hash)) + } + + // Then, if summaryProof has rows, we add them to prove from the block_root to the historical_summary row + for _, hash := range summaryProof { + response.Proof = append(response.Proof, hexutil.EncodeToString(hash)) + } + + // Finally, we prove either from the historical_summary or the block_root to the state_root + for _, hash := range stateProof { + response.Proof = append(response.Proof, hexutil.EncodeToString(hash)) + } + + // Render response json + json, err := json.Marshal(response) + if err != nil { + return err + } + fmt.Println(string(json)) + + return nil +} diff --git a/rocketpool/api/debug/commands.go b/rocketpool/api/debug/commands.go index 72b54ca3e..66adc64dc 100644 --- a/rocketpool/api/debug/commands.go +++ b/rocketpool/api/debug/commands.go @@ -40,7 +40,7 @@ func RegisterSubcommands(command *cli.Command, name string, aliases []string) { Name: "get-beacon-state", Aliases: []string{"b"}, Usage: "Returns the beacon state for a given slot number", - UsageText: "rocketpool api debug get-beacon-state slot-number", + UsageText: "rocketpool api debug get-beacon-state slot-number validator-index", Action: func(c *cli.Context) error { // Validate args @@ -65,6 +65,35 @@ func RegisterSubcommands(command *cli.Command, name string, aliases []string) { }, }, + { + Name: "get-withdrawal-proof", + Aliases: []string{"w"}, + Usage: "Returns a withdrawal proof for a given validator index and given slot, for the withdrawal most recent to that slot", + UsageText: "rocketpool api debug get-withdrawal-proof slot-number validator-index", + Action: func(c *cli.Context) error { + + // Validate args + if err := cliutils.ValidateArgCount(c, 2); err != nil { + return err + } + + slotNumber, err := cliutils.ValidatePositiveUint("slot number", c.Args().Get(0)) + if err != nil { + return err + } + + validatorIndex, err := cliutils.ValidatePositiveUint("validator index", c.Args().Get(1)) + if err != nil { + return err + } + + if err := getWithdrawalProofForSlot(c, slotNumber, validatorIndex); err != nil { + fmt.Printf("An error occurred: %s\n", err) + } + return nil + + }, + }, }, }) } 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 18b284063..b23a3f04c 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 571f97a11..7ff511bf9 100644 --- a/rocketpool/api/network/stats.go +++ b/rocketpool/api/network/stats.go @@ -5,14 +5,14 @@ import ( "fmt" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/deposit" - node131 "github.com/rocket-pool/rocketpool-go/legacy/v1.3.1/node" - "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" + node131 "github.com/rocket-pool/smartnode/bindings/legacy/v1.3.1/node" + "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" "golang.org/x/sync/errgroup" 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 3e2f03c82..af88ab97c 100644 --- a/rocketpool/api/node/claim-rewards.go +++ b/rocketpool/api/node/claim-rewards.go @@ -10,13 +10,13 @@ import ( "github.com/urfave/cli" "golang.org/x/sync/errgroup" - node131 "github.com/rocket-pool/rocketpool-go/legacy/v1.3.1/node" - "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" + node131 "github.com/rocket-pool/smartnode/bindings/legacy/v1.3.1/node" + "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/commands.go b/rocketpool/api/node/commands.go index 7527532c3..5fcf5a868 100644 --- a/rocketpool/api/node/commands.go +++ b/rocketpool/api/node/commands.go @@ -997,7 +997,7 @@ func RegisterSubcommands(command *cli.Command, name string, aliases []string) { if err := cliutils.ValidateArgCount(c, 3); err != nil { return err } - amountWei, err := cliutils.ValidatePositiveWeiAmount("send amount", c.Args().Get(0)) + amountRaw, err := cliutils.ValidatePositiveEthAmount("send amount", c.Args().Get(0)) if err != nil { return err } @@ -1011,7 +1011,7 @@ func RegisterSubcommands(command *cli.Command, name string, aliases []string) { } // Run - api.PrintResponse(canNodeSend(c, amountWei, token, toAddress)) + api.PrintResponse(canNodeSend(c, amountRaw, token, toAddress)) return nil }, @@ -1027,7 +1027,7 @@ func RegisterSubcommands(command *cli.Command, name string, aliases []string) { if err := cliutils.ValidateArgCount(c, 3); err != nil { return err } - amountWei, err := cliutils.ValidatePositiveWeiAmount("send amount", c.Args().Get(0)) + amountRaw, err := cliutils.ValidatePositiveEthAmount("send amount", c.Args().Get(0)) if err != nil { return err } @@ -1041,7 +1041,7 @@ func RegisterSubcommands(command *cli.Command, name string, aliases []string) { } // Run - api.PrintResponse(nodeSend(c, amountWei, token, toAddress)) + api.PrintResponse(nodeSend(c, amountRaw, token, toAddress)) return nil }, 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 bbd315a41..82b574a04 100644 --- a/rocketpool/api/node/rewards.go +++ b/rocketpool/api/node/rewards.go @@ -7,14 +7,14 @@ import ( "time" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/dao/trustednode" - node131 "github.com/rocket-pool/rocketpool-go/legacy/v1.3.1/node" - "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" + node131 "github.com/rocket-pool/smartnode/bindings/legacy/v1.3.1/node" + "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 22bb1400e..b0561d2ea 100644 --- a/rocketpool/api/node/rpl-withdrawal-address.go +++ b/rocketpool/api/node/rpl-withdrawal-address.go @@ -5,10 +5,12 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" - node131 "github.com/rocket-pool/rocketpool-go/legacy/v1.3.1/node" - "github.com/rocket-pool/rocketpool-go/node" - "github.com/rocket-pool/rocketpool-go/storage" + node131 "github.com/rocket-pool/smartnode/bindings/legacy/v1.3.1/node" + "github.com/rocket-pool/smartnode/bindings/node" + "github.com/rocket-pool/smartnode/bindings/storage" "github.com/rocket-pool/smartnode/shared/services" + "github.com/urfave/cli" + "golang.org/x/sync/errgroup" updateCheck "github.com/rocket-pool/smartnode/shared/services/state" "github.com/rocket-pool/smartnode/shared/types/api" 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 b242b1df9..ebc1e098e 100644 --- a/rocketpool/api/node/send.go +++ b/rocketpool/api/node/send.go @@ -1,14 +1,15 @@ package node import ( + "bytes" "context" "fmt" "math/big" "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" @@ -16,7 +17,7 @@ import ( "github.com/rocket-pool/smartnode/shared/utils/eth1" ) -func canNodeSend(c *cli.Context, amountWei *big.Int, token string, to common.Address) (*api.CanNodeSendResponse, error) { +func canNodeSend(c *cli.Context, amountRaw float64, token string, to common.Address) (*api.CanNodeSendResponse, error) { // Get services if err := services.RequireNodeWallet(c); err != nil { @@ -79,6 +80,10 @@ func canNodeSend(c *cli.Context, amountWei *big.Int, token string, to common.Add if strings.HasPrefix(token, "0x") { tokenAddress := common.HexToAddress(token) + if bytes.Equal(to.Bytes(), tokenAddress.Bytes()) { + return nil, fmt.Errorf("sending tokens to the same address as the token is prohibited for safety") + } + // Error out if using one of the well-known ones if tokenAddress == *rplContract.Address { return nil, fmt.Errorf("sending RPL via the token address is prohibited for safety; please use 'rpl' as the token to send instead of its address") @@ -95,6 +100,8 @@ func canNodeSend(c *cli.Context, amountWei *big.Int, token string, to common.Add if err != nil { return nil, fmt.Errorf("error creating ERC20 contract binding: %w", err) } + + amountWei := eth.EthToWeiWithDecimals(amountRaw, contract.Decimals) response.TokenName = contract.Name response.TokenSymbol = contract.Symbol @@ -103,7 +110,8 @@ func canNodeSend(c *cli.Context, amountWei *big.Int, token string, to common.Add if err != nil { return nil, fmt.Errorf("error getting ERC20 balance: %w", err) } - response.Balance = balance + + response.Balance = eth.WeiToEthWithDecimals(balance, contract.Decimals) response.InsufficientBalance = (amountWei.Cmp(balance) > 0) // Get the gas info @@ -114,16 +122,17 @@ func canNodeSend(c *cli.Context, amountWei *big.Int, token string, to common.Add response.GasInfo = gasInfo } else { // Handle well-known token types + amountWei := eth.EthToWei(amountRaw) + var balanceWei *big.Int switch token { case "eth": // Check node ETH balance - ethBalanceWei, err := ec.BalanceAt(context.Background(), nodeAccount.Address, nil) + balanceWei, err = ec.BalanceAt(context.Background(), nodeAccount.Address, nil) if err != nil { return nil, err } - response.Balance = ethBalanceWei - response.InsufficientBalance = (amountWei.Cmp(ethBalanceWei) > 0) + response.InsufficientBalance = (amountWei.Cmp(balanceWei) > 0) gasInfo, err := eth.EstimateSendTransactionGas(ec, to, nil, false, opts) if err != nil { return nil, err @@ -137,12 +146,11 @@ func canNodeSend(c *cli.Context, amountWei *big.Int, token string, to common.Add return nil, err } // Check node RPL balance - rplBalanceWei, err := tokens.GetRPLBalance(rp, nodeAccount.Address, nil) + balanceWei, err = tokens.GetRPLBalance(rp, nodeAccount.Address, nil) if err != nil { return nil, err } - response.Balance = rplBalanceWei - response.InsufficientBalance = (amountWei.Cmp(rplBalanceWei) > 0) + response.InsufficientBalance = (amountWei.Cmp(balanceWei) > 0) gasInfo, err := tokens.EstimateTransferRPLGas(rp, to, amountWei, opts) if err != nil { return nil, err @@ -156,12 +164,11 @@ func canNodeSend(c *cli.Context, amountWei *big.Int, token string, to common.Add return nil, err } // Check node fixed-supply RPL balance - fixedSupplyRplBalanceWei, err := tokens.GetFixedSupplyRPLBalance(rp, nodeAccount.Address, nil) + balanceWei, err = tokens.GetFixedSupplyRPLBalance(rp, nodeAccount.Address, nil) if err != nil { return nil, err } - response.Balance = fixedSupplyRplBalanceWei - response.InsufficientBalance = (amountWei.Cmp(fixedSupplyRplBalanceWei) > 0) + response.InsufficientBalance = (amountWei.Cmp(balanceWei) > 0) gasInfo, err := tokens.EstimateTransferFixedSupplyRPLGas(rp, to, amountWei, opts) if err != nil { return nil, err @@ -175,12 +182,11 @@ func canNodeSend(c *cli.Context, amountWei *big.Int, token string, to common.Add return nil, err } // Check node rETH balance - rethBalanceWei, err := tokens.GetRETHBalance(rp, nodeAccount.Address, nil) + balanceWei, err = tokens.GetRETHBalance(rp, nodeAccount.Address, nil) if err != nil { return nil, err } - response.Balance = rethBalanceWei - response.InsufficientBalance = (amountWei.Cmp(rethBalanceWei) > 0) + response.InsufficientBalance = (amountWei.Cmp(balanceWei) > 0) gasInfo, err := tokens.EstimateTransferRETHGas(rp, to, amountWei, opts) if err != nil { return nil, err @@ -188,6 +194,7 @@ func canNodeSend(c *cli.Context, amountWei *big.Int, token string, to common.Add response.GasInfo = gasInfo } + response.Balance = eth.WeiToEth(balanceWei) } // Update & return response @@ -196,7 +203,7 @@ func canNodeSend(c *cli.Context, amountWei *big.Int, token string, to common.Add } -func nodeSend(c *cli.Context, amountWei *big.Int, token string, to common.Address) (*api.NodeSendResponse, error) { +func nodeSend(c *cli.Context, amountRaw float64, token string, to common.Address) (*api.NodeSendResponse, error) { // Get services if err := services.RequireNodeWallet(c); err != nil { @@ -238,12 +245,15 @@ func nodeSend(c *cli.Context, amountWei *big.Int, token string, to common.Addres return nil, fmt.Errorf("error creating ERC20 contract binding: %w", err) } + amountWei := eth.EthToWeiWithDecimals(amountRaw, contract.Decimals) + tx, err := contract.Transfer(to, amountWei, opts) if err != nil { return nil, err } response.TxHash = tx.Hash() } else { + amountWei := eth.EthToWei(amountRaw) // Handle token type switch token { case "eth": 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 786bbe132..c4b9a7719 100644 --- a/rocketpool/api/node/stake-rpl.go +++ b/rocketpool/api/node/stake-rpl.go @@ -5,11 +5,11 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" - node131 "github.com/rocket-pool/rocketpool-go/legacy/v1.3.1/node" - "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" + node131 "github.com/rocket-pool/smartnode/bindings/legacy/v1.3.1/node" + "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 7b75f3e06..7da076899 100644 --- a/rocketpool/api/node/status.go +++ b/rocketpool/api/node/status.go @@ -9,21 +9,21 @@ 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" - node131 "github.com/rocket-pool/rocketpool-go/legacy/v1.3.1/node" + node131 "github.com/rocket-pool/smartnode/bindings/legacy/v1.3.1/node" mp "github.com/rocket-pool/smartnode/rocketpool/api/minipool" "github.com/rocket-pool/smartnode/rocketpool/api/pdao" "github.com/rocket-pool/smartnode/shared/services" 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/unstake-rpl.go b/rocketpool/api/node/unstake-rpl.go index a4aedc339..ef489618b 100644 --- a/rocketpool/api/node/unstake-rpl.go +++ b/rocketpool/api/node/unstake-rpl.go @@ -5,7 +5,7 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" - "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/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-legacy-rpl.go b/rocketpool/api/node/withdraw-legacy-rpl.go index 6916355e2..24bc87b49 100644 --- a/rocketpool/api/node/withdraw-legacy-rpl.go +++ b/rocketpool/api/node/withdraw-legacy-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/node/withdraw-rpl.go b/rocketpool/api/node/withdraw-rpl.go index 09ff9c5c1..b5e2b14c7 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 f91b71077..ecd77670a 100644 --- a/rocketpool/api/pdao/propose-settings.go +++ b/rocketpool/api/pdao/propose-settings.go @@ -5,10 +5,10 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" - node131 "github.com/rocket-pool/rocketpool-go/legacy/v1.3.1/node" - "github.com/rocket-pool/rocketpool-go/node" - "github.com/rocket-pool/rocketpool-go/rocketpool" - "github.com/rocket-pool/rocketpool-go/settings/protocol" + node131 "github.com/rocket-pool/smartnode/bindings/legacy/v1.3.1/node" + "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" updateCheck "github.com/rocket-pool/smartnode/shared/services/state" "github.com/rocket-pool/smartnode/shared/types/api" 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 dcb2bf15c..d863cacd5 100644 --- a/rocketpool/api/pdao/status.go +++ b/rocketpool/api/pdao/status.go @@ -12,13 +12,13 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" - node131 "github.com/rocket-pool/rocketpool-go/legacy/v1.3.1/node" + node131 "github.com/rocket-pool/smartnode/bindings/legacy/v1.3.1/node" "github.com/urfave/cli" "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" updateCheck "github.com/rocket-pool/smartnode/shared/services/state" 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/ec_migrate multi.sh b/rocketpool/ec_migrate multi.sh deleted file mode 100755 index f2a058bbb..000000000 --- a/rocketpool/ec_migrate multi.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/sh - -EC_CHAINDATA_DIR=/ethclient -EXTERNAL_DIR=/mnt/external - -# Get the core count -CORE_COUNT=$(grep -c ^processor /proc/cpuinfo) -if [ -z "$CORE_COUNT" ]; then - CORE_COUNT=1 -elif [ "$CORE_COUNT" -lt 1 ]; then - CORE_COUNT=1 -fi - -RSYNC_CMD="xargs -n1 -P${CORE_COUNT} -I% rsync -a --progress %" - -if [ "$EC_MIGRATE_MODE" = "export" ]; then - ls $EC_CHAINDATA_DIR/* | $RSYNC_CMD $EXTERNAL_DIR -elif [ "$EC_MIGRATE_MODE" = "import" ]; then - rm -rf $EC_CHAINDATA_DIR/* - ls $EXTERNAL_DIR/* | $RSYNC_CMD $EC_CHAINDATA_DIR -else - echo "Unknown migrate mode \"$EC_MIGRATE_MODE\"" -fi \ No newline at end of file diff --git a/rocketpool/ec_migrate.sh b/rocketpool/ec_migrate.sh deleted file mode 100755 index ead1ed3f4..000000000 --- a/rocketpool/ec_migrate.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/sh - -EC_CHAINDATA_DIR=/ethclient -EXTERNAL_DIR=/mnt/external - -if [ "$OPERATION" = "size" ]; then - - if [ ! -d $EXTERNAL_DIR ]; then - echo "Source path is not a directory." 1>&2 - exit 1 - else - # Get the space used by the external directory - DIR_SIZE=$(du -s -k $EXTERNAL_DIR | awk -F '\\s*' '{print $1}') - if [ ! -z "$DIR_SIZE" ]; then - # The size will be in KB because of Busybox, so turn it into bytes - expr $DIR_SIZE \* 1024 - else - echo "Failed to get source directory size." 1>&2 - exit 2 - fi - fi - -else - - RSYNC_CMD="rsync -a --progress" - - if [ "$EC_MIGRATE_MODE" = "export" ]; then - $RSYNC_CMD $EC_CHAINDATA_DIR/* $EXTERNAL_DIR - elif [ "$EC_MIGRATE_MODE" = "import" ]; then - rm -rf $EC_CHAINDATA_DIR/* - $RSYNC_CMD $EXTERNAL_DIR/* $EC_CHAINDATA_DIR - else - echo "Unknown migrate mode \"$EC_MIGRATE_MODE\"" - fi - -fi \ No newline at end of file diff --git a/rocketpool/go.mod b/rocketpool/go.mod new file mode 100644 index 000000000..36bf08709 --- /dev/null +++ b/rocketpool/go.mod @@ -0,0 +1,172 @@ +module github.com/rocket-pool/smartnode/rocketpool + +go 1.21 + +require ( + github.com/alessio/shellescape v1.4.1 + github.com/blang/semver/v4 v4.0.0 + github.com/btcsuite/btcd v0.23.4 + github.com/btcsuite/btcd/btcutil v1.1.3 + github.com/docker/docker v23.0.3+incompatible + github.com/dustin/go-humanize v1.0.1 + github.com/ethereum/go-ethereum v1.13.5 + github.com/fatih/color v1.14.1 + github.com/ferranbt/fastssz v0.1.3 + github.com/gdamore/tcell/v2 v2.6.0 + github.com/glendc/go-external-ip v0.1.0 + github.com/go-openapi/errors v0.21.0 + github.com/go-openapi/runtime v0.27.1 + github.com/go-openapi/strfmt v0.22.0 + github.com/go-openapi/swag v0.22.9 + github.com/go-openapi/validate v0.23.0 + github.com/goccy/go-json v0.10.2 + github.com/google/uuid v1.5.0 + github.com/hashicorp/go-version v1.6.0 + github.com/holiman/uint256 v1.2.4 + github.com/ipfs/boxo v0.8.0 + github.com/ipfs/go-cid v0.4.1 + github.com/ipfs/go-datastore v0.6.0 + github.com/klauspost/compress v1.17.6 + github.com/mitchellh/go-homedir v1.1.0 + github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 + github.com/prometheus/client_golang v1.18.0 + 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/sethvargo/go-password v0.2.0 + github.com/shirou/gopsutil/v3 v3.23.1 + github.com/tyler-smith/go-bip39 v1.1.0 + github.com/urfave/cli v1.22.12 + github.com/wealdtech/go-ens/v3 v3.5.5 + github.com/wealdtech/go-eth2-types/v2 v2.8.1-0.20230131115251-b93cf60cee26 + github.com/wealdtech/go-eth2-util v1.8.0 + github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4 v1.3.0 + github.com/wealdtech/go-merkletree v1.0.1-0.20190605192610-2bb163c2ea2a + golang.org/x/crypto v0.19.0 + golang.org/x/sync v0.6.0 + golang.org/x/term v0.17.0 + golang.org/x/text v0.14.0 + google.golang.org/protobuf v1.32.0 + gopkg.in/yaml.v2 v2.4.0 +) + +require ( + github.com/Microsoft/go-winio v0.6.1 // indirect + github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect + github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/bits-and-blooms/bitset v1.11.0 // indirect + github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect + github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // 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/crackcomm/go-gitignore v0.0.0-20170627025303-887ab5e44cc3 // 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.2.0 // indirect + github.com/docker/distribution v2.8.2+incompatible // indirect + github.com/docker/go-connections v0.4.0 // indirect + github.com/docker/go-units v0.5.0 // indirect + github.com/ethereum/c-kzg-4844 v0.4.0 // indirect + github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/gdamore/encoding v1.0.0 // indirect + github.com/go-logr/logr v1.3.0 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect + github.com/go-openapi/analysis v0.22.2 // indirect + github.com/go-openapi/jsonpointer v0.20.2 // indirect + github.com/go-openapi/jsonreference v0.20.4 // indirect + github.com/go-openapi/loads v0.21.5 // indirect + github.com/go-openapi/spec v0.20.14 // indirect + github.com/go-stack/stack v1.8.1 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/gorilla/websocket v1.5.1 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.0.1 // indirect + github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d // indirect + github.com/herumi/bls-eth-go-binary v1.28.1 // indirect + github.com/ipfs/bbloom v0.0.4 // indirect + github.com/ipfs/go-bitfield v1.1.0 // indirect + github.com/ipfs/go-block-format v0.1.2 // indirect + github.com/ipfs/go-ipfs-util v0.0.2 // indirect + github.com/ipfs/go-ipld-cbor v0.0.6 // indirect + github.com/ipfs/go-ipld-format v0.4.0 // indirect + github.com/ipfs/go-ipld-legacy v0.1.1 // indirect + github.com/ipfs/go-log v1.0.5 // indirect + github.com/ipfs/go-log/v2 v2.5.1 // indirect + github.com/ipfs/go-metrics-interface v0.0.1 // indirect + github.com/ipld/go-codec-dagpb v1.6.0 // indirect + github.com/ipld/go-ipld-prime v0.20.0 // indirect + github.com/jbenet/goprocess v0.1.4 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/klauspost/cpuid/v2 v2.2.7 // indirect + github.com/libp2p/go-buffer-pool v0.1.0 // indirect + github.com/lucasb-eyer/go-colorful v1.2.0 // indirect + github.com/mailru/easyjson v0.7.7 // 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/minio/highwayhash v1.0.2 // indirect + github.com/minio/sha256-simd v1.0.1 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/mmcloughlin/addchain v0.4.0 // indirect + github.com/moby/term v0.0.0-20221205130635-1aeaba878587 // indirect + github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect + github.com/morikuni/aec v1.0.0 // indirect + github.com/mr-tron/base58 v1.2.0 // indirect + github.com/multiformats/go-base32 v0.1.0 // indirect + github.com/multiformats/go-base36 v0.2.0 // indirect + github.com/multiformats/go-multibase v0.2.0 // indirect + github.com/multiformats/go-multihash v0.2.3 // indirect + github.com/multiformats/go-varint v0.0.7 // indirect + github.com/oklog/ulid v1.3.1 // indirect + github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/opencontainers/image-spec v1.0.2 // indirect + github.com/opentracing/opentracing-go v1.2.0 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/polydawn/refmt v0.89.0 // indirect + github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect + github.com/prometheus/client_model v0.6.0 // indirect + github.com/prometheus/common v0.47.0 // indirect + github.com/prometheus/procfs v0.12.0 // indirect + github.com/prysmaticlabs/fastssz v0.0.0-20221107182844-78142813af44 // indirect + github.com/prysmaticlabs/gohashtree v0.0.4-beta // indirect + github.com/rivo/uniseg v0.4.4 // indirect + github.com/rs/cors v1.8.2 // indirect + github.com/russross/blackfriday/v2 v2.1.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/supranational/blst v0.3.11 // indirect + github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect + github.com/thomaso-mirodin/intmath v0.0.0-20160323211736-5dc6d854e46e // indirect + github.com/tklauser/go-sysconf v0.3.13 // indirect + github.com/tklauser/numcpus v0.7.0 // indirect + github.com/wealdtech/go-bytesutil v1.2.0 // indirect + github.com/wealdtech/go-multicodec v1.4.0 // indirect + github.com/whyrusleeping/cbor-gen v0.0.0-20230126041949-52956bd4c9aa // indirect + github.com/whyrusleeping/chunker v0.0.0-20181014151217-fe64bd25879f // indirect + github.com/yusufpapurcu/wmi v1.2.3 // indirect + go.mongodb.org/mongo-driver v1.13.1 // indirect + go.opentelemetry.io/otel v1.17.0 // indirect + go.opentelemetry.io/otel/metric v1.17.0 // indirect + go.opentelemetry.io/otel/trace v1.17.0 // indirect + go.uber.org/atomic v1.10.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.27.0 // indirect + golang.org/x/exp v0.0.0-20240213143201-ec583247a57a // indirect + golang.org/x/mod v0.15.0 // indirect + golang.org/x/net v0.21.0 // indirect + golang.org/x/sys v0.17.0 // indirect + golang.org/x/tools v0.18.0 // indirect + golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect + google.golang.org/api v0.45.0 // indirect + google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect + google.golang.org/grpc v1.56.3 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + gotest.tools/v3 v3.4.0 // indirect + lukechampine.com/blake3 v1.2.1 // indirect + rsc.io/tmplfunc v0.0.3 // indirect +) 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/prune_provision.sh b/rocketpool/prune_provision.sh deleted file mode 100755 index 715e61f12..000000000 --- a/rocketpool/prune_provision.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -touch /ethclient/prune.lock \ No newline at end of file diff --git a/rocketpool/rocketpool.go b/rocketpool/rocketpool.go index b0c6723d0..af5ed6b1e 100644 --- a/rocketpool/rocketpool.go +++ b/rocketpool/rocketpool.go @@ -22,7 +22,7 @@ func main() { // Set application info app.Name = "rocketpool" app.Usage = "Rocket Pool service" - app.Version = shared.RocketPoolVersion + app.Version = shared.RocketPoolVersion() app.Copyright = "(c) 2024 Rocket Pool Pty Ltd" // Set application flags 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 5e84089b9..d87bf9973 100644 --- a/rocketpool/watchtower/submit-rewards-tree-stateless.go +++ b/rocketpool/watchtower/submit-rewards-tree-stateless.go @@ -11,14 +11,15 @@ import ( "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/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" @@ -173,6 +174,19 @@ func (t *submitRewardsTree_Stateless) Run(nodeTrusted bool, state *state.Network currentIndex := state.NetworkDetails.RewardIndex currentIndexBig := big.NewInt(0).SetUint64(currentIndex) + // Check if the node has already submitted the rewards for the previous interval + // In case we just had consensus, we want other nodes to also submit + var hasSubmitted bool + if currentIndexBig.Cmp(big.NewInt(0)) > 0 { + hasSubmitted, err := t.hasSubmittedTree(nodeAccount.Address, currentIndexBig.Sub(currentIndexBig, big.NewInt(1))) + if err != nil { + return fmt.Errorf("error checking if Merkle tree submission has already been processed: %w", err) + } + if !hasSubmitted { // didn't participate in the previous consensus. Decrement the index and submit it as a health check + currentIndexBig = currentIndexBig.Sub(currentIndexBig, big.NewInt(1)) + } + } + // Check if rewards generation is already running t.lock.Lock() if t.isRunning { @@ -194,10 +208,6 @@ func (t *submitRewardsTree_Stateless) Run(nodeTrusted bool, state *state.Network } // Return if this node has already submitted the tree for the current interval and there's a file present - hasSubmitted, err := t.hasSubmittedTree(nodeAccount.Address, currentIndexBig) - if err != nil { - return fmt.Errorf("error checking if Merkle tree submission has already been processed: %w", err) - } if hasSubmitted { return nil } @@ -508,7 +518,27 @@ func (t *submitRewardsTree_Stateless) getSnapshotEnd(endTime time.Time, state *s // Check whether the rewards tree for the current interval been submitted by the node func (t *submitRewardsTree_Stateless) hasSubmittedTree(nodeAddress common.Address, index *big.Int) (bool, error) { - indexBuffer := make([]byte, 32) - index.FillBytes(indexBuffer) - return t.rp.RocketStorage.GetBool(nil, crypto.Keccak256Hash([]byte("rewards.snapshot.submitted.node"), nodeAddress.Bytes(), indexBuffer)) + stringArgument, err := abi.NewType("string", "string", nil) + if err != nil { + return false, err + } + addressArgument, err := abi.NewType("address", "address", nil) + if err != nil { + return false, err + } + uint256Argument, err := abi.NewType("uint256", "uint256", nil) + if err != nil { + return false, err + } + + arguments := abi.Arguments{ + {Type: stringArgument}, {Type: addressArgument}, {Type: uint256Argument}, + } + + packedBytes, err := arguments.Pack("rewards.snapshot.submitted.node", nodeAddress, index) + if err != nil { + return false, err + } + + return t.rp.RocketStorage.GetBool(nil, crypto.Keccak256Hash(packedBytes)) } diff --git a/rocketpool/watchtower/submit-rpl-price.go b/rocketpool/watchtower/submit-rpl-price.go index 188778afc..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 lastSubmissionBlockHeader *types.Header - + var lastSubmissionSlotTimestamp uint64 // Check if the node has submitted prices for the latest block if lastSubmissionBlock != 0 { - // Create context with timeout - ctx, cancel := context.WithTimeout(context.Background(), contextTimeout) - defer cancel() - - // Get the lastSubmissionBlock timestamp - lastSubmissionBlockHeader, err = t.rp.Client.HeaderByNumber(ctx, big.NewInt(int64(lastSubmissionBlock))) + eventFound, lastSubmissionEvent, err := network.GetPriceUpdatedEvent(t.rp, lastSubmissionBlock, nil) if err != nil { - t.log.Printlnf("Error getting the latest submission block header: %s", err.Error()) + t.log.Printlnf("Error getting price submission event for block %d", lastSubmissionBlock) return err } - hasSubmittedPastBlock, err = t.hasSubmittedSpecificBlockPrices(nodeAccount.Address, lastSubmissionBlock, lastSubmissionBlockHeader.Time, 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 !eventFound { + t.log.Printlnf("No price submission event found for block %d", lastSubmissionBlock) + } 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 + } } } - 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) @@ -411,7 +412,7 @@ func (t *submitRplPrice) run(state *state.NetworkState) error { // If the node didn't participate in consensus, use the last submission block as the target block as a health check t.log.Printlnf("Node has not participated on the consensus for block %d, using it as the target block.", lastSubmissionBlock) targetBlockNumber = lastSubmissionBlock - nextSubmissionTime = time.Unix(int64(lastSubmissionBlockHeader.Time), 0) + nextSubmissionTime = time.Unix(int64(lastSubmissionSlotTimestamp), 0) } 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 new file mode 100644 index 000000000..c2e5de344 --- /dev/null +++ b/shared/go.mod @@ -0,0 +1,172 @@ +module github.com/rocket-pool/smartnode/shared + +go 1.21 + +require ( + github.com/alessio/shellescape v1.4.1 + github.com/blang/semver/v4 v4.0.0 + github.com/btcsuite/btcd v0.23.4 + github.com/btcsuite/btcd/btcutil v1.1.3 + github.com/docker/docker v23.0.3+incompatible + github.com/dustin/go-humanize v1.0.1 + github.com/ethereum/go-ethereum v1.13.5 + github.com/fatih/color v1.14.1 + github.com/ferranbt/fastssz v0.1.3 + github.com/gdamore/tcell/v2 v2.6.0 + github.com/glendc/go-external-ip v0.1.0 + github.com/go-openapi/errors v0.21.0 + github.com/go-openapi/runtime v0.27.1 + github.com/go-openapi/strfmt v0.22.0 + github.com/go-openapi/swag v0.22.9 + github.com/go-openapi/validate v0.23.0 + github.com/goccy/go-json v0.10.2 + github.com/google/uuid v1.5.0 + github.com/hashicorp/go-version v1.6.0 + github.com/holiman/uint256 v1.2.4 + github.com/ipfs/boxo v0.8.0 + github.com/ipfs/go-cid v0.4.1 + github.com/ipfs/go-datastore v0.6.0 + github.com/klauspost/compress v1.17.6 + github.com/mitchellh/go-homedir v1.1.0 + github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 + github.com/prometheus/client_golang v1.18.0 + 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/sethvargo/go-password v0.2.0 + github.com/shirou/gopsutil/v3 v3.23.1 + github.com/tyler-smith/go-bip39 v1.1.0 + github.com/urfave/cli v1.22.12 + github.com/wealdtech/go-ens/v3 v3.5.5 + github.com/wealdtech/go-eth2-types/v2 v2.8.1-0.20230131115251-b93cf60cee26 + github.com/wealdtech/go-eth2-util v1.8.0 + github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4 v1.3.0 + github.com/wealdtech/go-merkletree v1.0.1-0.20190605192610-2bb163c2ea2a + golang.org/x/crypto v0.19.0 + golang.org/x/sync v0.6.0 + golang.org/x/term v0.17.0 + golang.org/x/text v0.14.0 + google.golang.org/protobuf v1.32.0 + gopkg.in/yaml.v2 v2.4.0 +) + +require ( + github.com/Microsoft/go-winio v0.6.1 // indirect + github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect + github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/bits-and-blooms/bitset v1.11.0 // indirect + github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect + github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // 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/crackcomm/go-gitignore v0.0.0-20170627025303-887ab5e44cc3 // 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.2.0 // indirect + github.com/docker/distribution v2.8.2+incompatible // indirect + github.com/docker/go-connections v0.4.0 // indirect + github.com/docker/go-units v0.5.0 // indirect + github.com/ethereum/c-kzg-4844 v0.4.0 // indirect + github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/gdamore/encoding v1.0.0 // indirect + github.com/go-logr/logr v1.3.0 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect + github.com/go-openapi/analysis v0.22.2 // indirect + github.com/go-openapi/jsonpointer v0.20.2 // indirect + github.com/go-openapi/jsonreference v0.20.4 // indirect + github.com/go-openapi/loads v0.21.5 // indirect + github.com/go-openapi/spec v0.20.14 // indirect + github.com/go-stack/stack v1.8.1 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/gorilla/websocket v1.5.1 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.0.1 // indirect + github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d // indirect + github.com/herumi/bls-eth-go-binary v1.28.1 // indirect + github.com/ipfs/bbloom v0.0.4 // indirect + github.com/ipfs/go-bitfield v1.1.0 // indirect + github.com/ipfs/go-block-format v0.1.2 // indirect + github.com/ipfs/go-ipfs-util v0.0.2 // indirect + github.com/ipfs/go-ipld-cbor v0.0.6 // indirect + github.com/ipfs/go-ipld-format v0.4.0 // indirect + github.com/ipfs/go-ipld-legacy v0.1.1 // indirect + github.com/ipfs/go-log v1.0.5 // indirect + github.com/ipfs/go-log/v2 v2.5.1 // indirect + github.com/ipfs/go-metrics-interface v0.0.1 // indirect + github.com/ipld/go-codec-dagpb v1.6.0 // indirect + github.com/ipld/go-ipld-prime v0.20.0 // indirect + github.com/jbenet/goprocess v0.1.4 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/klauspost/cpuid/v2 v2.2.7 // indirect + github.com/libp2p/go-buffer-pool v0.1.0 // indirect + github.com/lucasb-eyer/go-colorful v1.2.0 // indirect + github.com/mailru/easyjson v0.7.7 // 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/minio/highwayhash v1.0.2 // indirect + github.com/minio/sha256-simd v1.0.1 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/mmcloughlin/addchain v0.4.0 // indirect + github.com/moby/term v0.0.0-20221205130635-1aeaba878587 // indirect + github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect + github.com/morikuni/aec v1.0.0 // indirect + github.com/mr-tron/base58 v1.2.0 // indirect + github.com/multiformats/go-base32 v0.1.0 // indirect + github.com/multiformats/go-base36 v0.2.0 // indirect + github.com/multiformats/go-multibase v0.2.0 // indirect + github.com/multiformats/go-multihash v0.2.3 // indirect + github.com/multiformats/go-varint v0.0.7 // indirect + github.com/oklog/ulid v1.3.1 // indirect + github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/opencontainers/image-spec v1.0.2 // indirect + github.com/opentracing/opentracing-go v1.2.0 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/polydawn/refmt v0.89.0 // indirect + github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect + github.com/prometheus/client_model v0.6.0 // indirect + github.com/prometheus/common v0.47.0 // indirect + github.com/prometheus/procfs v0.12.0 // indirect + github.com/prysmaticlabs/fastssz v0.0.0-20221107182844-78142813af44 // indirect + github.com/prysmaticlabs/gohashtree v0.0.4-beta // indirect + github.com/rivo/uniseg v0.4.4 // indirect + github.com/rs/cors v1.8.2 // indirect + github.com/russross/blackfriday/v2 v2.1.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/supranational/blst v0.3.11 // indirect + github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect + github.com/thomaso-mirodin/intmath v0.0.0-20160323211736-5dc6d854e46e // indirect + github.com/tklauser/go-sysconf v0.3.13 // indirect + github.com/tklauser/numcpus v0.7.0 // indirect + github.com/wealdtech/go-bytesutil v1.2.0 // indirect + github.com/wealdtech/go-multicodec v1.4.0 // indirect + github.com/whyrusleeping/cbor-gen v0.0.0-20230126041949-52956bd4c9aa // indirect + github.com/whyrusleeping/chunker v0.0.0-20181014151217-fe64bd25879f // indirect + github.com/yusufpapurcu/wmi v1.2.3 // indirect + go.mongodb.org/mongo-driver v1.13.1 // indirect + go.opentelemetry.io/otel v1.17.0 // indirect + go.opentelemetry.io/otel/metric v1.17.0 // indirect + go.opentelemetry.io/otel/trace v1.17.0 // indirect + go.uber.org/atomic v1.10.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.27.0 // indirect + golang.org/x/exp v0.0.0-20240213143201-ec583247a57a // indirect + golang.org/x/mod v0.15.0 // indirect + golang.org/x/net v0.21.0 // indirect + golang.org/x/sys v0.17.0 // indirect + golang.org/x/tools v0.18.0 // indirect + golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect + google.golang.org/api v0.45.0 // indirect + google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect + google.golang.org/grpc v1.56.3 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + gotest.tools/v3 v3.4.0 // indirect + lukechampine.com/blake3 v1.2.1 // indirect + rsc.io/tmplfunc v0.0.3 // indirect +) diff --git a/shared/logo.txt b/shared/logo.txt new file mode 100644 index 000000000..377450081 --- /dev/null +++ b/shared/logo.txt @@ -0,0 +1,6 @@ +______ _ _ ______ _ +| ___ \ | | | | | ___ \ | | +| |_/ /___ ___| | _____| |_ | |_/ /__ ___ | | +| // _ \ / __| |/ / _ \ __| | __/ _ \ / _ \| | +| |\ \ (_) | (__| < __/ |_ | | | (_) | (_) | | +\_| \_\___/ \___|_|\_\___|\__| \_| \___/ \___/|_| \ No newline at end of file diff --git a/shared/services/bc-manager.go b/shared/services/bc-manager.go index c9207c898..82966e2bb 100644 --- a/shared/services/bc-manager.go +++ b/shared/services/bc-manager.go @@ -7,13 +7,12 @@ 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" "github.com/rocket-pool/smartnode/shared/types/api" cfgtypes "github.com/rocket-pool/smartnode/shared/types/config" - "github.com/rocket-pool/smartnode/shared/types/eth2" "github.com/rocket-pool/smartnode/shared/utils/log" ) @@ -185,14 +184,25 @@ func (m *BeaconClientManager) GetBeaconHead() (beacon.BeaconHead, error) { } // Get the Beacon State information -func (m *BeaconClientManager) GetBeaconState(slot uint64) (*eth2.BeaconStateDeneb, error) { +func (m *BeaconClientManager) GetBeaconStateSSZ(slot uint64) (*beacon.BeaconStateSSZ, error) { result, err := m.runFunction1(func(client beacon.Client) (interface{}, error) { - return client.GetBeaconState(slot) + return client.GetBeaconStateSSZ(slot) }) if err != nil { return nil, err } - return result.(*eth2.BeaconStateDeneb), nil + return result.(*beacon.BeaconStateSSZ), nil +} + +// Get deneb beacon block by slot +func (m *BeaconClientManager) GetBeaconBlockSSZ(slot uint64) (*beacon.BeaconBlockSSZ, bool, error) { + result1, result2, err := m.runFunction2(func(client beacon.Client) (interface{}, interface{}, error) { + return client.GetBeaconBlockSSZ(slot) + }) + if err != nil { + return nil, false, err + } + return result1.(*beacon.BeaconBlockSSZ), result2.(bool), nil } // Get a validator's status by its index diff --git a/shared/services/beacon/client.go b/shared/services/beacon/client.go index 3c7d210fd..4c277c640 100644 --- a/shared/services/beacon/client.go +++ b/shared/services/beacon/client.go @@ -6,8 +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/shared/types/eth2" + "github.com/rocket-pool/smartnode/bindings/types" ) // API request options @@ -153,6 +152,20 @@ const ( ValidatorState_WithdrawalDone ValidatorState = "withdrawal_done" ) +// SSZ response go into these wrapper types. +// You can stream Data into a deserializer. +// Fork is the consensus version, e.g, "deneb" or "electra" + +type BeaconStateSSZ struct { + Data []byte + Fork string +} + +type BeaconBlockSSZ struct { + Data []byte + Fork string +} + // Beacon client interface type Client interface { GetClientType() (BeaconClientType, error) @@ -163,7 +176,6 @@ type Client interface { GetBeaconBlock(blockId string) (BeaconBlock, bool, error) GetBeaconBlockHeader(blockId string) (BeaconBlockHeader, bool, error) GetBeaconHead() (BeaconHead, error) - GetBeaconState(slot uint64) (*eth2.BeaconStateDeneb, error) GetValidatorStatusByIndex(index string, opts *ValidatorStatusOptions) (ValidatorStatus, error) GetValidatorStatus(pubkey types.ValidatorPubkey, opts *ValidatorStatusOptions) (ValidatorStatus, error) GetValidatorStatuses(pubkeys []types.ValidatorPubkey, opts *ValidatorStatusOptions) (map[types.ValidatorPubkey]ValidatorStatus, error) @@ -178,4 +190,7 @@ type Client interface { GetEth1DataForEth2Block(blockId string) (Eth1Data, bool, error) GetCommitteesForEpoch(epoch *uint64) (Committees, error) ChangeWithdrawalCredentials(validatorIndex string, fromBlsPubkey types.ValidatorPubkey, toExecutionAddress common.Address, signature types.ValidatorSignature) error + + GetBeaconStateSSZ(slot uint64) (*BeaconStateSSZ, error) + GetBeaconBlockSSZ(slot uint64) (*BeaconBlockSSZ, bool, error) } diff --git a/shared/services/beacon/client/std-http-client.go b/shared/services/beacon/client/std-http-client.go index d5bcd69a2..1781be741 100644 --- a/shared/services/beacon/client/std-http-client.go +++ b/shared/services/beacon/client/std-http-client.go @@ -18,21 +18,21 @@ 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" "github.com/rocket-pool/smartnode/shared/services/beacon" - beacontypes "github.com/rocket-pool/smartnode/shared/types/eth2" "github.com/rocket-pool/smartnode/shared/utils/eth2" hexutil "github.com/rocket-pool/smartnode/shared/utils/hex" ) // Config const ( - RequestUrlFormat = "%s%s" - RequestJsonContentType = "application/json" - RequestSSZContentType = "application/octet-stream" + RequestUrlFormat = "%s%s" + RequestJsonContentType = "application/json" + RequestSSZContentType = "application/octet-stream" + ResponseConsensusVersionHeader = "Eth-Consensus-Version" RequestSyncStatusPath = "/eth/v1/node/syncing" RequestEth2ConfigPath = "/eth/v1/config/spec" @@ -977,7 +977,7 @@ func (c *StandardHttpClient) getAttestations(blockId string) (AttestationsRespon // Get the target beacon block func (c *StandardHttpClient) getBeaconBlock(blockId string) (BeaconBlockResponse, bool, error) { - responseBody, status, err := c.getRequest(fmt.Sprintf(RequestBeaconBlockPath, blockId)) + responseBody, status, err := c.getRequest(fmt.Sprintf(RequestBeaconBlockPath, fmt.Sprint(blockId))) if err != nil { return BeaconBlockResponse{}, false, fmt.Errorf("Could not get beacon block data: %w", err) } @@ -995,20 +995,56 @@ func (c *StandardHttpClient) getBeaconBlock(blockId string) (BeaconBlockResponse } // Get the Beacon state for a slot -func (c *StandardHttpClient) GetBeaconState(slot uint64) (*beacontypes.BeaconStateDeneb, error) { - responseBody, status, err := c.getRequestWithContentType(fmt.Sprintf(RequestBeaconStatePath, slot), RequestSSZContentType) +func (c *StandardHttpClient) GetBeaconStateSSZ(slot uint64) (*beacon.BeaconStateSSZ, error) { + response, err := c.sszRequest(fmt.Sprintf(RequestBeaconStatePath, slot)) if err != nil { return nil, fmt.Errorf("Could not get beacon state data: %w", err) } - if status != http.StatusOK { - return nil, fmt.Errorf("Could not get beacon state data: HTTP status %d; response body: '%s'", status, string(responseBody)) + if response.StatusCode != http.StatusOK { + responseBody, err := io.ReadAll(response.Body) + if err != nil { + return nil, fmt.Errorf("Could not get beacon state data: HTTP status %d; response body: '%s'", response.StatusCode, string(responseBody)) + } + return nil, fmt.Errorf("Could not get beacon state data: HTTP status %d", response.StatusCode) + } + + // Slurp the body + body, err := io.ReadAll(response.Body) + if err != nil { + return nil, fmt.Errorf("Could not get beacon state data: %w", err) + } + + return &beacon.BeaconStateSSZ{ + Data: body, + Fork: response.Header.Get(ResponseConsensusVersionHeader), + }, nil +} + +func (c *StandardHttpClient) GetBeaconBlockSSZ(slot uint64) (*beacon.BeaconBlockSSZ, bool, error) { + response, err := c.sszRequest(fmt.Sprintf(RequestBeaconBlockPath, fmt.Sprint(slot))) + if err != nil { + return nil, false, fmt.Errorf("Could not get beacon block data: %w", err) } - var beaconState beacontypes.BeaconStateDeneb - if err := beaconState.UnmarshalSSZ(responseBody); err != nil { - return nil, fmt.Errorf("Could not decode beacon state data: %w", err) + if response.StatusCode == http.StatusNotFound { + return nil, false, nil + } + if response.StatusCode != http.StatusOK { + responseBody, err := io.ReadAll(response.Body) + if err != nil { + return nil, false, fmt.Errorf("Could not get beacon block data: HTTP status %d; response body: '%s'", response.StatusCode, string(responseBody)) + } + return nil, false, fmt.Errorf("Could not get beacon block data: HTTP status %d; response body: '%s'", response.StatusCode, string(responseBody)) } - return &beaconState, nil + // Slurp the body + body, err := io.ReadAll(response.Body) + if err != nil { + return nil, false, fmt.Errorf("Could not get beacon block data: %w", err) + } + return &beacon.BeaconBlockSSZ{ + Data: body, + Fork: response.Header.Get(ResponseConsensusVersionHeader), + }, true, nil } // Get the specified beacon block header @@ -1164,6 +1200,15 @@ func (c *StandardHttpClient) getRequestWithContentType(requestPath string, conte return body, status, nil } +func (c *StandardHttpClient) sszRequest(requestPath string) (*http.Response, error) { + request, err := http.NewRequest("GET", fmt.Sprintf(RequestUrlFormat, c.providerAddress, requestPath), nil) + if err != nil { + return nil, err + } + request.Header.Set("Accept", RequestSSZContentType) + return http.DefaultClient.Do(request) +} + // Make a POST request to the beacon node func (c *StandardHttpClient) postRequest(requestPath string, requestBody interface{}) ([]byte, int, error) { diff --git a/shared/services/config/external-configs.go b/shared/services/config/external-configs.go index f576fc8ab..ef71cd00b 100644 --- a/shared/services/config/external-configs.go +++ b/shared/services/config/external-configs.go @@ -118,6 +118,13 @@ type ExternalTekuConfig struct { DoppelgangerDetection config.Parameter `yaml:"doppelgangerDetection,omitempty"` } +// Type assertions for all ExternalConsensusConfigs +var _ config.ConsensusConfig = &ExternalLighthouseConfig{} +var _ config.ConsensusConfig = &ExternalLodestarConfig{} +var _ config.ConsensusConfig = &ExternalNimbusConfig{} +var _ config.ConsensusConfig = &ExternalPrysmConfig{} +var _ config.ConsensusConfig = &ExternalTekuConfig{} + // Generates a new ExternalExecutionConfig configuration func NewExternalExecutionConfig(cfg *RocketPoolConfig) *ExternalExecutionConfig { return &ExternalExecutionConfig{ diff --git a/shared/services/config/lighthouse-config.go b/shared/services/config/lighthouse-config.go index 53e6a8421..8d7666c40 100644 --- a/shared/services/config/lighthouse-config.go +++ b/shared/services/config/lighthouse-config.go @@ -33,6 +33,9 @@ type LighthouseConfig struct { P2pQuicPort config.Parameter `yaml:"p2pQuicPort,omitempty"` } +// Type assertion for LighthouseConfig +var _ config.ConsensusConfig = &LighthouseConfig{} + // Generates a new Lighthouse configuration func NewLighthouseConfig(cfg *RocketPoolConfig) *LighthouseConfig { return &LighthouseConfig{ diff --git a/shared/services/config/lodestar-config.go b/shared/services/config/lodestar-config.go index 539b5b7b5..9bd28138a 100644 --- a/shared/services/config/lodestar-config.go +++ b/shared/services/config/lodestar-config.go @@ -30,6 +30,9 @@ type LodestarConfig struct { AdditionalVcFlags config.Parameter `yaml:"additionalVcFlags,omitempty"` } +// Type assertion for LodestarConfig +var _ config.ConsensusConfig = &LodestarConfig{} + // Generates a new Lodestar configuration func NewLodestarConfig(cfg *RocketPoolConfig) *LodestarConfig { return &LodestarConfig{ diff --git a/shared/services/config/nimbus-config.go b/shared/services/config/nimbus-config.go index b0b070bd7..9c92d21d9 100644 --- a/shared/services/config/nimbus-config.go +++ b/shared/services/config/nimbus-config.go @@ -45,6 +45,9 @@ type NimbusConfig struct { AdditionalVcFlags config.Parameter `yaml:"additionalVcFlags,omitempty"` } +// Type assertion for NimbusConfig +var _ config.ConsensusConfig = &NimbusConfig{} + // Generates a new Nimbus configuration func NewNimbusConfig(cfg *RocketPoolConfig) *NimbusConfig { return &NimbusConfig{ diff --git a/shared/services/config/prysm-config.go b/shared/services/config/prysm-config.go index 8c4df2b0a..3ebe4e456 100644 --- a/shared/services/config/prysm-config.go +++ b/shared/services/config/prysm-config.go @@ -5,10 +5,10 @@ import ( ) const ( - prysmBnTest string = "rocketpool/prysm:v6.0.1" - prysmBnProd string = "rocketpool/prysm:v6.0.1" - prysmVcTest string = "rocketpool/prysm:v6.0.1" - prysmVcProd string = "rocketpool/prysm:v6.0.1" + prysmBnTest string = "gcr.io/offchainlabs/prysm/beacon-chain:v6.0.2" + prysmBnProd string = "gcr.io/offchainlabs/prysm/beacon-chain:v6.0.2" + prysmVcTest string = "gcr.io/offchainlabs/prysm/validator:v6.0.2" + prysmVcProd string = "gcr.io/offchainlabs/prysm/validator:v6.0.2" defaultPrysmRpcPort uint16 = 5053 defaultPrysmOpenRpcPort string = string(config.RPC_Closed) defaultPrysmMaxPeers uint16 = 70 @@ -46,6 +46,9 @@ type PrysmConfig struct { AdditionalVcFlags config.Parameter `yaml:"additionalVcFlags,omitempty"` } +// Type assertion for PrysmConfig +var _ config.ConsensusConfig = &PrysmConfig{} + // Generates a new Prysm configuration func NewPrysmConfig(cfg *RocketPoolConfig) *PrysmConfig { rpcPortModes := config.PortModes("Allow connections from external hosts. This is safe if you're running your node on your local network. If you're a VPS user, this would expose your node to the internet and could make it vulnerable to MEV/tips theft") diff --git a/shared/services/config/rocket-pool-config.go b/shared/services/config/rocket-pool-config.go index 8c205edc6..179af9cfc 100644 --- a/shared/services/config/rocket-pool-config.go +++ b/shared/services/config/rocket-pool-config.go @@ -762,7 +762,7 @@ func (cfg *RocketPoolConfig) Serialize() map[string]map[string]string { masterMap[rootConfigName] = rootParams masterMap[rootConfigName]["rpDir"] = cfg.RocketPoolDirectory masterMap[rootConfigName]["isNative"] = fmt.Sprint(cfg.IsNativeMode) - masterMap[rootConfigName]["version"] = fmt.Sprintf("v%s", shared.RocketPoolVersion) // Update the version with the current Smartnode version + masterMap[rootConfigName]["version"] = fmt.Sprintf("v%s", shared.RocketPoolVersion()) // Update the version with the current Smartnode version // Serialize the subconfigs for name, subconfig := range cfg.GetSubconfigs() { @@ -782,7 +782,7 @@ func (cfg *RocketPoolConfig) Deserialize(masterMap map[string]map[string]string) // Upgrade the config to the latest version err := migration.UpdateConfig(masterMap) if err != nil { - return fmt.Errorf("error upgrading configuration to v%s: %w", shared.RocketPoolVersion, err) + return fmt.Errorf("error upgrading configuration to v%s: %w", shared.RocketPoolVersion(), err) } // Get the network @@ -925,6 +925,14 @@ func (cfg *RocketPoolConfig) ConsensusClientApiUrl() (string, error) { return cCfg.(config.ExternalConsensusConfig).GetApiUrl(), nil } +func stripScheme(url string) string { + idx := strings.Index(url, "://") + if idx != -1 { + url = url[idx+3:] + } + return url +} + // Used by text/template to format validator.yml func (cfg *RocketPoolConfig) ConsensusClientRpcUrl() (string, error) { // Check if Rescue Node is in-use @@ -947,8 +955,8 @@ func (cfg *RocketPoolConfig) ConsensusClientRpcUrl() (string, error) { return fmt.Sprintf("%s:%d", Eth2ContainerName, cfg.Prysm.RpcPort.Value), nil } - // Use the external RPC endpoint - return cfg.ExternalPrysm.JsonRpcUrl.Value.(string), nil + // Use the external RPC endpoint, but strip any scheme + return stripScheme(cfg.ExternalPrysm.JsonRpcUrl.Value.(string)), nil } // Used by text/template to format validator.yml @@ -976,7 +984,7 @@ func (cfg *RocketPoolConfig) FallbackCcRpcUrl() string { return "" } - return cfg.FallbackPrysm.JsonRpcUrl.Value.(string) + return stripScheme(cfg.FallbackPrysm.JsonRpcUrl.Value.(string)) } // Used by text/template to format validator.yml @@ -1008,7 +1016,7 @@ func (cfg *RocketPoolConfig) CustomGraffiti() (string, error) { func (cfg *RocketPoolConfig) GraffitiPrefix() string { // Graffiti identifier := "" - versionString := fmt.Sprintf("v%s", shared.RocketPoolVersion) + versionString := fmt.Sprintf("v%s", shared.RocketPoolVersion()) if len(versionString) < 8 { var ecInitial string if !cfg.ExecutionClientLocal() { @@ -1046,7 +1054,7 @@ func (cfg *RocketPoolConfig) Graffiti() (string, error) { // Used by text/template to format validator.yml func (cfg *RocketPoolConfig) RocketPoolVersion() string { - return shared.RocketPoolVersion + return shared.RocketPoolVersion() } // Used by text/template to format validator.yml diff --git a/shared/services/config/smartnode-config.go b/shared/services/config/smartnode-config.go index 269336ffc..8974217eb 100644 --- a/shared/services/config/smartnode-config.go +++ b/shared/services/config/smartnode-config.go @@ -12,9 +12,7 @@ import ( // Constants const ( - smartnodeTag string = "rocketpool/smartnode:v" + shared.RocketPoolVersion - pruneProvisionerTag string = "rocketpool/eth1-prune-provision:v0.0.1" - ecMigratorTag string = "rocketpool/ec-migrator:v1.0.0" + smartnodeTagPrefix string = "rocketpool/smartnode:v" NetworkID string = "network" ProjectNameID string = "projectName" SnapshotID string = "rocketpool-dao.eth" @@ -758,15 +756,7 @@ func (cfg *SmartnodeConfig) GetRplTokenAddress() string { } func (cfg *SmartnodeConfig) GetSmartnodeContainerTag() string { - return smartnodeTag -} - -func (config *SmartnodeConfig) GetPruneProvisionerContainerTag() string { - return pruneProvisionerTag -} - -func (cfg *SmartnodeConfig) GetEcMigratorContainerTag() string { - return ecMigratorTag + return smartnodeTagPrefix + shared.RocketPoolVersion() } func (cfg *SmartnodeConfig) GetSnapshotApiDomain() string { @@ -981,7 +971,7 @@ func getNetworkOptions() []config.ParameterOption { }, } - if strings.HasSuffix(shared.RocketPoolVersion, "-dev") { + if strings.HasSuffix(shared.RocketPoolVersion(), "-dev") { options = append(options, config.ParameterOption{ Name: "Devnet", Description: "This is a development network used by Rocket Pool engineers to test new features and contract upgrades before they are promoted to a Testnet for staging. You should not use this network unless invited to do so by the developers.", diff --git a/shared/services/config/teku-config.go b/shared/services/config/teku-config.go index 3f912de00..a00fb67c2 100644 --- a/shared/services/config/teku-config.go +++ b/shared/services/config/teku-config.go @@ -40,6 +40,9 @@ type TekuConfig struct { AdditionalVcFlags config.Parameter `yaml:"additionalVcFlags,omitempty"` } +// Type assertion for TekuConfig +var _ config.ConsensusConfig = &TekuConfig{} + // Generates a new Teku configuration func NewTekuConfig(cfg *RocketPoolConfig) *TekuConfig { return &TekuConfig{ 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 c906c5a8e..e8ae26ab9 100644 --- a/shared/services/megapools.go +++ b/shared/services/megapools.go @@ -12,17 +12,18 @@ 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" + "github.com/rocket-pool/smartnode/shared/types/eth2" "github.com/rocket-pool/smartnode/shared/utils/validator" "github.com/urfave/cli" eth2types "github.com/wealdtech/go-eth2-types/v2" @@ -72,7 +73,7 @@ func GetStakeValidatorInfo(c *cli.Context, wallet *wallet.Wallet, eth2Config bea var block beacon.BeaconBlock const maxAttempts = 10 for attempts := 0; attempts < maxAttempts; attempts++ { - block, _, err := bc.GetBeaconBlock(blockToRequest) + block, _, err = bc.GetBeaconBlock(blockToRequest) if err != nil { return megapool.ValidatorProof{}, err } @@ -87,7 +88,12 @@ func GetStakeValidatorInfo(c *cli.Context, wallet *wallet.Wallet, eth2Config bea } // Get the beacon state for that slot - beaconState, err := bc.GetBeaconState(block.Slot) + beaconStateResponse, err := bc.GetBeaconStateSSZ(block.Slot) + if err != nil { + return megapool.ValidatorProof{}, err + } + + beaconState, err := eth2.NewBeaconState(beaconStateResponse.Data, beaconStateResponse.Fork) if err != nil { return megapool.ValidatorProof{}, err } @@ -148,12 +154,17 @@ func GetWithdrawableEpochProof(c *cli.Context, wallet *wallet.Wallet, eth2Config } // Get the beacon state for that slot - beaconState, err := bc.GetBeaconState(block.Slot) + beaconStateResponse, err := bc.GetBeaconStateSSZ(block.Slot) + if err != nil { + return api.ValidatorWithdrawableEpochProof{}, err + } + + beaconState, err := eth2.NewBeaconState(beaconStateResponse.Data, beaconStateResponse.Fork) if err != nil { return api.ValidatorWithdrawableEpochProof{}, err } - withdrawableEpoch := beaconState.Validators[validatorIndex64].WithdrawableEpoch + withdrawableEpoch := beaconState.GetValidators()[validatorIndex64].WithdrawableEpoch proofBytes, err := beaconState.ValidatorWithdrawableEpochProof(validatorIndex64) if err != nil { 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 0df511b49..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" @@ -108,7 +108,7 @@ func (m *VotingInfoSnapshotManager) CreateVotingInfoSnapshot(blockNumber uint32) } return &VotingInfoSnapshot{ - SmartnodeVersion: shared.RocketPoolVersion, + SmartnodeVersion: shared.RocketPoolVersion(), Network: m.cfg.Smartnode.Network.Value.(cfgtypes.Network), BlockNumber: blockNumber, Info: infos, diff --git a/shared/services/proposals/voting-tree.go b/shared/services/proposals/voting-tree.go index c407ee0e7..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" ) @@ -63,7 +63,7 @@ func CreateTreeFromLeaves(blockNumber uint32, network cfgtypes.Network, leaves [ } return &VotingTree{ - SmartnodeVersion: shared.RocketPoolVersion, + SmartnodeVersion: shared.RocketPoolVersion(), BlockNumber: blockNumber, Network: network, Nodes: nodes, 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/ssz_types/rewards-file-v4.go b/shared/services/rewards/ssz_types/rewards-file-v4.go index 2f4e7c63c..4a551e9f2 100644 --- a/shared/services/rewards/ssz_types/rewards-file-v4.go +++ b/shared/services/rewards/ssz_types/rewards-file-v4.go @@ -68,7 +68,7 @@ type SSZFile_v1 struct { ConsensusStartBlock uint64 `json:"consensusStartBlock,omitempty"` // ConsensusEndBlock is the last non-empty slot of the interval ConsensusEndBlock uint64 `json:"consensusEndBlock"` - // ExecutionBlock is the execution block number included in ConsensusStartBlock + // ExecutionStartBlock is the execution block number included in ConsensusStartBlock ExecutionStartBlock uint64 `json:"executionStartBlock,omitempty"` // ExecutionEndBlock is the execution block number included in ConsensusEndBlock ExecutionEndBlock uint64 `json:"executionEndBlock"` 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 8deda40ae..abcd1bff1 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/assets/assets.go b/shared/services/rocketpool/assets/assets.go new file mode 100644 index 000000000..4e8aa8504 --- /dev/null +++ b/shared/services/rocketpool/assets/assets.go @@ -0,0 +1,33 @@ +package assets + +import ( + "embed" + "io/fs" +) + +// the all: prefix is used because there are hidden files in the install directory +// +//go:embed all:install +var installFS embed.FS + +//go:embed rp-update-tracker +var rpUpdateTrackerFS embed.FS + +//go:embed scripts/install.sh +var installScript []byte + +//go:embed scripts/install-update-tracker.sh +var installUpdateTrackerScript []byte + +type ScriptWithContext struct { + Script []byte + Context fs.FS +} + +func InstallScript() ScriptWithContext { + return ScriptWithContext{Script: installScript, Context: installFS} +} + +func InstallUpdateTrackerScript() ScriptWithContext { + return ScriptWithContext{Script: installUpdateTrackerScript, Context: rpUpdateTrackerFS} +} diff --git a/shared/services/rocketpool/assets/install/addons/gww/.ignoreme b/shared/services/rocketpool/assets/install/addons/gww/.ignoreme new file mode 100644 index 000000000..e69de29bb diff --git a/shared/services/rocketpool/assets/install/alerting/alertmanager.tmpl b/shared/services/rocketpool/assets/install/alerting/alertmanager.tmpl new file mode 100644 index 000000000..dff7d382d --- /dev/null +++ b/shared/services/rocketpool/assets/install/alerting/alertmanager.tmpl @@ -0,0 +1,60 @@ +# Autogenerated - DO NOT MODIFY THIS FILE DIRECTLY + +global: + # ResolveTimeout is the default value used by alertmanager if the alert does + # not include EndsAt, after this time passes it can declare the alert as resolved if it has not been updated. + # This has no impact on alerts from Prometheus, as they always include EndsAt. + # default = 5m + resolve_timeout: 5m + +route: + # The labels by which incoming alerts are grouped together. + group_by: ["alertname"] + # How long to initially wait to send a notification for a group + # of alerts. Allows to wait for an inhibiting alert to arrive or collect + # more initial alerts for the same group. + group_wait: 30s + # How long to wait before sending a notification about new alerts that + # are added to a group of alerts for which an initial notification has + # already been sent. (Usually ~5m or more.) + group_interval: 5m + # How long to wait before sending a notification again if it has already been sent successfully for an alert. + repeat_interval: 4h + routes: + # severity=info: Don't send the follow-up resolved notification. + - match: + severity: info + continue: false + # The notification destination + receiver: "node_operator_no_resolved" + # all other alerts get sent notifications for the initial firing _and_ resolved notifications. + - receiver: "node_operator_default" + #match: We want this to match all alerts (severity=info is frist though so it will stop) + + # The notification destination + receiver: "node_operator_default" + +receivers: + - name: "node_operator_default" + {{- if .DiscordWebhookURL.Value }} + discord_configs: + - webhook_url: "{{ .DiscordWebhookURL.Value }}" + {{- end }} + + - name: "node_operator_no_resolved" + {{- if .DiscordWebhookURL.Value }} + discord_configs: + - webhook_url: "{{ .DiscordWebhookURL.Value }}" + send_resolved: false + {{- end }} + +inhibit_rules: + # Inhibit rules mute a new alert (target) that matches an existing alert (source). + - source_match: + # if the existing alert (source) is severity=critical + severity: "critical" + target_match: + # and the new alert (target) is severity=warning + severity: "warning" + # and the alertname, job, and instance labels have the same value + equal: ["alertname", "job", "instance"] diff --git a/shared/services/rocketpool/assets/install/alerting/rules/default.tmpl b/shared/services/rocketpool/assets/install/alerting/rules/default.tmpl new file mode 100644 index 000000000..78d833302 --- /dev/null +++ b/shared/services/rocketpool/assets/install/alerting/rules/default.tmpl @@ -0,0 +1,131 @@ +# Autogenerated - DO NOT MODIFY THIS FILE DIRECTLY If you want to overwrite some +# of these values with your own customizations, please disable the alerts in the +# TUI and then add your own fule files to the /alerting/rules +# directory. +# +# NOTE: This file uses non-default go template delimiters (triple braces) to avoid +# conflicts with the default delimiters used in the alerting rules. + +groups: + - name: NodeOperator + rules: + {{{- if .AlertEnabled_ClientSyncStatusBeacon.Value }}} + - alert: ClientSyncStatusBeacon + expr: rocketpool_node_sync_progress{client="beacon"} < 1.0 + for: 5m + labels: + severity: critical + annotations: + summary: "The beacon client is not synced" + {{{- end }}} + + {{{- if .AlertEnabled_ClientSyncStatusExecution.Value }}} + - alert: ClientSyncStatusExecution + expr: rocketpool_node_sync_progress{client="execution"} < 1.0 + for: 5m + labels: + severity: critical + annotations: + summary: "The execution client is not synced" + {{{- end }}} + + {{{- if .AlertEnabled_UpcomingSyncCommittee.Value }}} + - alert: UpcomingSyncCommittee + expr: rocketpool_beacon_upcoming_sync_committee > 0 + labels: + severity: warning + job: validator + annotations: + summary: "Your Rocket Pool node is about to become part of a sync committee" + description: | + If you were planning on doing maintenance to your node, **you should wait until the sync committee is over**. Not only are they worth an **extremely** large amount of ETH, but if you miss attestations during a sync committee, you **lose an extremely large amount of ETH** instead! + You should be online as long as possible while you are in a sync committee. + {{{- end }}} + + {{{- if .AlertEnabled_ActiveSyncCommittee.Value }}} + - alert: ActiveSyncCommittee + expr: rocketpool_beacon_active_sync_committee > 0 + labels: + severity: warning + job: validator + annotations: + summary: "Your Rocket Pool node is part of a sync committee" + description: | + If you were planning on doing maintenance to your node, **you should wait until the sync committee is over**. Not only are they worth an **extremely** large amount of ETH, but if you miss attestations during a sync committee, you **lose an extremely large amount of ETH** instead! + You should be online as long as possible while you are in a sync committee. + {{{- end }}} + + {{{- if .AlertEnabled_UpcomingProposal.Value }}} + - alert: UpcomingProposal + expr: rocketpool_beacon_upcoming_proposals > 0 + labels: + severity: warning + job: validator + annotations: + summary: "Your Rocket Pool node is about to propose a block" + description: | + You have {{ $value }} block proposals coming up in the next few minutes. If you were planning on taking your node down for maintenance, you should wait until after the proposals because they're worth a lot of ETH! + {{{- end }}} + + {{{- if .AlertEnabled_RecentProposal.Value }}} + - alert: RecentProposal + expr: rocketpool_beacon_recent_proposals > 0 + # note: 384s = 12s slot time * 32 slots per epoch: This should prevent the alert from refiring during a single epoch + for: 384s + labels: + severity: info + job: validator + annotations: + summary: "Your Rocket Pool node proposed a block" + description: | + Your node proposed {{ $value }} blocks a recent epoch. + {{{- end }}} + + {{{- if .AlertEnabled_LowDiskSpaceWarning.Value }}} + - alert: LowDiskSpaceWarning + expr: node_filesystem_avail_bytes{job="node", mountpoint="/"} / 1024^3 < 200 + labels: + severity: warning + job: node + annotations: + summary: "Device {{ $labels.device }} on instance {{ $labels.instance }} is getting low on disk space" + description: "{{ $labels.instance }} has low disk space. Currently has {{ humanize $value }} GB free." + {{{- end }}} + + {{{- if .AlertEnabled_LowDiskSpaceCritical.Value }}} + - alert: LowDiskSpaceCritical + # NOTE: 50GB taken from PruneFreeSpaceRequired in rocketpool-cli's nethermind pruning (it won't prune below 50GB) + expr: node_filesystem_avail_bytes{job="node", mountpoint="/"} / 1024^3 < 50 + labels: + severity: critical + job: node + annotations: + summary: "Device {{ $labels.device }} on instance {{ $labels.instance }} has critically low disk space" + description: "{{ $labels.instance }} has critically low disk space. Currently has {{ humanize $value }} GB free." + {{{- end }}} + + {{{- if .AlertEnabled_OSUpdatesAvailable.Value }}} + - alert: OSUpdatesAvailable + expr: max(os_upgrades_pending{job="node"}) > 0 + labels: + severity: warning + job: node + annotations: + summary: "Rocket Pool OS Updates Available" + description: | + There are updates available for your OS that haven't been applied yet. You should update your OS. + For more information on updating see the documentation at https://docs.rocketpool.net/guides/node/updates#updating-your-operating-system + {{{- end }}} + + {{{- if .AlertEnabled_RPUpdatesAvailable.Value }}} + - alert: RPUpdatesAvailable + expr: max(rocketpool_version_update{job="node"}) > 0 + labels: + severity: warning + job: node + annotations: + summary: "Rocket Pool Smartnode Update Available" + description: | + There are updates available for the Rocket Pool Smartnode that haven't been applied yet. You should update the smartnode stack. + For more information on updating see the documentation at https://docs.rocketpool.net/guides/node/updates#updating-the-smartnode-stack + {{{- end }}} diff --git a/shared/services/rocketpool/assets/install/dashboards/Rocket Pool Dashboard v1.3.2.json b/shared/services/rocketpool/assets/install/dashboards/Rocket Pool Dashboard v1.3.2.json new file mode 100644 index 000000000..c5a4aedc4 --- /dev/null +++ b/shared/services/rocketpool/assets/install/dashboards/Rocket Pool Dashboard v1.3.2.json @@ -0,0 +1,4601 @@ +{ + "__elements": {}, + "__requires": [ + { + "type": "panel", + "id": "gauge", + "name": "Gauge", + "version": "" + }, + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "9.5.18" + }, + { + "type": "panel", + "id": "piechart", + "name": "Pie chart", + "version": "" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + }, + { + "type": "panel", + "id": "stat", + "name": "Stat", + "version": "" + }, + { + "type": "panel", + "id": "table", + "name": "Table", + "version": "" + }, + { + "type": "panel", + "id": "text", + "name": "Text", + "version": "" + }, + { + "type": "panel", + "id": "timeseries", + "name": "Time series", + "version": "" + } + ], + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "description": "This is the standard dashboard for Rocket Pool node operators.", + "editable": true, + "fiscalYearStartMonth": 0, + "gnetId": 18391, + "graphTooltip": 0, + "id": null, + "links": [], + "liveNow": false, + "panels": [ + { + "gridPos": { + "h": 1, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 163, + "options": { + "code": { + "language": "plaintext", + "showLineNumbers": false, + "showMiniMap": false + }, + "content": "", + "mode": "markdown" + }, + "pluginVersion": "9.5.18", + "title": "Hardware Stats", + "transparent": true, + "type": "text" + }, + { + "gridPos": { + "h": 1, + "w": 9, + "x": 13, + "y": 0 + }, + "id": 165, + "options": { + "code": { + "language": "plaintext", + "showLineNumbers": false, + "showMiniMap": false + }, + "content": "", + "mode": "markdown" + }, + "pluginVersion": "9.5.18", + "title": "Validator Stats", + "transparent": true, + "type": "text" + }, + { + "description": "A count of how long your Beacon Node has been running. If you don't restart it, this is about the same as your total system uptime.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "rgb(212, 212, 212)", + "value": null + } + ] + }, + "unit": "dtdhms" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 2, + "x": 0, + "y": 1 + }, + "id": 40, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "9.5.18", + "targets": [ + { + "exemplar": true, + "expr": "time() - process_start_time_seconds{job=\"eth2\"}", + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Beacon Node Uptime", + "type": "stat" + }, + { + "description": "The temperature of your CPU (you can set this to Tctl or Tdie, whichever you prefer to monitor).\n\nTo get the correct chip and sensor ID, you'll want to run `sensors` from the `lm-sensors` package. See the documentation at https://docs.rocketpool.net/guides/node/grafana.html for more information.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 50 + }, + { + "color": "red", + "value": 65 + } + ] + }, + "unit": "celsius" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 3, + "x": 2, + "y": 1 + }, + "id": 82, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "9.5.18", + "targets": [ + { + "editorMode": "code", + "exemplar": true, + "expr": "node_hwmon_temp_celsius{job=\"node\", chip=\"\", sensor=\"\"}", + "interval": "", + "legendFormat": "", + "range": true, + "refId": "A" + } + ], + "title": "CPU Temp", + "type": "stat" + }, + { + "description": "This will turn red when your system needs to be rebooted because of security updates.\n\nSetting this up is Operating System specific, so it doesn't come ready out of the box; see the documentation at https://docs.rocketpool.net/guides/node/grafana.html for more information.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "0": { + "color": "transparent", + "index": 1, + "text": "No" + }, + "1": { + "color": "red", + "index": 0, + "text": "Reboot Required" + } + }, + "type": "value" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 2, + "x": 5, + "y": 1 + }, + "id": 224, + "options": { + "colorMode": "background", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "9.5.18", + "targets": [ + { + "exemplar": true, + "expr": "os_reboot_required{job=\"node\"}", + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Reboot Needed?", + "type": "stat" + }, + { + "description": "Tracks any updates that are available for your OS or for Rocket Pool but haven't been applied yet. When one of these numbers is higher than 0, it means you should update your system or Rocket Pool accordingly.\n\nSetting this up is Operating System specific, so it doesn't come ready out of the box; see the documentation at https://docs.rocketpool.net/guides/node/grafana.html for more information.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "transparent", + "value": null + }, + { + "color": "red", + "value": 1 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 3, + "x": 7, + "y": 1 + }, + "id": 94, + "options": { + "colorMode": "background", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "9.5.18", + "targets": [ + { + "exemplar": true, + "expr": "max(os_upgrades_pending{job=\"node\"})", + "interval": "", + "legendFormat": "OS", + "refId": "A" + }, + { + "exemplar": true, + "expr": "max(rocketpool_version_update{job=\"node\"})", + "hide": false, + "interval": "", + "legendFormat": "Rocket Pool", + "refId": "B" + } + ], + "title": "Available Updates", + "type": "stat" + }, + { + "description": "How much of your total swap space you're currently using. If you have swap space enabled, you want this to be as low as possible - otherwise, your system is running out of free RAM.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "decimals": 2, + "mappings": [], + "max": 1, + "min": 0, + "thresholds": { + "mode": "percentage", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 20 + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "percentunit" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 2, + "x": 10, + "y": 1 + }, + "id": 92, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true, + "text": {} + }, + "pluginVersion": "9.5.18", + "targets": [ + { + "exemplar": true, + "expr": "((node_memory_SwapTotal_bytes{job=\"node\"} - node_memory_SwapFree_bytes{job=\"node\"}) / node_memory_SwapTotal_bytes{job=\"node\"})", + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Swap Space Usage", + "type": "gauge" + }, + { + "description": "Tracks the activity of all of your validators on the Beacon Chain. This is a cumulative count; it starts at 0 when your node first starts up, and then continuously increments as it goes.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 4, + "x": 13, + "y": 1 + }, + "id": 13, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "9.5.18", + "targets": [ + { + "editorMode": "code", + "exemplar": true, + "expr": "sum(vc_signed_attestations_total{job=\"validator\", status=\"success\"}) OR # Lighthouse\nsum(vc_published_attestations_total{job=\"validator\"}) OR # Lodestar\nsum(beacon_attestations_sent_total{job=\"validator\"}) OR # Nimbus\nsum(validator_successful_attestations{job=\"validator\"}) OR # Prysm\nvalidator_duties_performed_total{job=\"validator\", type=\"attestation\", result=\"success\"} OR # Teku\non() vector(0)", + "hide": false, + "interval": "", + "legendFormat": "Attestations", + "range": true, + "refId": "B" + }, + { + "editorMode": "code", + "exemplar": true, + "expr": "sum(vc_signed_beacon_blocks_total{job=\"validator\", status=\"success\"}) OR # Lighthouse\nsum(vc_block_published_total{job=\"validator\"}) OR # Lodestar\nsum(beacon_blocks_sent_total{job=\"validator\"}) OR # Nimbus\nsum(validator_successful_proposals{job=\"validator\"}) OR # Prysm\nvalidator_duties_performed_total{job=\"validator\", type=\"block\", result=\"success\"} OR # Teku\non() vector(0)", + "interval": "", + "legendFormat": "Proposals", + "range": true, + "refId": "A" + } + ], + "title": "Validator Activity", + "type": "stat" + }, + { + "description": "Tracks the activity of all of your validators on the Beacon Chain.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "stepAfter", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 1, + "links": [], + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 5, + "x": 17, + "y": 1 + }, + "id": 30, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "8.1.1", + "targets": [ + { + "editorMode": "code", + "exemplar": true, + "expr": "(sum(rate(vc_signed_beacon_blocks_total{job=\"validator\", status=\"success\"}[$__rate_interval])) OR # Lighthouse\nsum(rate(vc_block_published_total{job=\"validator\"}[$__rate_interval])) OR # Lodestar\nsum(rate(beacon_blocks_sent_total{job=\"validator\"}[$__rate_interval])) OR # Nimbus\nsum(rate(validator_successful_proposals{job=\"validator\"}[$__rate_interval])) OR # Prysm\nrate(validator_duties_performed{job=\"validator\", type=\"block\", result=\"success\"}[$__rate_interval]) OR # Teku\non() vector(0)) * 45", + "hide": false, + "interval": "", + "legendFormat": "Proposal", + "range": true, + "refId": "A" + }, + { + "editorMode": "code", + "exemplar": true, + "expr": "(sum(rate(vc_signed_attestations_total{job=\"validator\", status=\"success\"}[$__rate_interval])) OR # Lighthouse\nsum(rate(vc_published_attestations_total{job=\"validator\"}[$__rate_interval])) OR # Lodestar\nsum(rate(beacon_attestations_sent_total{job=\"validator\"}[$__rate_interval])) OR # Nimbus\nsum(rate(validator_successful_attestations{job=\"validator\"}[$__rate_interval])) OR # Prysm\nrate(validator_duties_performed{job=\"validator\", type=\"attestation\", result=\"success\"}[$__rate_interval]) OR # Teku\non() vector(0)) * 45", + "hide": false, + "interval": "", + "legendFormat": "Attestation", + "range": true, + "refId": "B" + } + ], + "title": "Validator Activity", + "type": "timeseries" + }, + { + "description": "The CPU usage of your Execution Client and Beacon Node + Validator Client pair, as well as your system total (with respect to all cores on your CPU).", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "max": 100, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "semi-dark-blue", + "value": null + } + ] + }, + "unit": "percent" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "EC" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "dark-yellow", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "System Total" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "dark-green", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 4, + "w": 6, + "x": 0, + "y": 5 + }, + "id": 8, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "vertical", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "9.5.18", + "targets": [ + { + "editorMode": "code", + "expr": "(system_cpu_procload{job=\"geth\"} / scalar(count without(cpu, mode) (node_cpu_seconds_total{mode=\"idle\"}))) OR # Geth\nirate(process_cpu_seconds_total{job=\"eth1\"}[$__rate_interval]) * 100 / scalar(count without(cpu, mode) (node_cpu_seconds_total{mode=\"idle\"})) OR # Besu, Nethermind\nirate(reth_process_cpu_seconds_total{job=\"eth1\"}[$__rate_interval]) * 100 / scalar(count without(cpu, mode) (node_cpu_seconds_total{mode=\"idle\"})) # Reth", + "hide": false, + "legendFormat": "EC", + "range": true, + "refId": "C" + }, + { + "editorMode": "code", + "exemplar": true, + "expr": "(sum(avg (irate(process_cpu_seconds_total{job=\"eth2\"}[$__rate_interval]))) + (sum(avg (irate(process_cpu_seconds_total{job=\"validator\"}[$__rate_interval]))) or vector(0))) * 100 / (count (node_cpu_seconds_total{job=\"node\", mode=\"idle\"}))", + "interval": "", + "legendFormat": "BN+VC", + "range": true, + "refId": "A" + }, + { + "editorMode": "code", + "expr": "(1 - avg (irate(node_cpu_seconds_total{job=\"node\", mode=\"idle\"}[$__rate_interval]))) * 100", + "hide": false, + "legendFormat": "System Total", + "range": true, + "refId": "B" + } + ], + "title": "CPU Usage", + "type": "stat" + }, + { + "description": "The amount of RAM that your Execution Client, Beacon Node and Validator Client combo, and your whole system are using.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "blue", + "value": null + } + ] + }, + "unit": "bytes" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "EC" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "dark-yellow", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "System Total" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "dark-green", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 4, + "w": 6, + "x": 6, + "y": 5 + }, + "id": 260, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "vertical", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "9.5.18", + "targets": [ + { + "editorMode": "code", + "exemplar": true, + "expr": "system_memory_used{job=\"geth\"} OR # Geth\nprocess_resident_memory_bytes{job=\"eth1\"} OR # Besu\nprocess_working_set_bytes{job=\"eth1\"} OR # Nethermind\nreth_process_resident_memory_bytes{job=\"eth1\"} # Reth", + "interval": "", + "legendFormat": "EC", + "range": true, + "refId": "A" + }, + { + "editorMode": "code", + "expr": "sum(process_resident_memory_bytes{job=\"eth2\"}) + (sum(process_resident_memory_bytes{job=\"validator\"}) or vector(0))", + "hide": false, + "legendFormat": "BN+VC", + "range": true, + "refId": "B" + }, + { + "editorMode": "code", + "expr": "node_memory_MemTotal_bytes{job=\"node\"} - node_memory_MemAvailable_bytes{job=\"node\"}", + "hide": false, + "legendFormat": "System Total", + "range": true, + "refId": "C" + } + ], + "title": "RAM Usage", + "type": "stat" + }, + { + "description": "The number of block proposals you have coming up in the next few minutes. If you were planning on taking your node down for maintenance, you should wait until after the proposals because they're worth a lot of ETH!", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "transparent", + "value": null + }, + { + "color": "orange", + "value": 1 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 2, + "x": 13, + "y": 5 + }, + "id": 231, + "options": { + "colorMode": "background", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.5.18", + "targets": [ + { + "exemplar": true, + "expr": "rocketpool_beacon_upcoming_proposals", + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Upcoming Proposals", + "type": "stat" + }, + { + "description": "The number of [sync committees](https://github.com/ethereum/annotated-spec/blob/master/altair/sync-protocol.md#introduction) that you're about to be a part of, and that you're currently a part of.\n\nIf you were planning on doing maintenance to your node, **you should wait until the sync committee is over**. Not only are they worth an **extremely** large amount of ETH, but if you miss attestations during a sync committee, you **lose an extremely large amount of ETH** instead!\n\nYou should be online as long as possible while you are in a sync committee.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "transparent", + "value": null + }, + { + "color": "orange", + "value": 1 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 2, + "x": 15, + "y": 5 + }, + "id": 229, + "options": { + "colorMode": "background", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.5.18", + "targets": [ + { + "exemplar": true, + "expr": "rocketpool_beacon_upcoming_sync_committee", + "interval": "", + "legendFormat": "Upcoming", + "refId": "A" + }, + { + "exemplar": true, + "expr": "rocketpool_beacon_active_sync_committee", + "hide": false, + "interval": "", + "legendFormat": "Active", + "refId": "B" + } + ], + "title": "Sync Committees", + "type": "stat" + }, + { + "description": "This chart shows the overall performance of your total attestations on the Beacon Chain, broken down by the individual attestation components - correct head, correct source, and correct target. If any of these are below 100%, you lost some rewards for submitting a late, partially incorrect, or fully incorrect attestation. For a detailed explanation of each component, take a look at Ben Edgington's explainer: https://eth2book.info/bellatrix/annotated-spec/\n\nThis is updated once per epoch, once your Beacon Node can see how well your node did during the previous epoch.\n\nNOTE: **Lighthouse** and **Teku** currently do not provide source correctness information, only head and target.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "hue", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "max": 1.05, + "min": -0.05, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "percentunit" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 5, + "x": 17, + "y": 7 + }, + "id": 26, + "interval": "", + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "9.3.6", + "targets": [ + { + "editorMode": "code", + "expr": "(irate(validator_monitor_prev_epoch_on_chain_target_attester_hit{job=\"eth2\", validator=\"total\"}[$__rate_interval]) / (irate(validator_monitor_prev_epoch_on_chain_target_attester_hit{job=\"eth2\", validator=\"total\"}[$__rate_interval]) + irate(validator_monitor_prev_epoch_on_chain_target_attester_miss{job=\"eth2\", validator=\"total\"}[$__rate_interval]))) OR # Lighthouse\n(irate(validator_monitor_prev_epoch_on_chain_target_attester_hit_total{job=\"eth2\"}[$__rate_interval]) / (irate(validator_monitor_prev_epoch_on_chain_target_attester_hit_total{job=\"eth2\"}[$__rate_interval]) + irate(validator_monitor_prev_epoch_on_chain_target_attester_miss_total{job=\"eth2\"}[$__rate_interval]))) OR # Nimbus and Lodestar\n(sum(validator_correctly_voted_target{job=\"validator\"}) / count(validator_correctly_voted_target{job=\"validator\"})) OR # Prysm\n(validator_performance_correct_target_count{job=\"eth2\"} / validator_performance_expected_attestations{job=\"eth2\"}) # Teku", + "hide": false, + "legendFormat": "Target", + "range": true, + "refId": "B" + }, + { + "editorMode": "code", + "expr": "irate(validator_monitor_prev_epoch_on_chain_source_attester_hit_total{job=\"eth2\"}[$__rate_interval]) / (irate(validator_monitor_prev_epoch_on_chain_source_attester_hit_total{job=\"eth2\"}[$__rate_interval]) + irate(validator_monitor_prev_epoch_on_chain_source_attester_miss_total{job=\"eth2\"}[$__rate_interval])) OR # Nimbus and Lodestar\n(sum(validator_correctly_voted_source{job=\"validator\"}) / count(validator_correctly_voted_source{job=\"validator\"})) # Prysm", + "hide": false, + "legendFormat": "Source", + "range": true, + "refId": "A" + }, + { + "editorMode": "code", + "expr": "irate(validator_monitor_prev_epoch_on_chain_head_attester_hit{job=\"eth2\", validator=\"total\"}[$__rate_interval]) / (irate(validator_monitor_prev_epoch_on_chain_head_attester_hit{job=\"eth2\", validator=\"total\"}[$__rate_interval]) + irate(validator_monitor_prev_epoch_on_chain_head_attester_miss{job=\"eth2\", validator=\"total\"}[$__rate_interval])) OR # Lighthouse\nirate(validator_monitor_prev_epoch_on_chain_head_attester_hit_total{job=\"eth2\"}[$__rate_interval]) / (irate(validator_monitor_prev_epoch_on_chain_head_attester_hit_total{job=\"eth2\"}[$__rate_interval]) + irate(validator_monitor_prev_epoch_on_chain_head_attester_miss_total{job=\"eth2\"}[$__rate_interval])) OR # Nimbus and Lodestar\n(sum(validator_correctly_voted_head{job=\"validator\"}) / count(validator_correctly_voted_head{job=\"validator\"})) OR # Prysm\n(validator_performance_correct_head_block_count{job=\"eth2\"} / validator_performance_expected_attestations{job=\"eth2\"}) # Teku", + "hide": false, + "legendFormat": "Head", + "range": true, + "refId": "E" + } + ], + "title": "Attestation Accuracy", + "transformations": [], + "type": "timeseries" + }, + { + "description": "Tracks the amount of CPU consumption for the Execution Client, the Beacon Node, and the total system. 100% means all of your CPU core are being used at their full capacity. The orange line indicates how much CPU a single core can provide in your system.\n\n**Note:** The EC's usage is stacked *on top of* the BN+VC, so it shows the aggregated amount of EC + BN + VC.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 60, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "short" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Single Core Cap" + }, + "properties": [ + { + "id": "custom.fillOpacity", + "value": 0 + }, + { + "id": "custom.lineWidth", + "value": 2 + }, + { + "id": "color", + "value": { + "fixedColor": "dark-orange", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "BN+VC" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#3274D9", + "mode": "fixed" + } + }, + { + "id": "custom.stacking", + "value": { + "group": "Clients", + "mode": "normal" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "EC (Stacked)" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "dark-yellow", + "mode": "fixed" + } + }, + { + "id": "custom.stacking", + "value": { + "group": "Clients", + "mode": "normal" + } + } + ] + } + ] + }, + "gridPos": { + "h": 6, + "w": 6, + "x": 0, + "y": 9 + }, + "id": 56, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "8.1.1", + "targets": [ + { + "editorMode": "code", + "exemplar": true, + "expr": "(1 - avg by (instance) (irate(node_cpu_seconds_total{job=\"node\", mode=\"idle\"}[$__rate_interval]))) * 100", + "hide": false, + "interval": "", + "legendFormat": "Total", + "range": true, + "refId": "B" + }, + { + "editorMode": "code", + "exemplar": true, + "expr": "(sum(avg (irate(process_cpu_seconds_total{job=\"eth2\"}[$__rate_interval]))) + (sum(avg (irate(process_cpu_seconds_total{job=\"validator\"}[$__rate_interval]))) or vector(0))) * 100 / (count (node_cpu_seconds_total{job=\"node\", mode=\"idle\"}))", + "instant": false, + "interval": "", + "legendFormat": "BN+VC", + "refId": "A" + }, + { + "editorMode": "code", + "expr": "(system_cpu_procload{job=\"geth\"} / scalar(count without(cpu, mode) (node_cpu_seconds_total{mode=\"idle\"}))) OR # Geth\nirate(process_cpu_seconds_total{job=\"eth1\"}[$__rate_interval]) * 100 / scalar(count without(cpu, mode) (node_cpu_seconds_total{mode=\"idle\"})) OR # Besu, Nethermind\nirate(reth_process_cpu_seconds_total{job=\"eth1\"}[$__rate_interval]) * 100 / scalar(count without(cpu, mode) (node_cpu_seconds_total{mode=\"idle\"})) # Reth", + "hide": false, + "legendFormat": "EC (Stacked)", + "range": true, + "refId": "D" + }, + { + "editorMode": "code", + "exemplar": true, + "expr": "100 / (count (node_cpu_seconds_total{job=\"node\", mode=\"idle\"}))", + "format": "time_series", + "hide": false, + "instant": false, + "interval": "", + "legendFormat": "Single Core Cap", + "refId": "C" + } + ], + "title": "CPU Usage", + "type": "timeseries" + }, + { + "description": "Tracks the total amount of RAM usage by the eth2 client and the entire system, versus how much is available.\n\n**Note:** The EC's usage is stacked *on top of* the BN+VC, so it shows the aggregated amount of EC + BN + VC.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "bits" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Total Used" + }, + "properties": [ + { + "id": "custom.fillOpacity", + "value": 60 + }, + { + "id": "custom.lineWidth", + "value": 2 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "BN+VC" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#3274D9", + "mode": "fixed" + } + }, + { + "id": "custom.fillOpacity", + "value": 60 + }, + { + "id": "custom.lineWidth", + "value": 2 + }, + { + "id": "custom.stacking", + "value": { + "group": "Clients", + "mode": "normal" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "EC (Stacked)" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "dark-yellow", + "mode": "fixed" + } + }, + { + "id": "custom.lineWidth", + "value": 2 + }, + { + "id": "custom.fillOpacity", + "value": 60 + }, + { + "id": "custom.stacking", + "value": { + "group": "Clients", + "mode": "normal" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "RAM Limit" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "dark-orange", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 6, + "w": 6, + "x": 6, + "y": 9 + }, + "id": 60, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "8.1.1", + "targets": [ + { + "editorMode": "code", + "exemplar": true, + "expr": "node_memory_MemTotal_bytes{job=\"node\"} - node_memory_MemAvailable_bytes{job=\"node\"}", + "interval": "", + "legendFormat": "Total Used", + "range": true, + "refId": "A" + }, + { + "editorMode": "code", + "exemplar": true, + "expr": "sum(process_resident_memory_bytes{job=\"eth2\"}) + (sum(process_resident_memory_bytes{job=\"validator\"}) or vector(0))", + "hide": false, + "interval": "", + "legendFormat": "BN+VC", + "range": true, + "refId": "C" + }, + { + "editorMode": "code", + "expr": "system_memory_used{job=\"geth\"} OR # Geth\nprocess_resident_memory_bytes{job=\"eth1\"} OR # Besu\nprocess_working_set_bytes{job=\"eth1\"} OR # Nethermind\nreth_process_resident_memory_bytes{job=\"eth1\"} # Reth", + "hide": false, + "legendFormat": "EC (Stacked)", + "range": true, + "refId": "D" + }, + { + "editorMode": "code", + "exemplar": true, + "expr": "node_memory_MemTotal_bytes{job=\"node\"}", + "hide": false, + "interval": "", + "legendFormat": "RAM Limit", + "range": true, + "refId": "B" + } + ], + "title": "RAM Usage", + "type": "timeseries" + }, + { + "description": "This tells you when your next attestation duty will arrive. Use it to find an optimal time to take your clients down for maintenance so you're back up and running before it hits.\n\n- NOTE: **Lodestar** and **Teku** do not provide this information.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "0": { + "color": "text", + "index": 0, + "text": "Unavailable" + } + }, + "type": "value" + }, + { + "options": { + "match": "null", + "result": { + "color": "text", + "index": 1, + "text": "Unavailable" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 2, + "x": 13, + "y": 9 + }, + "id": 154, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "9.5.18", + "targets": [ + { + "editorMode": "code", + "exemplar": true, + "expr": "((min(vc_attestation_duty_slot{job=\"validator\"}) - scalar(beacon_head_slot{job=\"eth2\"})) * 12) OR # Lighthouse\nmax(next_action_wait{job=\"eth2\"} OR # Nimbus\n(min((validator_next_attestation_slot{job=\"validator\"} - scalar(beacon_slot{job=\"eth2\"})) > 0 - 1) * 12) OR # Prysm\non() vector(0))", + "interval": "", + "legendFormat": "", + "range": true, + "refId": "A" + } + ], + "title": "Next Attestation", + "type": "stat" + }, + { + "description": "How many minipools you currently have running on your node.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "blue", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 2, + "x": 15, + "y": 9 + }, + "id": 233, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.5.18", + "targets": [ + { + "exemplar": true, + "expr": "rocketpool_node_active_minipool_count", + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Your Minipools", + "type": "stat" + }, + { + "gridPos": { + "h": 1, + "w": 9, + "x": 13, + "y": 12 + }, + "id": 234, + "options": { + "code": { + "language": "plaintext", + "showLineNumbers": false, + "showMiniMap": false + }, + "content": "", + "mode": "markdown" + }, + "pluginVersion": "9.5.18", + "title": "Connectivity", + "transparent": true, + "type": "text" + }, + { + "description": "How many peers your EC is currently connected to.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "max": 49, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "red", + "value": null + }, + { + "color": "#EAB839", + "value": 12 + }, + { + "color": "green", + "value": 20 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 2, + "x": 13, + "y": 13 + }, + "id": 237, + "links": [], + "maxDataPoints": 100, + "options": { + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true, + "text": {} + }, + "pluginVersion": "9.5.18", + "targets": [ + { + "editorMode": "code", + "exemplar": true, + "expr": "p2p_peers{job=\"geth\"} OR # Geth\nethereum_peer_count{job=\"eth1\"} OR # Nethermind, Besu\nreth_network_connected_peers{job=\"eth1\"} # Reth", + "interval": "", + "legendFormat": "", + "range": true, + "refId": "A" + } + ], + "title": "EC Peers", + "type": "gauge" + }, + { + "description": "How many peers your Beacon Node is currently connected to on the Beacon Chain.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "max": 160, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "red", + "value": null + }, + { + "color": "#EAB839", + "value": 20 + }, + { + "color": "green", + "value": 40 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 2, + "x": 15, + "y": 13 + }, + "id": 12, + "links": [], + "maxDataPoints": 100, + "options": { + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true, + "text": {} + }, + "pluginVersion": "9.5.18", + "targets": [ + { + "editorMode": "code", + "exemplar": true, + "expr": "sum(sync_peers_per_status{job=\"eth2\", sync_status=~\"Synced|Advanced\"}) OR # Lighthouse\nlodestar_peers_sync_count{job=\"eth2\"} OR # Lodestar\nnbc_peers{job=\"eth2\"} OR # Nimbus\np2p_peer_count{job=\"eth2\", state=\"Connected\"} OR # Prysm\nbeacon_peer_count{job=\"eth2\"} # Teku", + "interval": "", + "legendFormat": "", + "range": true, + "refId": "A" + } + ], + "title": "BN Peers", + "type": "gauge" + }, + { + "description": "How many peers Execution Client and Beacon Node are currently connected to for blockchain traffic.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "links": [], + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 5, + "x": 17, + "y": 13 + }, + "id": 51, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "8.1.1", + "targets": [ + { + "editorMode": "code", + "exemplar": true, + "expr": "sum(sync_peers_per_status{job=\"eth2\", sync_status=~\"Synced|Advanced\"}) OR # Lighthouse\nlodestar_peers_sync_count{job=\"eth2\"} OR # Lodestar\nnbc_peers{job=\"eth2\"} OR # Nimbus\np2p_peer_count{job=\"eth2\", state=\"Connected\"} OR # Prysm\nbeacon_peer_count{job=\"eth2\"} # Teku", + "interval": "", + "legendFormat": "BN", + "range": true, + "refId": "B" + }, + { + "editorMode": "code", + "expr": "p2p_peers{job=\"geth\"} OR # Geth\nethereum_peer_count{job=\"eth1\"} OR # Nethermind, Besu\nreth_network_connected_peers{job=\"eth1\"} # Reth", + "hide": false, + "legendFormat": "EC", + "range": true, + "refId": "A" + } + ], + "title": "Peers", + "type": "timeseries" + }, + { + "description": "This tracks how long it takes between one of your processes trying to read from / write to your SSD, and when that operation is actually performed. Think of this like \"how long SSD reads and writes have to wait in a queue before completing\". The longer this time, the longer it takes for your node to perform its Beacon Chain duties.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 70, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 0, + "y": 15 + }, + "id": 76, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": false + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "8.1.1", + "targets": [ + { + "exemplar": true, + "expr": "irate(node_pressure_io_waiting_seconds_total{job=\"node\"}[$__rate_interval])", + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "I/O Wait Time", + "type": "timeseries" + }, + { + "description": "The disk space used by your primary (Operating System) hard drive. Change the `device` option in this query to be your machine's hard drive.\n\nSee the documentation at https://docs.rocketpool.net/guides/node/grafana.html for more information.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 1, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 0.75 + }, + { + "color": "red", + "value": 0.9 + } + ] + }, + "unit": "percentunit" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 4, + "x": 6, + "y": 15 + }, + "id": 88, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true, + "text": {} + }, + "pluginVersion": "9.5.18", + "targets": [ + { + "editorMode": "code", + "exemplar": true, + "expr": "(node_filesystem_size_bytes{job=\"node\", mountpoint=\"/\"} - node_filesystem_avail_bytes{job=\"node\", mountpoint=\"/\"}) / node_filesystem_size_bytes{job=\"node\", mountpoint=\"/\"}", + "interval": "", + "legendFormat": "OS", + "range": true, + "refId": "A" + }, + { + "editorMode": "code", + "exemplar": true, + "expr": "(node_filesystem_size_bytes{job=\"node\", device=\"\"} - node_filesystem_avail_bytes{job=\"node\", device=\"\"}) / node_filesystem_size_bytes{job=\"node\", device=\"\"}", + "hide": false, + "interval": "", + "legendFormat": "Disk 2", + "range": true, + "refId": "B" + } + ], + "title": "Disk Space Used", + "type": "gauge" + }, + { + "description": "The temperature of your SSD.\n\nTo get the correct chip and sensor ID, you'll want to run `sensors` from the `lm-sensors` package. See the documentation at https://docs.rocketpool.net/guides/node/grafana.html for more information.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 50 + }, + { + "color": "red", + "value": 65 + } + ] + }, + "unit": "celsius" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 2, + "x": 10, + "y": 15 + }, + "id": 263, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "9.5.18", + "targets": [ + { + "editorMode": "code", + "exemplar": true, + "expr": "node_hwmon_temp_celsius{job=\"node\", chip=\"\", sensor=\"\"}", + "format": "time_series", + "interval": "", + "legendFormat": "OS", + "range": true, + "refId": "A" + }, + { + "editorMode": "code", + "exemplar": true, + "expr": "node_hwmon_temp_celsius{job=\"node\", chip=\"\", sensor=\"\"}", + "hide": false, + "interval": "", + "legendFormat": "Disk 2", + "range": true, + "refId": "B" + } + ], + "title": "Disk Temp", + "type": "stat" + }, + { + "description": "", + "gridPos": { + "h": 1, + "w": 9, + "x": 13, + "y": 18 + }, + "id": 258, + "options": { + "code": { + "language": "plaintext", + "showLineNumbers": false, + "showMiniMap": false + }, + "content": "", + "mode": "markdown" + }, + "pluginVersion": "9.5.18", + "transparent": true, + "type": "text" + }, + { + "gridPos": { + "h": 1, + "w": 9, + "x": 13, + "y": 19 + }, + "id": 212, + "options": { + "code": { + "language": "plaintext", + "showLineNumbers": false, + "showMiniMap": false + }, + "content": "", + "mode": "markdown" + }, + "pluginVersion": "9.5.18", + "title": "Beacon Chain Rewards (Updates Every 5 Minutes)", + "transparent": true, + "type": "text" + }, + { + "description": "The average read/write latency of your SSD. The lower it is, the faster your machine can process and respond to Beacon Chain activities like attesting. Change the `device` setting in the queries to be the SSD you want to track (typically the one with your chain data on it).\n\nSee the documentation at https://docs.rocketpool.net/guides/node/grafana.html for more information.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 70, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 6, + "x": 0, + "y": 20 + }, + "id": 70, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "8.1.1", + "targets": [ + { + "editorMode": "code", + "exemplar": true, + "expr": "irate(node_disk_write_time_seconds_total{job=\"node\", device=\"\"}[$__rate_interval]) / irate(node_disk_writes_completed_total{job=\"node\", device=\"\"}[$__rate_interval])", + "hide": false, + "interval": "", + "legendFormat": "OS Write", + "range": true, + "refId": "A" + }, + { + "editorMode": "code", + "exemplar": true, + "expr": "irate(node_disk_read_time_seconds_total{job=\"node\", device=\"\"}[$__rate_interval]) / irate(node_disk_reads_completed_total{job=\"node\", device=\"\"}[$__rate_interval])", + "interval": "", + "legendFormat": "OS Read", + "range": true, + "refId": "B" + }, + { + "editorMode": "code", + "exemplar": true, + "expr": "irate(node_disk_write_time_seconds_total{job=\"node\", device=\"\"}[$__rate_interval]) / irate(node_disk_writes_completed_total{job=\"node\", device=\"\"}[$__rate_interval])", + "hide": false, + "interval": "", + "legendFormat": "Disk 2 Write", + "range": true, + "refId": "C" + }, + { + "editorMode": "code", + "exemplar": true, + "expr": "irate(node_disk_read_time_seconds_total{job=\"node\", device=\"\"}[$__rate_interval]) / irate(node_disk_reads_completed_total{job=\"node\", device=\"\"}[$__rate_interval])", + "hide": false, + "interval": "", + "legendFormat": "Disk 2 Read", + "range": true, + "refId": "D" + } + ], + "title": "SSD Latency", + "type": "timeseries" + }, + { + "description": "A chart showing the network I/O used by your main network adapter over time. To make it work, change the `device` setting in the queries to match the name of the network adapter you want to track (you can use `ifconfig` to get a list of network adapters and find the right one). \n\nSee the documentation at https://docs.rocketpool.net/guides/node/grafana.html for more information.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 70, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "bits" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 4, + "x": 6, + "y": 20 + }, + "id": 80, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "8.1.1", + "targets": [ + { + "editorMode": "code", + "exemplar": true, + "expr": "irate(node_network_transmit_bytes_total{job=\"node\", device=\"\"}[$__rate_interval])", + "hide": false, + "interval": "", + "legendFormat": "Tx", + "range": true, + "refId": "B" + }, + { + "editorMode": "code", + "exemplar": true, + "expr": "irate(node_network_receive_bytes_total{job=\"node\", device=\"\"}[$__rate_interval])", + "interval": "", + "legendFormat": "Rx", + "range": true, + "refId": "A" + } + ], + "title": "Network Usage", + "type": "timeseries" + }, + { + "description": "The total network I/O that has gone through your main network adapter. To make it work, change the `device` setting in the queries to match the name of the network adapter you want to track (you can use `ifconfig` to get a list of network adapters and find the right one). \n\nSee the documentation at https://docs.rocketpool.net/guides/node/grafana.html for more information.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 2, + "x": 10, + "y": 20 + }, + "id": 74, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "9.5.18", + "targets": [ + { + "editorMode": "code", + "exemplar": true, + "expr": "node_network_transmit_bytes_total{job=\"node\", device=\"\"}", + "hide": false, + "interval": "", + "legendFormat": "Tx", + "range": true, + "refId": "B" + }, + { + "editorMode": "code", + "exemplar": true, + "expr": "node_network_receive_bytes_total{job=\"node\", device=\"\"}", + "interval": "", + "legendFormat": "Rx", + "range": true, + "refId": "A" + } + ], + "title": "Total Net I/O", + "type": "stat" + }, + { + "description": "The total ETH balance of all of your Rocket Pool validators on the Beacon Chain. This includes both ETH belonging to you and ETH belonging to the pool stakers you used to make your minipools.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "decimals": 2, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "blue", + "value": null + } + ] + }, + "unit": "ETH" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 4, + "x": 13, + "y": 20 + }, + "id": 72, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "9.5.18", + "targets": [ + { + "exemplar": true, + "expr": "rocketpool_node_beacon_balance{job=\"rocketpool\"}", + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Beacon Balance", + "type": "stat" + }, + { + "description": "This is how much ETH is currently available for distribution in all of your minipool contracts (on the execution layer). Note that if any of them have a refund, this shows the total balance **minus** that refund amount - in other words, it shows whatever's left over after your refund.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "decimals": 2, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "blue", + "value": null + } + ] + }, + "unit": "ETH" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 3, + "x": 17, + "y": 20 + }, + "id": 210, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "9.5.18", + "targets": [ + { + "editorMode": "code", + "exemplar": true, + "expr": "rocketpool_node_minipool_balance{job=\"rocketpool\"}", + "interval": "", + "legendFormat": "", + "range": true, + "refId": "A" + } + ], + "title": "Minipool Balance (EL)", + "type": "stat" + }, + { + "description": "A collection of other ETH rewards you can claim (or have claimed).\n\n- **Unclaimed ETH (SP)**: ETH you've earned from the Smoothing Pool that you haven't claimed yet.\n- **Claimed ETH (SP)**: ETH from the Smoothing Pool you've already claimed in the past.\n- **Total Refund**: ETH that belongs to you from special operations like bond reduction or solo staker migration. When you claim this, it goes directly to you - it isn't split with the pool stakers.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "decimals": 2, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "transparent", + "value": null + }, + { + "color": "green", + "value": 0.00001 + } + ] + }, + "unit": "ETH" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Claimed ETH (SP)" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "transparent", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 9, + "w": 2, + "x": 20, + "y": 20 + }, + "id": 256, + "options": { + "colorMode": "background", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "value_and_name" + }, + "pluginVersion": "9.5.18", + "targets": [ + { + "editorMode": "code", + "expr": "rocketpool_node_unclaimed_eth_rewards{job=\"rocketpool\"}", + "hide": false, + "legendFormat": "Unclaimed ETH (SP)", + "range": true, + "refId": "B" + }, + { + "editorMode": "code", + "exemplar": true, + "expr": "rocketpool_node_claimed_eth_rewards{job=\"rocketpool\"}", + "interval": "", + "legendFormat": "Claimed ETH (SP)", + "range": true, + "refId": "A" + }, + { + "editorMode": "code", + "expr": "rocketpool_node_refund_balance{job=\"rocketpool\"}", + "hide": false, + "legendFormat": "Total Refund", + "range": true, + "refId": "C" + } + ], + "title": "Other ETH Rewards", + "type": "stat" + }, + { + "description": "This is how much ETH you will receive if you exit all of your validators on the Beacon Chain; it's your share of the total balance for each Rocket Pool minipool.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "decimals": 4, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "ETH" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 4, + "x": 13, + "y": 23 + }, + "id": 204, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "9.5.18", + "targets": [ + { + "editorMode": "code", + "exemplar": true, + "expr": "rocketpool_node_beacon_share{job=\"rocketpool\"}", + "interval": "", + "legendFormat": "", + "range": true, + "refId": "A" + } + ], + "title": "Your Beacon Chain Share", + "type": "stat" + }, + { + "description": "The total amount of ETH that belongs to you across the EL balances of all of your minipool contracts (minus any refunds you may have waiting).", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "decimals": 4, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "ETH" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 3, + "x": 17, + "y": 23 + }, + "id": 152, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.5.18", + "targets": [ + { + "editorMode": "code", + "exemplar": true, + "expr": "rocketpool_node_minipool_share{job=\"rocketpool\"}", + "hide": false, + "interval": "", + "legendFormat": "annualized daily return", + "range": true, + "refId": "B" + } + ], + "title": "Your Minipool (EL) Share", + "type": "stat" + }, + { + "description": "", + "gridPos": { + "h": 1, + "w": 12, + "x": 0, + "y": 26 + }, + "id": 158, + "options": { + "code": { + "language": "plaintext", + "showLineNumbers": false, + "showMiniMap": false + }, + "content": "", + "mode": "markdown" + }, + "pluginVersion": "9.5.18", + "title": "Rocket Pool Network Stats (Updates Every 5 Minutes)", + "transparent": true, + "type": "text" + }, + { + "description": "The total amount of ETH locked on the Rocket Pool network, including staked RPL (but not including Beacon Chain rewards).", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "decimals": 2, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "blue", + "value": null + } + ] + }, + "unit": "locale" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 3, + "x": 0, + "y": 27 + }, + "id": 156, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "9.5.18", + "targets": [ + { + "exemplar": true, + "expr": "rocketpool_demand_deposit_pool_balance{job=\"rocketpool\"} + rocketpool_demand_total_minipool_capacity{job=\"rocketpool\"} + rocketpool_supply_active_minipools{job=\"rocketpool\"} * 32 + rocketpool_rpl_total_value_staked{job=\"rocketpool\"} * rocketpool_rpl_rpl_price{job=\"rocketpool\"}", + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Total ETH Locked", + "type": "stat" + }, + { + "description": "The total amount of RPL staked across all node operators in the Rocket Pool network.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "decimals": 2, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "orange", + "value": null + } + ] + }, + "unit": "locale" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 3, + "x": 3, + "y": 27 + }, + "id": 214, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "9.5.18", + "targets": [ + { + "exemplar": true, + "expr": "rocketpool_rpl_total_value_staked{job=\"rocketpool\"}", + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Total RPL Staked", + "type": "stat" + }, + { + "description": "The total *effective* RPL stake across all node operators on the Rocket Pool network. This accounts for the 150% collateral cap per minipool. If this number is lower than the Total RPL Staked, then some node operators have gone above 150%.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "decimals": 2, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "orange", + "value": null + } + ] + }, + "unit": "locale" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 3, + "x": 6, + "y": 27 + }, + "id": 182, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "9.5.18", + "targets": [ + { + "exemplar": true, + "expr": "rocketpool_rpl_total_effective_staked{job=\"rocketpool\"}", + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Total Effective RPL Staked", + "type": "stat" + }, + { + "description": "The current balance of the Smoothing Pool. This is what will get distributed to each node operator opted into the Smoothing Pool at the end of each rewards interval.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "decimals": 4, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "ETH" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 3, + "x": 9, + "y": 27 + }, + "id": 250, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "9.5.18", + "targets": [ + { + "editorMode": "code", + "exemplar": true, + "expr": "rocketpool_smoothing_pool_eth_balance", + "interval": "", + "legendFormat": "", + "range": true, + "refId": "A" + } + ], + "title": "Smoothing Pool Balance", + "type": "stat" + }, + { + "gridPos": { + "h": 1, + "w": 9, + "x": 13, + "y": 29 + }, + "id": 186, + "options": { + "code": { + "language": "plaintext", + "showLineNumbers": false, + "showMiniMap": false + }, + "content": "", + "mode": "markdown" + }, + "pluginVersion": "9.5.18", + "title": "RPL Rewards (Updates Every 5 Minutes)", + "transparent": true, + "type": "text" + }, + { + "description": "The time of the next RPL rewards checkpoint. If you have an effective RPL stake greater than zero, you should receive rewards here (unless it's your first checkpoint, in which case you'll need to wait a bit longer).", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "text", + "value": null + } + ] + }, + "unit": "dateTimeAsLocalNoDateIfToday" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 5, + "x": 13, + "y": 30 + }, + "id": 218, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "9.5.18", + "targets": [ + { + "exemplar": true, + "expr": "rocketpool_rpl_checkpoint_time{job=\"rocketpool\"}", + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Next RPL Rewards Checkpoint", + "type": "stat" + }, + { + "description": "This tracks how long you have until the next RPL rewards checkpoint.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "text", + "value": null + } + ] + }, + "unit": "dateTimeFromNow" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 4, + "x": 18, + "y": 30 + }, + "id": 216, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "9.5.18", + "targets": [ + { + "exemplar": true, + "expr": "rocketpool_rpl_checkpoint_time{job=\"rocketpool\"}", + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Next RPL Rewards Checkpoint", + "type": "stat" + }, + { + "description": "The amount of ETH that 1 rETH is worth.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "decimals": 6, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "purple", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 3, + "x": 0, + "y": 32 + }, + "id": 178, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "9.5.18", + "targets": [ + { + "exemplar": true, + "expr": "rocketpool_performance_eth_reth_exchange_rate{job=\"rocketpool\"}", + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "rETH Price (ETH / rETH)", + "type": "stat" + }, + { + "description": "The amount of ETH that 1 RPL is worth, as reported by the Oracle DAO. This updates somewhat infrequently, so it's expected if it falls out of sync with other price watchers.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "decimals": 6, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "orange", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 3, + "x": 3, + "y": 32 + }, + "id": 180, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "9.5.18", + "targets": [ + { + "exemplar": true, + "expr": "rocketpool_rpl_rpl_price{job=\"rocketpool\"}", + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "RPL Price (ETH / RPL)", + "type": "stat" + }, + { + "description": "The amount of ETH currently in the staking pool, waiting to be used by node operators to create new minipools.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 18000, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 18000 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 3, + "x": 6, + "y": 32 + }, + "id": 160, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true, + "text": {} + }, + "pluginVersion": "9.5.18", + "targets": [ + { + "exemplar": true, + "expr": "rocketpool_demand_deposit_pool_balance{job=\"rocketpool\"}", + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Staking Pool Balance", + "type": "gauge" + }, + { + "description": "The number of minipools that are currently in the queue, waiting for ETH to be assigned to them from the staking pool.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "blue", + "value": null + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 3, + "x": 9, + "y": 32 + }, + "id": 208, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.5.18", + "targets": [ + { + "editorMode": "code", + "exemplar": true, + "expr": "rocketpool_demand_queue_length{job=\"rocketpool\"}", + "interval": "", + "legendFormat": "", + "range": true, + "refId": "A" + } + ], + "title": "Minipools in Queue", + "type": "stat" + }, + { + "description": "This shows various levels of RPL stake you have:\n\n- **Total**: the total amount of RPL you have staked.\n- **Effective**: the amount of your staked RPL that is being put to use, accounting for the 10% borrowed minimum and 150% bonded maximum limits.\n- **Rewardable**: the effective amount of RPL that is eligible for earning RPL rewards at the end of each rewards period. This is based on the status of your validators on the Beacon Chain; validators that haven't been activated yet or validators that have been exited are not eligible for rewards.\n\n**Note**: this takes any *pending bond reductions* you may have into account.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "decimals": 2, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "orange", + "value": null + } + ] + }, + "unit": "RPL" + }, + "overrides": [] + }, + "gridPos": { + "h": 13, + "w": 3, + "x": 13, + "y": 34 + }, + "id": 190, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "9.5.18", + "targets": [ + { + "editorMode": "code", + "exemplar": true, + "expr": "rocketpool_node_total_staked_rpl{job=\"rocketpool\"}", + "interval": "", + "legendFormat": "Total", + "range": true, + "refId": "A" + }, + { + "editorMode": "code", + "expr": "rocketpool_node_effective_staked_rpl{job=\"rocketpool\"}", + "hide": false, + "legendFormat": "Effective", + "range": true, + "refId": "B" + }, + { + "editorMode": "code", + "expr": "rocketpool_node_rewardable_staked_rpl{job=\"rocketpool\"}", + "hide": false, + "legendFormat": "Rewardable", + "range": true, + "refId": "C" + } + ], + "title": "RPL Staked", + "type": "stat" + }, + { + "description": "Your total staked RPL collateral levels. This shows your collateral relative to the amount of ETH you have *borrowed* from the staking pool to complete your validators, and the amount of ETH you have *bonded* with your own funds.\n\nIf you fall below 10% of the *borrowed* ETH, you won't be able to claim your rewards at the next checkpoint until you get back to 10%.\n\nIf you go over 150% of the *bonded* ETH, you'll only be rewarded for the first 150% of your stake.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "decimals": 2, + "mappings": [], + "max": 1.5, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "red", + "value": null + }, + { + "color": "green", + "value": 0.1 + }, + { + "color": "red", + "value": 1.5 + } + ] + }, + "unit": "percentunit" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 16, + "y": 34 + }, + "id": 196, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true, + "text": {} + }, + "pluginVersion": "9.5.18", + "targets": [ + { + "editorMode": "code", + "exemplar": true, + "expr": "rocketpool_node_borrowed_collateral_ratio{job=\"rocketpool\"}", + "interval": "", + "legendFormat": "Borrowed", + "range": true, + "refId": "A" + }, + { + "editorMode": "code", + "expr": "rocketpool_node_bonded_collateral_ratio{job=\"rocketpool\"}", + "hide": false, + "legendFormat": "Bonded", + "range": true, + "refId": "B" + } + ], + "title": "RPL Collateral", + "type": "gauge" + }, + { + "description": "The total number of nodes that have registered on the Rocket Pool network.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 2, + "x": 0, + "y": 36 + }, + "id": 172, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "value" + }, + "pluginVersion": "9.5.18", + "targets": [ + { + "exemplar": true, + "expr": "rocketpool_supply_node_count{job=\"rocketpool\"}", + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Node Count", + "type": "stat" + }, + { + "description": "A breakdown of the counts of each minipool on the Rocket Pool network by status:\n\n- **Initialized**: waiting for a deposit still\n- **Prelaunch:** deposits are done, waiting to be staked by the node operator's `rocketpool_node` container \n- **Staking:** deposited, validator created, and active (or pending) on the Beacon Chain\n- **Dissolved:** staking failed, funds returned to the node operator and staking pool\n- **Withdrawable:** exited from the Beacon Chain, waiting for rewards to be withdrawn to the minipool\n- **Finalized:** exited, withdrawn from, and essentially closed (inactive)", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + } + }, + "mappings": [] + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "staking" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "green", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "initialized" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "blue", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "finalized" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "purple", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "dissolved" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "red", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "prelaunch" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "yellow", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "withdrawable" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "dark-orange", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 6, + "w": 4, + "x": 2, + "y": 36 + }, + "id": 176, + "options": { + "legend": { + "displayMode": "table", + "placement": "right", + "showLegend": true, + "values": [ + "value" + ] + }, + "pieType": "donut", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "exemplar": true, + "expr": "rocketpool_supply_minipool_count{job=\"rocketpool\"}", + "format": "time_series", + "instant": true, + "interval": "", + "legendFormat": "{{status}}", + "refId": "A" + } + ], + "title": "Minipools by Status", + "type": "piechart" + }, + { + "description": "The relative amount of ETH that was deposited into the staking pool by rETH stakers, which was then used by node operators to create new minipools with validators on the Beacon Chain. The closer this number is to 100%, the more efficiently the Rocket Pool network is operating and the faster rETH's price grows.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 1, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "red", + "value": null + }, + { + "color": "#EAB839", + "value": 0.2 + }, + { + "color": "green", + "value": 0.5 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Value" + }, + "properties": [ + { + "id": "unit", + "value": "percentunit" + } + ] + } + ] + }, + "gridPos": { + "h": 5, + "w": 3, + "x": 6, + "y": 37 + }, + "id": 168, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true, + "text": {} + }, + "pluginVersion": "9.5.18", + "targets": [ + { + "exemplar": true, + "expr": "rocketpool_performance_eth_utilization_rate{job=\"rocketpool\"}", + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Staking Pool ETH Used", + "type": "gauge" + }, + { + "description": "The total amount of ETH needed to be added to the staking pool in order to clear the minipool queue completely.", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "blue", + "mode": "fixed" + }, + "decimals": 2, + "mappings": [], + "max": 0, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "blue", + "value": null + } + ] + }, + "unit": "ETH" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 3, + "x": 9, + "y": 37 + }, + "id": 225, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.5.18", + "targets": [ + { + "editorMode": "code", + "exemplar": true, + "expr": "rocketpool_demand_total_minipool_capacity", + "interval": "", + "legendFormat": "", + "range": true, + "refId": "A" + } + ], + "title": "ETH Capacity of Queue", + "type": "stat" + }, + { + "description": "The total number of Rocket Pool minipools (validators) that are active (not exited and withdrawn from the Beacon Chain).", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 2, + "x": 0, + "y": 39 + }, + "id": 174, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "center", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "9.5.18", + "targets": [ + { + "exemplar": true, + "expr": "rocketpool_supply_active_minipools{job=\"rocketpool\"}", + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Active Minipools", + "type": "stat" + }, + { + "description": "The approximate amount of RPL you'll receive at the next checkpoint, based on your current rewardable stake and how much RPL is staked on the entire network.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "decimals": 2, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "orange", + "value": null + } + ] + }, + "unit": "RPL" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 4, + "x": 16, + "y": 41 + }, + "id": 198, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "9.5.18", + "targets": [ + { + "exemplar": true, + "expr": "rocketpool_node_expected_rpl_rewards{job=\"rocketpool\"}", + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Approx. Rewards from Next Checkpoint", + "type": "stat" + }, + { + "description": "The amount of RPL you've claimed over the life of your node, and the amount you still have yet to claim.", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "transparent", + "mode": "fixed" + }, + "decimals": 2, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "transparent", + "value": null + } + ] + }, + "unit": "RPL" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Unclaimed" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "blue", + "mode": "thresholds" + } + }, + { + "id": "thresholds", + "value": { + "mode": "absolute", + "steps": [ + { + "color": "transparent", + "value": null + }, + { + "color": "green", + "value": 0.00001 + } + ] + } + } + ] + } + ] + }, + "gridPos": { + "h": 6, + "w": 2, + "x": 20, + "y": 41 + }, + "id": 192, + "options": { + "colorMode": "background", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "9.5.18", + "targets": [ + { + "editorMode": "code", + "exemplar": true, + "expr": "rocketpool_node_unclaimed_rewards{job=\"rocketpool\"}", + "interval": "", + "legendFormat": "Unclaimed", + "range": true, + "refId": "A" + }, + { + "editorMode": "code", + "expr": "rocketpool_node_cumulative_rpl_rewards{job=\"rocketpool\"}", + "hide": false, + "legendFormat": "Claimed", + "range": true, + "refId": "B" + } + ], + "title": "RPL Rewards", + "type": "stat" + }, + { + "description": "", + "gridPos": { + "h": 1, + "w": 12, + "x": 0, + "y": 42 + }, + "id": 238, + "options": { + "code": { + "language": "plaintext", + "showLineNumbers": false, + "showMiniMap": false + }, + "content": "", + "mode": "markdown" + }, + "pluginVersion": "9.5.18", + "title": "Governance (Updates Every 6 Hours)", + "transparent": true, + "type": "text" + }, + { + "description": "The number of active proposals you or your delegate have voted on.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "transparent", + "value": null + }, + { + "color": "red", + "value": 1 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Voted" + }, + "properties": [ + { + "id": "thresholds", + "value": { + "mode": "absolute", + "steps": [ + { + "color": "transparent", + "value": null + }, + { + "color": "green", + "value": 1 + } + ] + } + } + ] + } + ] + }, + "gridPos": { + "h": 4, + "w": 3, + "x": 0, + "y": 43 + }, + "id": 235, + "options": { + "colorMode": "background", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.5.18", + "targets": [ + { + "editorMode": "code", + "exemplar": true, + "expr": "rocketpool_snapshot_votes_active", + "interval": "", + "legendFormat": "Voted", + "range": true, + "refId": "A" + }, + { + "editorMode": "code", + "exemplar": true, + "expr": "rocketpool_snapshot_proposals_active - rocketpool_snapshot_votes_active", + "hide": false, + "interval": "", + "legendFormat": "Missing votes", + "range": true, + "refId": "B" + } + ], + "title": "Active Proposals", + "type": "stat" + }, + { + "description": "Shows the number of closed votes you or your delegate has voted or missed.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "transparent", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 3, + "x": 3, + "y": 43 + }, + "id": 236, + "options": { + "colorMode": "background", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.5.18", + "targets": [ + { + "editorMode": "code", + "exemplar": true, + "expr": "rocketpool_snapshot_votes_closed", + "interval": "", + "legendFormat": "Voted", + "range": true, + "refId": "A" + }, + { + "editorMode": "code", + "exemplar": true, + "expr": "rocketpool_snapshot_proposals_closed - rocketpool_snapshot_votes_closed", + "hide": false, + "interval": "", + "legendFormat": "Missed votes", + "range": true, + "refId": "B" + } + ], + "title": "Closed Proposals", + "type": "stat" + }, + { + "description": "The node voting power on Snapshot.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "transparent", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 3, + "x": 6, + "y": 43 + }, + "id": 240, + "options": { + "colorMode": "background", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.5.18", + "targets": [ + { + "editorMode": "code", + "exemplar": true, + "expr": "rocketpool_snapshot_node_vp ", + "interval": "", + "legendFormat": "", + "range": true, + "refId": "A" + } + ], + "title": "Node Voting Power", + "type": "stat" + }, + { + "description": "The voting power of the delegate you have chosen.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "transparent", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 3, + "x": 9, + "y": 43 + }, + "id": 242, + "options": { + "colorMode": "background", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.5.18", + "targets": [ + { + "editorMode": "code", + "exemplar": true, + "expr": "rocketpool_snapshot_delegate_vp ", + "interval": "", + "legendFormat": "", + "range": true, + "refId": "A" + } + ], + "title": "Delegate Voting Power", + "type": "stat" + }, + { + "description": "The approximate APR for your RPL stake, based on the rewards you will receive at the next checkpoint.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "decimals": 2, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "orange", + "value": null + } + ] + }, + "unit": "percent" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 4, + "x": 16, + "y": 44 + }, + "id": 194, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "9.5.18", + "targets": [ + { + "exemplar": true, + "expr": "rocketpool_node_rpl_apr{job=\"rocketpool\"}", + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Your RPL APR for the Next Checkpoint", + "type": "stat" + }, + { + "description": "", + "gridPos": { + "h": 1, + "w": 9, + "x": 13, + "y": 47 + }, + "id": 261, + "options": { + "code": { + "language": "plaintext", + "showLineNumbers": false, + "showMiniMap": false + }, + "content": "", + "mode": "markdown" + }, + "pluginVersion": "9.5.18", + "transparent": true, + "type": "text" + }, + { + "description": "The balances of your node wallet (only tracks Rocket Pool tokens). Keep an eye on how much ETH you have; if it falls too low, your node won't be able to pay for gas during automatic RPL reward claims or other transactions.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "center", + "cellOptions": { + "type": "auto" + }, + "filterable": false, + "inspect": false + }, + "decimals": 6, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 9, + "x": 13, + "y": 48 + }, + "id": 200, + "options": { + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true, + "sortBy": [] + }, + "pluginVersion": "9.5.18", + "targets": [ + { + "exemplar": false, + "expr": "rocketpool_node_balance{job=\"rocketpool\"}", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "{{Token}}", + "refId": "A" + } + ], + "title": "Node Wallet Balances", + "transformations": [ + { + "id": "organize", + "options": { + "excludeByName": { + "Time": true, + "__name__": true, + "instance": true, + "job": true + }, + "indexByName": {}, + "renameByName": { + "Value #A": "Value", + "job": "" + } + } + } + ], + "type": "table" + } + ], + "refresh": "30s", + "revision": 1, + "schemaVersion": 38, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-1h", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ] + }, + "timezone": "", + "title": "Rocket Pool Dashboard v1.3.2 - Provisioned", + "uid": "Ur22GG77z132", + "version": 20, + "weekStart": "" +} diff --git a/shared/services/rocketpool/assets/install/grafana-dashboards.yml b/shared/services/rocketpool/assets/install/grafana-dashboards.yml new file mode 100644 index 000000000..85b337c92 --- /dev/null +++ b/shared/services/rocketpool/assets/install/grafana-dashboards.yml @@ -0,0 +1,12 @@ +apiVersion: 1 + +providers: + - name: Default + folder: Rocket Pool Default + orgId: 1 + type: file + allowUiUpdates: false + options: + path: /etc/grafana/default_dashboards/ + updateIntervalSeconds: 300 + diff --git a/shared/services/rocketpool/assets/install/grafana-prometheus-datasource.yml b/shared/services/rocketpool/assets/install/grafana-prometheus-datasource.yml new file mode 100644 index 000000000..f90b695d5 --- /dev/null +++ b/shared/services/rocketpool/assets/install/grafana-prometheus-datasource.yml @@ -0,0 +1,16 @@ +apiVersion: 1 + +deleteDatasources: + - name: Prometheus + orgId: 1 + +datasources: + - name: Prometheus + type: prometheus + access: proxy + orgId: 1 + url: http://prometheus:9091 + basicAuth: false + isDefault: true + version: 1 + editable: true diff --git a/shared/services/rocketpool/assets/install/override/addons/gww/addon_gww.yml b/shared/services/rocketpool/assets/install/override/addons/gww/addon_gww.yml new file mode 100644 index 000000000..e7ab0ed4d --- /dev/null +++ b/shared/services/rocketpool/assets/install/override/addons/gww/addon_gww.yml @@ -0,0 +1,8 @@ +# Enter your own customizations for the Graffiti Wall Addon container here. These changes will persist after upgrades, so you only need to do them once. +# +# See https://docs.docker.com/compose/extends/#adding-and-overriding-configuration +# for more information on overriding specific parameters of docker-compose files. + +services: + addon_gww: + x-rp-comment: Add your customizations below this line diff --git a/shared/services/rocketpool/assets/install/override/alertmanager.yml b/shared/services/rocketpool/assets/install/override/alertmanager.yml new file mode 100644 index 000000000..520aa4622 --- /dev/null +++ b/shared/services/rocketpool/assets/install/override/alertmanager.yml @@ -0,0 +1,8 @@ +# Enter your own customizations for the alertmanager container here. These changes will persist after upgrades, so you only need to do them once. +# +# See https://docs.docker.com/compose/extends/#adding-and-overriding-configuration +# for more information on overriding specific parameters of docker-compose files. + +services: + alertmanager: + x-rp-comment: Add your customizations below this line diff --git a/shared/services/rocketpool/assets/install/override/api.yml b/shared/services/rocketpool/assets/install/override/api.yml new file mode 100644 index 000000000..d7444de62 --- /dev/null +++ b/shared/services/rocketpool/assets/install/override/api.yml @@ -0,0 +1,8 @@ +# Enter your own customizations for the API container here. These changes will persist after upgrades, so you only need to do them once. +# +# See https://docs.docker.com/compose/extends/#adding-and-overriding-configuration +# for more information on overriding specific parameters of docker-compose files. + +services: + api: + x-rp-comment: Add your customizations below this line diff --git a/shared/services/rocketpool/assets/install/override/eth1.yml b/shared/services/rocketpool/assets/install/override/eth1.yml new file mode 100644 index 000000000..92387361e --- /dev/null +++ b/shared/services/rocketpool/assets/install/override/eth1.yml @@ -0,0 +1,8 @@ +# Enter your own customizations for the eth1 container here. These changes will persist after upgrades, so you only need to do them once. +# +# See https://docs.docker.com/compose/extends/#adding-and-overriding-configuration +# for more information on overriding specific parameters of docker-compose files. + +services: + eth1: + x-rp-comment: Add your customizations below this line diff --git a/shared/services/rocketpool/assets/install/override/eth2.yml b/shared/services/rocketpool/assets/install/override/eth2.yml new file mode 100644 index 000000000..e9a2a2725 --- /dev/null +++ b/shared/services/rocketpool/assets/install/override/eth2.yml @@ -0,0 +1,8 @@ +# Enter your own customizations for the eth2 container here. These changes will persist after upgrades, so you only need to do them once. +# +# See https://docs.docker.com/compose/extends/#adding-and-overriding-configuration +# for more information on overriding specific parameters of docker-compose files. + +services: + eth2: + x-rp-comment: Add your customizations below this line diff --git a/shared/services/rocketpool/assets/install/override/exporter.yml b/shared/services/rocketpool/assets/install/override/exporter.yml new file mode 100644 index 000000000..75e3f64b8 --- /dev/null +++ b/shared/services/rocketpool/assets/install/override/exporter.yml @@ -0,0 +1,8 @@ +# Enter your own customizations for the node exporter container here. These changes will persist after upgrades, so you only need to do them once. +# +# See https://docs.docker.com/compose/extends/#adding-and-overriding-configuration +# for more information on overriding specific parameters of docker-compose files. + +services: + node-exporter: + x-rp-comment: Add your customizations below this line diff --git a/shared/services/rocketpool/assets/install/override/grafana.yml b/shared/services/rocketpool/assets/install/override/grafana.yml new file mode 100644 index 000000000..31b7ea3f9 --- /dev/null +++ b/shared/services/rocketpool/assets/install/override/grafana.yml @@ -0,0 +1,8 @@ +# Enter your own customizations for the Grafana container here. These changes will persist after upgrades, so you only need to do them once. +# +# See https://docs.docker.com/compose/extends/#adding-and-overriding-configuration +# for more information on overriding specific parameters of docker-compose files. + +services: + grafana: + x-rp-comment: Add your customizations below this line diff --git a/shared/services/rocketpool/assets/install/override/mev-boost.yml b/shared/services/rocketpool/assets/install/override/mev-boost.yml new file mode 100644 index 000000000..d972b42b4 --- /dev/null +++ b/shared/services/rocketpool/assets/install/override/mev-boost.yml @@ -0,0 +1,8 @@ +# Enter your own customizations for the MEV Boost container here. These changes will persist after upgrades, so you only need to do them once. +# +# See https://docs.docker.com/compose/extends/#adding-and-overriding-configuration +# for more information on overriding specific parameters of docker-compose files. + +services: + mev-boost: + x-rp-comment: Add your customizations below this line diff --git a/shared/services/rocketpool/assets/install/override/node.yml b/shared/services/rocketpool/assets/install/override/node.yml new file mode 100644 index 000000000..8c2a2812e --- /dev/null +++ b/shared/services/rocketpool/assets/install/override/node.yml @@ -0,0 +1,8 @@ +# Enter your own customizations for the node container here. These changes will persist after upgrades, so you only need to do them once. +# +# See https://docs.docker.com/compose/extends/#adding-and-overriding-configuration +# for more information on overriding specific parameters of docker-compose files. + +services: + node: + x-rp-comment: Add your customizations below this line diff --git a/shared/services/rocketpool/assets/install/override/prometheus.yml b/shared/services/rocketpool/assets/install/override/prometheus.yml new file mode 100644 index 000000000..8b20b9108 --- /dev/null +++ b/shared/services/rocketpool/assets/install/override/prometheus.yml @@ -0,0 +1,8 @@ +# Enter your own customizations for the Prometheus container here. These changes will persist after upgrades, so you only need to do them once. +# +# See https://docs.docker.com/compose/extends/#adding-and-overriding-configuration +# for more information on overriding specific parameters of docker-compose files. + +services: + prometheus: + x-rp-comment: Add your customizations below this line diff --git a/shared/services/rocketpool/assets/install/override/validator.yml b/shared/services/rocketpool/assets/install/override/validator.yml new file mode 100644 index 000000000..88bbeff51 --- /dev/null +++ b/shared/services/rocketpool/assets/install/override/validator.yml @@ -0,0 +1,8 @@ +# Enter your own customizations for the validator container here. These changes will persist after upgrades, so you only need to do them once. +# +# See https://docs.docker.com/compose/extends/#adding-and-overriding-configuration +# for more information on overriding specific parameters of docker-compose files. + +services: + validator: + x-rp-comment: Add your customizations below this line diff --git a/shared/services/rocketpool/assets/install/override/watchtower.yml b/shared/services/rocketpool/assets/install/override/watchtower.yml new file mode 100644 index 000000000..abd1ea4fb --- /dev/null +++ b/shared/services/rocketpool/assets/install/override/watchtower.yml @@ -0,0 +1,8 @@ +# Enter your own customizations for the watchtower container here. These changes will persist after upgrades, so you only need to do them once. +# +# See https://docs.docker.com/compose/extends/#adding-and-overriding-configuration +# for more information on overriding specific parameters of docker-compose files. + +services: + watchtower: + x-rp-comment: Add your customizations below this line diff --git a/shared/services/rocketpool/assets/install/prometheus.tmpl b/shared/services/rocketpool/assets/install/prometheus.tmpl new file mode 100644 index 000000000..2ee31e74e --- /dev/null +++ b/shared/services/rocketpool/assets/install/prometheus.tmpl @@ -0,0 +1,60 @@ +# Default Prometheus configuration for Rocket Pool + +global: + scrape_interval: 15s # Set the scrape interval to every 15 seconds. Default is every 1 minute. + scrape_timeout: 12s # Timeout must be shorter than the interval + evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute. + +scrape_configs: + - job_name: 'prometheus' + static_configs: + - targets: ['localhost:{{or .Prometheus.Port.Value "9091"}}'] + + - job_name: 'node' + static_configs: + # node-exporter is on the host network so it can get access to the actual machine's network info + # We have to use 'hosts.docker.internal' to refer to it due to this configuration + - targets: ['host.docker.internal:{{or .ExporterMetricsPort.Value "9103"}}'] + + - job_name: 'geth' + static_configs: + - targets: ['{{.GetExecutionHostname}}:{{or .EcMetricsPort.Value "9105"}}'] + metrics_path: /debug/metrics/prometheus + + - job_name: 'eth1' + static_configs: + - targets: ['{{.GetExecutionHostname}}:{{or .EcMetricsPort.Value "9105"}}'] + + - job_name: 'eth2' + static_configs: + - targets: ['{{.GetConsensusHostname}}:{{or .BnMetricsPort.Value "9100"}}'] + + - job_name: 'validator' + static_configs: + - targets: ['validator:{{or .VcMetricsPort.Value "9101"}}'] + + - job_name: 'rocketpool' + scrape_interval: 5m + scrape_timeout: 5m + static_configs: + - targets: ['node:{{or .NodeMetricsPort.Value "9102"}}'] + + - job_name: 'watchtower' + scrape_interval: 5m + scrape_timeout: 5m + static_configs: + - targets: ['watchtower:{{or .WatchtowerMetricsPort.Value "9104"}}'] + + - job_name: 'custom_jobs' # Mandatory field, but will be ignored. + file_sd_configs: + - files: + - /extra-scrape-jobs/*.yml + +alerting: + alertmanagers: + - static_configs: + - targets: + - alertmanager:{{or .Alertmanager.Port.Value "9093"}} + +rule_files: + - /alerting/rules/*.yml \ No newline at end of file diff --git a/shared/services/rocketpool/assets/install/scripts/download-genesis.sh b/shared/services/rocketpool/assets/install/scripts/download-genesis.sh new file mode 100644 index 000000000..cdb097902 --- /dev/null +++ b/shared/services/rocketpool/assets/install/scripts/download-genesis.sh @@ -0,0 +1,25 @@ +#!/bin/sh +set -e + +# Grab the Testnet genesis state if needed +if [ "$NETWORK" = "testnet" ]; then + echo "Prysm is configured to use Hoodi, genesis state required." + if [ ! -f "/ethclient/hoodi-genesis.ssz" ]; then + echo "Downloading from Github..." + wget https://github.com/eth-clients/hoodi/raw/refs/heads/main/metadata/genesis.ssz -O /ethclient/hoodi-genesis.ssz + echo "Download complete." + else + echo "Genesis state already downloaded, continuing." + fi +elif [ "$NETWORK" = "devnet" ]; then + echo "Prysm is configured to use Hoodi, genesis state required." + if [ ! -f "/ethclient/hoodi-genesis.ssz" ]; then + echo "Downloading from Github..." + wget https://github.com/eth-clients/hoodi/raw/refs/heads/main/metadata/genesis.ssz -O /ethclient/hoodi-genesis.ssz + echo "Download complete." + else + echo "Genesis state already downloaded, continuing." + fi +else + echo "Genesis download not required for $NETWORK" +fi diff --git a/shared/services/rocketpool/assets/install/scripts/restart-vc.sh b/shared/services/rocketpool/assets/install/scripts/restart-vc.sh new file mode 100755 index 000000000..76321b7cd --- /dev/null +++ b/shared/services/rocketpool/assets/install/scripts/restart-vc.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +# This script is used to restart your Validator Client service. +# It is run in Native Mode only when the Smartnode needs to restart your Validator Client (for example, to load new minipool keys). +# It is not used in standard (docker-based) Rocket Pool setups. + +# The command below is an example only. +# Replace it with your own commands to restart your Validator Client service. + +#sudo systemctl restart lighthouse-validator diff --git a/shared/services/rocketpool/assets/install/scripts/start-bn.sh b/shared/services/rocketpool/assets/install/scripts/start-bn.sh new file mode 100755 index 000000000..6225563f8 --- /dev/null +++ b/shared/services/rocketpool/assets/install/scripts/start-bn.sh @@ -0,0 +1,314 @@ +#!/bin/sh +# This script launches ETH2 beacon clients for Rocket Pool's docker stack; only edit if you know what you're doing ;) + +# Performance tuning for ARM systems +UNAME_VAL=$(uname -m) +if [ "$UNAME_VAL" = "arm64" ] || [ "$UNAME_VAL" = "aarch64" ]; then + # Get the number of available cores + CORE_COUNT=$(nproc) + + # Don't do performance tweaks on systems with 6+ cores + if [ "$CORE_COUNT" -gt "5" ]; then + echo "$CORE_COUNT cores detected, skipping performance tuning" + else + echo "$CORE_COUNT cores detected, activating performance tuning" + PERF_PREFIX="ionice -c 2 -n 0" + echo "Performance tuning: $PERF_PREFIX" + fi +fi + +# Set up the network-based flags +if [ "$NETWORK" = "mainnet" ]; then + LH_NETWORK="mainnet" + LODESTAR_NETWORK="mainnet" + NIMBUS_NETWORK="mainnet" + PRYSM_NETWORK="--mainnet" + TEKU_NETWORK="mainnet" + PRYSM_GENESIS_STATE="" +elif [ "$NETWORK" = "devnet" ]; then + LH_NETWORK="hoodi" + LODESTAR_NETWORK="hoodi" + NIMBUS_NETWORK="hoodi" + PRYSM_NETWORK="--hoodi" + TEKU_NETWORK="hoodi" +elif [ "$NETWORK" = "testnet" ]; then + LH_NETWORK="hoodi" + LODESTAR_NETWORK="hoodi" + NIMBUS_NETWORK="hoodi" + PRYSM_NETWORK="--hoodi" + TEKU_NETWORK="hoodi" +else + echo "Unknown network [$NETWORK]" + exit 1 +fi + +# Check for the JWT auth file +if [ ! -f "/secrets/jwtsecret" ]; then + echo "JWT secret file not found, please try again when the Execution Client has created one." + exit 1 +fi + +# Report a missing fee recipient file +if [ ! -f "/validators/$FEE_RECIPIENT_FILE" ]; then + echo "Fee recipient file not found, please wait for the rocketpool_node process to create one." + exit 1 +fi + +# Lighthouse startup +if [ "$CC_CLIENT" = "lighthouse" ]; then + + CMD="$PERF_PREFIX /usr/local/bin/lighthouse beacon \ + --network $LH_NETWORK \ + --datadir /ethclient/lighthouse \ + --port $BN_P2P_PORT \ + --discovery-port $BN_P2P_PORT \ + --execution-endpoint $EC_ENGINE_ENDPOINT \ + --http \ + --http-address 0.0.0.0 \ + --http-port ${BN_API_PORT:-5052} \ + --eth1-blocks-per-log-query 150 \ + --disable-upnp \ + --staking \ + --execution-jwt=/secrets/jwtsecret \ + --quic-port ${BN_P2P_QUIC_PORT:-8001} \ + --historic-state-cache-size 2 \ + $BN_ADDITIONAL_FLAGS" + + # Performance tuning for ARM systems + UNAME_VAL=$(uname -m) + if [ "$UNAME_VAL" = "arm64" ] || [ "$UNAME_VAL" = "aarch64" ]; then + CMD="$CMD --execution-timeout-multiplier 2" + fi + + if [ ! -z "$MEV_BOOST_URL" ]; then + CMD="$CMD --builder $MEV_BOOST_URL" + fi + + if [ ! -z "$BN_MAX_PEERS" ]; then + CMD="$CMD --target-peers $BN_MAX_PEERS" + fi + + if [ "$ENABLE_METRICS" = "true" ]; then + CMD="$CMD --metrics --metrics-address 0.0.0.0 --metrics-port $BN_METRICS_PORT --validator-monitor-auto" + fi + + if [ ! -z "$CHECKPOINT_SYNC_URL" ]; then + CMD="$CMD --checkpoint-sync-url $CHECKPOINT_SYNC_URL" + fi + + if [ "$ENABLE_BITFLY_NODE_METRICS" = "true" ]; then + CMD="$CMD --monitoring-endpoint $BITFLY_NODE_METRICS_ENDPOINT?apikey=$BITFLY_NODE_METRICS_SECRET&machine=$BITFLY_NODE_METRICS_MACHINE_NAME" + fi + + exec ${CMD} + +fi + +# Lodestar startup +if [ "$CC_CLIENT" = "lodestar" ]; then + + CMD="$PERF_PREFIX /usr/local/bin/node --max-http-header-size=65536 /usr/app/packages/cli/bin/lodestar beacon \ + --network $LODESTAR_NETWORK \ + --dataDir /ethclient/lodestar \ + --port $BN_P2P_PORT \ + --execution.urls $EC_ENGINE_ENDPOINT \ + --rest \ + --rest.address 0.0.0.0 \ + --rest.port ${BN_API_PORT:-5052} \ + --jwt-secret /secrets/jwtsecret \ + $BN_ADDITIONAL_FLAGS" + + if [ ! -z "$TTD_OVERRIDE" ]; then + CMD="$CMD --terminal-total-difficulty-override $TTD_OVERRIDE" + fi + + if [ ! -z "$MEV_BOOST_URL" ]; then + CMD="$CMD --builder --builder.urls $MEV_BOOST_URL" + fi + + if [ ! -z "$BN_MAX_PEERS" ]; then + CMD="$CMD --targetPeers $BN_MAX_PEERS" + fi + + if [ "$ENABLE_METRICS" = "true" ]; then + CMD="$CMD --metrics --metrics.address 0.0.0.0 --metrics.port $BN_METRICS_PORT" + fi + + if [ ! -z "$EXTERNAL_IP" ]; then + CMD="$CMD --enr.ip $EXTERNAL_IP --nat" + fi + + if [ ! -z "$CHECKPOINT_SYNC_URL" ]; then + CMD="$CMD --checkpointSyncUrl $CHECKPOINT_SYNC_URL" + fi + + if [ "$ENABLE_BITFLY_NODE_METRICS" = "true" ]; then + CMD="$CMD --monitoring.endpoint $BITFLY_NODE_METRICS_ENDPOINT?apikey=$BITFLY_NODE_METRICS_SECRET&machine=$BITFLY_NODE_METRICS_MACHINE_NAME" + fi + + exec ${CMD} + +fi + +# Nimbus startup +if [ "$CC_CLIENT" = "nimbus" ]; then + + # Handle checkpoint syncing + if [ ! -z "$CHECKPOINT_SYNC_URL" ]; then + # Ignore it if a DB already exists + if [ -f "/ethclient/nimbus/db/nbc.sqlite3" ]; then + echo "Nimbus database already exists, ignoring checkpoint sync." + else + echo "Starting checkpoint sync for Nimbus..." + $PERF_PREFIX /home/user/nimbus-eth2/build/nimbus_beacon_node trustedNodeSync --network=$NIMBUS_NETWORK --data-dir=/ethclient/nimbus --trusted-node-url=$CHECKPOINT_SYNC_URL --backfill=false + echo "Checkpoint sync complete!" + fi + fi + + CMD="$PERF_PREFIX /home/user/nimbus-eth2/build/nimbus_beacon_node \ + --non-interactive \ + --enr-auto-update \ + --network=$NIMBUS_NETWORK \ + --data-dir=/ethclient/nimbus \ + --tcp-port=$BN_P2P_PORT \ + --udp-port=$BN_P2P_PORT \ + --web3-url=$EC_ENGINE_ENDPOINT \ + --rest \ + --rest-address=0.0.0.0 \ + --rest-port=${BN_API_PORT:-5052} \ + --jwt-secret=/secrets/jwtsecret \ + $BN_ADDITIONAL_FLAGS" + + if [ ! -z "$BN_SUGGESTED_BLOCK_GAS_LIMIT" ]; then + CMD="$CMD --suggested-gas-limit=$BN_SUGGESTED_BLOCK_GAS_LIMIT" + fi + + if [ ! -z "$MEV_BOOST_URL" ]; then + CMD="$CMD --payload-builder --payload-builder-url=$MEV_BOOST_URL" + fi + + if [ ! -z "$BN_MAX_PEERS" ]; then + CMD="$CMD --max-peers=$BN_MAX_PEERS" + fi + + if [ "$ENABLE_METRICS" = "true" ]; then + CMD="$CMD --metrics --metrics-address=0.0.0.0 --metrics-port=$BN_METRICS_PORT" + fi + + if [ ! -z "$EXTERNAL_IP" ]; then + CMD="$CMD --nat=extip:$EXTERNAL_IP" + fi + + if [ ! -z "$NIMBUS_PRUNING_MODE" ]; then + CMD="$CMD --history=$NIMBUS_PRUNING_MODE" + fi + + exec ${CMD} + +fi + +# Prysm startup +if [ "$CC_CLIENT" = "prysm" ]; then + + CMD="$PERF_PREFIX /app/cmd/beacon-chain/beacon-chain \ + --accept-terms-of-use \ + $PRYSM_NETWORK \ + $PRYSM_GENESIS_STATE \ + --datadir /ethclient/prysm \ + --p2p-tcp-port $BN_P2P_PORT \ + --p2p-udp-port $BN_P2P_PORT \ + --execution-endpoint $EC_ENGINE_ENDPOINT \ + --rpc-host 0.0.0.0 \ + --rpc-port ${BN_RPC_PORT:-5053} \ + --grpc-gateway-host 0.0.0.0 \ + --grpc-gateway-port ${BN_API_PORT:-5052} \ + --p2p-quic-port ${BN_P2P_QUIC_PORT:-8001} \ + --eth1-header-req-limit 150 \ + --jwt-secret=/secrets/jwtsecret \ + --api-timeout 20s \ + --enable-experimental-backfill \ + $BN_ADDITIONAL_FLAGS" + + if [ ! -z "$MEV_BOOST_URL" ]; then + CMD="$CMD --http-mev-relay $MEV_BOOST_URL" + fi + + if [ ! -z "$BN_MAX_PEERS" ]; then + CMD="$CMD --p2p-max-peers $BN_MAX_PEERS" + fi + + if [ "$ENABLE_METRICS" = "true" ]; then + CMD="$CMD --monitoring-host 0.0.0.0 --monitoring-port $BN_METRICS_PORT" + else + CMD="$CMD --disable-monitoring" + fi + + if [ "$NETWORK" = "testnet" ]; then + CMD="$CMD --genesis-state /ethclient/hoodi-genesis.ssz" + elif [ "$NETWORK" = "devnet" ]; then + CMD="$CMD --genesis-state /ethclient/hoodi-genesis.ssz" + fi + + if [ ! -z "$CHECKPOINT_SYNC_URL" ]; then + CMD="$CMD --checkpoint-sync-url=$CHECKPOINT_SYNC_URL --genesis-beacon-api-url=$CHECKPOINT_SYNC_URL" + fi + + exec ${CMD} + +fi + +# Teku startup +if [ "$CC_CLIENT" = "teku" ]; then + + CMD="$PERF_PREFIX /opt/teku/bin/teku \ + --network=$TEKU_NETWORK \ + --data-path=/ethclient/teku \ + --p2p-port=$BN_P2P_PORT \ + --ee-endpoint=$EC_ENGINE_ENDPOINT \ + --rest-api-enabled \ + --rest-api-interface=0.0.0.0 \ + --rest-api-port=${BN_API_PORT:-5052} \ + --rest-api-host-allowlist=* \ + --eth1-deposit-contract-max-request-size=150 \ + --log-destination=CONSOLE \ + --ee-jwt-secret-file=/secrets/jwtsecret \ + --beacon-liveness-tracking-enabled \ + --validators-proposer-default-fee-recipient=$RETH_ADDRESS \ + --validators-graffiti-client-append-format=DISABLED \ + $BN_ADDITIONAL_FLAGS" + + if [ ! -z "$BN_SUGGESTED_BLOCK_GAS_LIMIT" ]; then + CMD="$CMD --validators-builder-registration-default-gas-limit=$BN_SUGGESTED_BLOCK_GAS_LIMIT" + fi + + if [ "$TEKU_ARCHIVE_MODE" = "true" ]; then + CMD="$CMD --data-storage-mode=archive" + fi + + if [ ! -z "$MEV_BOOST_URL" ]; then + CMD="$CMD --builder-endpoint=$MEV_BOOST_URL" + fi + + if [ ! -z "$BN_MAX_PEERS" ]; then + CMD="$CMD --p2p-peer-lower-bound=$BN_MAX_PEERS --p2p-peer-upper-bound=$BN_MAX_PEERS" + fi + + if [ "$ENABLE_METRICS" = "true" ]; then + CMD="$CMD --metrics-enabled=true --metrics-interface=0.0.0.0 --metrics-port=$BN_METRICS_PORT --metrics-host-allowlist=*" + fi + + if [ ! -z "$CHECKPOINT_SYNC_URL" ]; then + CMD="$CMD --checkpoint-sync-url=$CHECKPOINT_SYNC_URL" + fi + + if [ "$ENABLE_BITFLY_NODE_METRICS" = "true" ]; then + CMD="$CMD --metrics-publish-endpoint=$BITFLY_NODE_METRICS_ENDPOINT?apikey=$BITFLY_NODE_METRICS_SECRET&machine=$BITFLY_NODE_METRICS_MACHINE_NAME" + fi + + if [ "$TEKU_JVM_HEAP_SIZE" -gt "0" ]; then + CMD="env JAVA_OPTS=\"-Xmx${TEKU_JVM_HEAP_SIZE}m\" $CMD" + fi + + exec ${CMD} + +fi diff --git a/shared/services/rocketpool/assets/install/scripts/start-ec.sh b/shared/services/rocketpool/assets/install/scripts/start-ec.sh new file mode 100755 index 000000000..0ebeba5a5 --- /dev/null +++ b/shared/services/rocketpool/assets/install/scripts/start-ec.sh @@ -0,0 +1,366 @@ +#!/bin/sh +# This script launches ETH1 clients for Rocket Pool's docker stack; only edit if you know what you're doing ;) + +# Performance tuning for ARM systems +define_perf_prefix() { + # Get the number of available cores + CORE_COUNT=$(nproc) + + # Don't do performance tweaks on systems with 6+ cores + if [ "$CORE_COUNT" -gt "5" ]; then + echo "$CORE_COUNT cores detected, skipping performance tuning" + return 0 + else + echo "$CORE_COUNT cores detected, activating performance tuning" + fi + + # Give the EC access to the last core + CURRENT_CORE=$((CORE_COUNT - 1)) + CORE_STRING="$CURRENT_CORE" + + # If there are more than 2 cores, limit the EC to use all but the first one + CURRENT_CORE=$((CURRENT_CORE - 1)) + while [ "$CURRENT_CORE" -gt "0" ]; do + CORE_STRING="$CORE_STRING,$CURRENT_CORE" + CURRENT_CORE=$((CURRENT_CORE - 1)) + done + + PERF_PREFIX="taskset -c $CORE_STRING ionice -c 3" + echo "Performance tuning: $PERF_PREFIX" +} + +# Set up the network-based flags +if [ "$NETWORK" = "mainnet" ]; then + GETH_NETWORK="" + RP_NETHERMIND_NETWORK="mainnet" + BESU_NETWORK="--network=mainnet" + RETH_NETWORK="--chain mainnet" +elif [ "$NETWORK" = "devnet" ]; then + GETH_NETWORK="--hoodi" + RP_NETHERMIND_NETWORK="hoodi" + BESU_NETWORK="--network=hoodi" + RETH_NETWORK="--chain hoodi" +elif [ "$NETWORK" = "testnet" ]; then + GETH_NETWORK="--hoodi" + RP_NETHERMIND_NETWORK="hoodi" + BESU_NETWORK="--network=hoodi" + RETH_NETWORK="--chain hoodi" +else + echo "Unknown network [$NETWORK]" + exit 1 +fi + + +# Geth startup +if [ "$CLIENT" = "geth" ]; then + + # Performance tuning for ARM systems + UNAME_VAL=$(uname -m) + if [ "$UNAME_VAL" = "arm64" ] || [ "$UNAME_VAL" = "aarch64" ]; then + + # Install taskset and ionice + apk add util-linux + + # Define the performance tuning prefix + define_perf_prefix + + fi + + # Check for the prune flag and run that first if requested + if [ -f "/ethclient/prune.lock" ]; then + + $PERF_PREFIX /usr/local/bin/geth snapshot prune-state $GETH_NETWORK --datadir /ethclient/geth ; rm /ethclient/prune.lock + + # Run Geth normally + else + + CMD="$PERF_PREFIX /usr/local/bin/geth $GETH_NETWORK \ + --datadir /ethclient/geth \ + --http \ + --http.addr 0.0.0.0 \ + --http.port ${EC_HTTP_PORT:-8545} \ + --http.api eth,net,web3 \ + --http.corsdomain=* \ + --ws \ + --ws.addr 0.0.0.0 \ + --ws.port ${EC_WS_PORT:-8546} \ + --ws.api eth,net,web3 \ + --authrpc.addr 0.0.0.0 \ + --authrpc.port ${EC_ENGINE_PORT:-8551} \ + --authrpc.jwtsecret /secrets/jwtsecret \ + --authrpc.vhosts=* \ + --pprof \ + $EC_ADDITIONAL_FLAGS" + + if [ ! -z "$EC_SUGGESTED_BLOCK_GAS_LIMIT" ]; then + CMD="$CMD --miner.gaslimit $EC_SUGGESTED_BLOCK_GAS_LIMIT" + fi + + if [ "$GETH_ARCHIVE_MODE" = "true" ]; then + CMD="$CMD --syncmode=full --gcmode=archive" + fi + + if [ ! -z "$GETH_EVM_TIMEOUT" ]; then + CMD="$CMD --rpc.evmtimeout ${GETH_EVM_TIMEOUT}s" + fi + + if [ ! -z "$ETHSTATS_LABEL" ] && [ ! -z "$ETHSTATS_LOGIN" ]; then + CMD="$CMD --ethstats $ETHSTATS_LABEL:$ETHSTATS_LOGIN" + fi + + if [ ! -z "$EC_MAX_PEERS" ]; then + CMD="$CMD --maxpeers $EC_MAX_PEERS" + fi + + if [ "$ENABLE_METRICS" = "true" ]; then + CMD="$CMD --metrics --metrics.addr 0.0.0.0 --metrics.port $EC_METRICS_PORT" + fi + + if [ ! -z "$EC_P2P_PORT" ]; then + CMD="$CMD --port $EC_P2P_PORT" + fi + + exec ${CMD} --http.vhosts '*' + + fi + +fi + + +# Nethermind startup +if [ "$CLIENT" = "nethermind" ]; then + + # Performance tuning for ARM systems + UNAME_VAL=$(uname -m) + if [ "$UNAME_VAL" = "arm64" ] || [ "$UNAME_VAL" = "aarch64" ]; then + + # Define the performance tuning prefix + define_perf_prefix + + fi + + # Create the JWT secret + if [ ! -f "/secrets/jwtsecret" ]; then + openssl rand -hex 32 | tr -d "\n" > /secrets/jwtsecret + fi + + # Set the JSON RPC logging level + LOG_LINE=$(awk '//{print NR}' /nethermind/NLog.config) + sed -e "${LOG_LINE} i \ \\n" -i /nethermind/NLog.config + sed -e "${LOG_LINE} i \ " -i /nethermind/NLog.config + sed -e "${LOG_LINE} i \ " -i /nethermind/NLog.config + + # Remove the sync peers report but leave error messages + sed -e "${LOG_LINE} i \ " -i /nethermind/NLog.config + sed -i 's//\1/g' /nethermind/NLog.config + + # Get the binary name (changed with v1.21, required for backwards compatibility) + if [ -f "/nethermind/Nethermind.Runner" ]; then + NETHERMIND_BINARY=/nethermind/Nethermind.Runner + elif [ -f "/nethermind/nethermind" ]; then + NETHERMIND_BINARY=/nethermind/nethermind + else + echo "Nethermind binary not found, cannot start Execution Client." + exit 1 + fi + + CMD="$PERF_PREFIX $NETHERMIND_BINARY \ + --config $RP_NETHERMIND_NETWORK \ + --Sync.SnapSync true \ + --data-dir /ethclient/nethermind \ + --JsonRpc.Enabled true \ + --JsonRpc.Host 0.0.0.0 \ + --JsonRpc.Port ${EC_HTTP_PORT:-8545} \ + --JsonRpc.EnginePort ${EC_ENGINE_PORT:-8551} \ + --JsonRpc.EngineHost 0.0.0.0 \ + --Init.WebSocketsEnabled true \ + --JsonRpc.WebSocketsPort ${EC_WS_PORT:-8546} \ + --JsonRpc.JwtSecretFile=/secrets/jwtsecret \ + --Pruning.FullPruningTrigger=VolumeFreeSpace \ + --Pruning.FullPruningThresholdMb=$RP_NETHERMIND_FULL_PRUNING_THRESHOLD_MB \ + --Pruning.FullPruningCompletionBehavior AlwaysShutdown \ + --Pruning.FullPruningMaxDegreeOfParallelism=$RP_NETHERMIND_FULL_PRUNING_MAX_DEGREE_PARALLELISM \ + --Pruning.FullPruningMemoryBudgetMb=$RP_NETHERMIND_FULL_PRUNE_MEMORY_BUDGET \ + $EC_ADDITIONAL_FLAGS" + + if [ ! -z "$EC_SUGGESTED_BLOCK_GAS_LIMIT" ]; then + CMD="$CMD --Blocks.TargetBlockGasLimit $EC_SUGGESTED_BLOCK_GAS_LIMIT" + fi + + # Add optional supplemental primary JSON-RPC modules + if [ ! -z "$RP_NETHERMIND_ADDITIONAL_MODULES" ]; then + RP_NETHERMIND_ADDITIONAL_MODULES=",${RP_NETHERMIND_ADDITIONAL_MODULES}" + fi + CMD="$CMD --JsonRpc.EnabledModules Eth,Net,Web3$RP_NETHERMIND_ADDITIONAL_MODULES" + + # Add optional supplemental JSON-RPC URLs + if [ ! -z "$RP_NETHERMIND_ADDITIONAL_URLS" ]; then + RP_NETHERMIND_ADDITIONAL_URLS=",${RP_NETHERMIND_ADDITIONAL_URLS}" + fi + CMD="$CMD --JsonRpc.AdditionalRpcUrls [\"http://127.0.0.1:7434|http|admin\"$RP_NETHERMIND_ADDITIONAL_URLS]" + + if [ ! -z "$ETHSTATS_LABEL" ] && [ ! -z "$ETHSTATS_LOGIN" ]; then + CMD="$CMD --EthStats.Enabled true --EthStats.Name $ETHSTATS_LABEL --EthStats.Secret $(echo $ETHSTATS_LOGIN | cut -d "@" -f1) --EthStats.Server $(echo $ETHSTATS_LOGIN | cut -d "@" -f2)" + fi + + if [ ! -z "$EC_CACHE_SIZE" ]; then + CMD="$CMD --Init.MemoryHint ${EC_CACHE_SIZE}000000" + fi + + if [ ! -z "$EC_MAX_PEERS" ]; then + CMD="$CMD --Network.MaxActivePeers $EC_MAX_PEERS" + fi + + if [ "$ENABLE_METRICS" = "true" ]; then + CMD="$CMD --Metrics.Enabled true --Metrics.ExposePort $EC_METRICS_PORT" + fi + + if [ ! -z "$EC_P2P_PORT" ]; then + CMD="$CMD --Network.DiscoveryPort $EC_P2P_PORT --Network.P2PPort $EC_P2P_PORT" + fi + + if [ ! -z "$RP_NETHERMIND_PRUNE_MEM_SIZE" ]; then + CMD="$CMD --Pruning.CacheMb $RP_NETHERMIND_PRUNE_MEM_SIZE" + fi + + exec ${CMD} + +fi + + +# Besu startup +if [ "$CLIENT" = "besu" ]; then + + # Performance tuning for ARM systems + UNAME_VAL=$(uname -m) + if [ "$UNAME_VAL" = "arm64" ] || [ "$UNAME_VAL" = "aarch64" ]; then + + # Define the performance tuning prefix + define_perf_prefix + + fi + + # Create the JWT secret + if [ ! -f "/secrets/jwtsecret" ]; then + openssl rand -hex 32 | tr -d "\n" > /secrets/jwtsecret + fi + + # Check for the prune flag and run that first if requested + if [ -f "/ethclient/prune.lock" ]; then + + $PERF_PREFIX /opt/besu/bin/besu $BESU_NETWORK --data-path=/ethclient/besu storage trie-log prune ; rm /ethclient/prune.lock + + # Run Besu normally + else + + CMD="$PERF_PREFIX /opt/besu/bin/besu \ + $BESU_NETWORK \ + --data-path=/ethclient/besu \ + --fast-sync-min-peers=3 \ + --rpc-http-enabled \ + --rpc-http-host=0.0.0.0 \ + --rpc-http-port=${EC_HTTP_PORT:-8545} \ + --rpc-ws-enabled \ + --rpc-ws-host=0.0.0.0 \ + --rpc-ws-port=${EC_WS_PORT:-8546} \ + --host-allowlist=* \ + --rpc-http-max-active-connections=1024 \ + --nat-method=docker \ + --p2p-host=$EXTERNAL_IP \ + --engine-rpc-enabled \ + --engine-rpc-port=${EC_ENGINE_PORT:-8551} \ + --engine-host-allowlist=* \ + --engine-jwt-secret=/secrets/jwtsecret \ + --Xbonsai-full-flat-db-enabled=true \ + $EC_ADDITIONAL_FLAGS" + + if [ ! -z "$EC_SUGGESTED_BLOCK_GAS_LIMIT" ]; then + CMD="$CMD --target-gas-limit=$EC_SUGGESTED_BLOCK_GAS_LIMIT" + fi + + if [ "$BESU_ARCHIVE_MODE" = "true" ]; then + CMD="$CMD --sync-mode=FULL --data-storage-format=FOREST" + else + CMD="$CMD --sync-mode=SNAP --data-storage-format=BONSAI" + fi + + if [ ! -z "$ETHSTATS_LABEL" ] && [ ! -z "$ETHSTATS_LOGIN" ]; then + CMD="$CMD --ethstats $ETHSTATS_LABEL:$ETHSTATS_LOGIN" + fi + + if [ ! -z "$EC_MAX_PEERS" ]; then + CMD="$CMD --max-peers=$EC_MAX_PEERS" + fi + + if [ "$ENABLE_METRICS" = "true" ]; then + CMD="$CMD --metrics-enabled --metrics-host=0.0.0.0 --metrics-port=$EC_METRICS_PORT" + fi + + if [ ! -z "$EC_P2P_PORT" ]; then + CMD="$CMD --p2p-port=$EC_P2P_PORT" + fi + + if [ ! -z "$BESU_MAX_BACK_LAYERS" ]; then + CMD="$CMD --bonsai-maximum-back-layers-to-load=$BESU_MAX_BACK_LAYERS" + fi + + if [ "$BESU_JVM_HEAP_SIZE" -gt "0" ]; then + CMD="env JAVA_OPTS=\"-Xmx${BESU_JVM_HEAP_SIZE}m\" $CMD" + fi + + exec ${CMD} + fi +fi + +# Reth startup +if [ "$CLIENT" = "reth" ]; then + + # Create the JWT secret + if [ ! -f "/secrets/jwtsecret" ]; then + echo -n "$(head -c 32 /dev/urandom | od -A n -t x1 | tr -d '[:space:]')" > /secrets/jwtsecret + fi + + CMD="$PERF_PREFIX /usr/local/bin/reth node $RETH_NETWORK \ + --datadir /ethclient/reth \ + --http \ + --http.addr 0.0.0.0 \ + --http.port ${EC_HTTP_PORT:-8545} \ + --http.api eth,net,web3 \ + --http.corsdomain="*" \ + --ws \ + --ws.addr 0.0.0.0 \ + --ws.port ${EC_WS_PORT:-8546} \ + --ws.api eth,net,web3 \ + --ws.origins '*' \ + --authrpc.addr 0.0.0.0 \ + --authrpc.port ${EC_ENGINE_PORT:-8551} \ + --authrpc.jwtsecret /secrets/jwtsecret \ + $EC_ADDITIONAL_FLAGS" + + if [ ! -z "$EC_SUGGESTED_BLOCK_GAS_LIMIT" ]; then + CMD="$CMD --builder.gaslimit $EC_SUGGESTED_BLOCK_GAS_LIMIT" + fi + + if [ "$ENABLE_METRICS" = "true" ]; then + CMD="$CMD --metrics 0.0.0.0:$EC_METRICS_PORT" + fi + + if [ "$RETH_ARCHIVE_MODE" = "false" ]; then + CMD="$CMD --full" + fi + + if [ ! -z "$EC_MAX_PEERS" ]; then + CMD="$CMD --max-outbound-peers=$EC_MAX_PEERS" + fi + + if [ ! -z "$RETH_MAX_INBOUND_PEERS" ]; then + CMD="$CMD --max-inbound-peers=$RETH_MAX_INBOUND_PEERS" + fi + + if [ ! -z "$EC_P2P_PORT" ]; then + CMD="$CMD --port $EC_P2P_PORT" + fi + + exec ${CMD} + +fi diff --git a/shared/services/rocketpool/assets/install/scripts/start-mev-boost.sh b/shared/services/rocketpool/assets/install/scripts/start-mev-boost.sh new file mode 100755 index 000000000..b90e37dfe --- /dev/null +++ b/shared/services/rocketpool/assets/install/scripts/start-mev-boost.sh @@ -0,0 +1,24 @@ +#!/bin/sh + +# Initialize an empty string for additional arguments +ADDITIONAL_ARGS="" + +# Check if the environment variable MEV_BOOST_ADDITIONAL_FLAGS is not empty +if [ -n "$MEV_BOOST_ADDITIONAL_FLAGS" ]; then + ADDITIONAL_ARGS=$MEV_BOOST_ADDITIONAL_FLAGS + +fi + +# Set up the network-based flag +if [ "$NETWORK" = "mainnet" ]; then + MEV_NETWORK="mainnet" +elif [ "$NETWORK" = "testnet" ]; then + MEV_NETWORK="hoodi" +elif [ "$NETWORK" = "devnet" ]; then + MEV_NETWORK="hoodi" +else + echo "Unknown network [$NETWORK]" + exit 1 +fi + +exec /app/mev-boost -${MEV_NETWORK} -addr 0.0.0.0:${MEV_BOOST_PORT} -relay-check -relays ${MEV_BOOST_RELAYS} ${ADDITIONAL_ARGS} diff --git a/shared/services/rocketpool/assets/install/scripts/start-vc.sh b/shared/services/rocketpool/assets/install/scripts/start-vc.sh new file mode 100755 index 000000000..8151fda51 --- /dev/null +++ b/shared/services/rocketpool/assets/install/scripts/start-vc.sh @@ -0,0 +1,269 @@ +#!/bin/sh +# This script launches ETH2 validator clients for Rocket Pool's docker stack; only edit if you know what you're doing ;) + +GWW_GRAFFITI_FILE="/addons/gww/graffiti.txt" + +# Set up the network-based flags +if [ "$NETWORK" = "mainnet" ]; then + LH_NETWORK="mainnet" + LODESTAR_NETWORK="mainnet" + PRYSM_NETWORK="--mainnet" + TEKU_NETWORK="mainnet" +elif [ "$NETWORK" = "devnet" ]; then + LH_NETWORK="hoodi" + LODESTAR_NETWORK="hoodi" + PRYSM_NETWORK="--hoodi" + TEKU_NETWORK="hoodi" +elif [ "$NETWORK" = "testnet" ]; then + LH_NETWORK="hoodi" + LODESTAR_NETWORK="hoodi" + PRYSM_NETWORK="--hoodi" + TEKU_NETWORK="hoodi" +else + echo "Unknown network [$NETWORK]" + exit 1 +fi + +# Report a missing fee recipient file +if [ ! -f "/validators/$FEE_RECIPIENT_FILE" ]; then + echo "Fee recipient file not found, please wait for the rocketpool_node process to create one." + exit 1 +fi + + +# Lighthouse startup +if [ "$CC_CLIENT" = "lighthouse" ]; then + + # Set up the CC + fallback string + CC_URL_STRING=$CC_API_ENDPOINT + if [ ! -z "$FALLBACK_CC_API_ENDPOINT" ]; then + CC_URL_STRING="$CC_API_ENDPOINT,$FALLBACK_CC_API_ENDPOINT" + fi + + CMD="/usr/local/bin/lighthouse validator \ + --network $LH_NETWORK \ + --datadir /validators/lighthouse \ + --init-slashing-protection \ + --logfile-max-number 0 \ + --beacon-nodes $CC_URL_STRING \ + --suggested-fee-recipient $(cat /validators/$FEE_RECIPIENT_FILE) \ + $VC_ADDITIONAL_FLAGS" + + if [ ! -z "$VC_SUGGESTED_BLOCK_GAS_LIMIT" ]; then + CMD="$CMD --gas-limit $VC_SUGGESTED_BLOCK_GAS_LIMIT" + fi + + if [ "$DOPPELGANGER_DETECTION" = "true" ]; then + CMD="$CMD --enable-doppelganger-protection" + fi + + if [ "$ENABLE_MEV_BOOST" = "true" ]; then + CMD="$CMD --builder-proposals --prefer-builder-proposals" + fi + + if [ "$ENABLE_METRICS" = "true" ]; then + CMD="$CMD --metrics --metrics-address 0.0.0.0 --metrics-port $VC_METRICS_PORT" + fi + + if [ "$ENABLE_BITFLY_NODE_METRICS" = "true" ]; then + CMD="$CMD --monitoring-endpoint $BITFLY_NODE_METRICS_ENDPOINT?apikey=$BITFLY_NODE_METRICS_SECRET&machine=$BITFLY_NODE_METRICS_MACHINE_NAME" + fi + + if [ "$ADDON_GWW_ENABLED" = "true" ]; then + echo "default: $GRAFFITI" > $GWW_GRAFFITI_FILE # Default graffiti value for Lighthouse + exec ${CMD} --graffiti-file $GWW_GRAFFITI_FILE + else + exec ${CMD} --graffiti "$GRAFFITI" + fi + +fi + +# Lodestar startup +if [ "$CC_CLIENT" = "lodestar" ]; then + + # Remove any lock files that were left over accidentally after an unclean shutdown + find /validators/lodestar/validators -name voting-keystore.json.lock -delete + + # Set up the CC + fallback string + CC_URL_STRING=$CC_API_ENDPOINT + if [ ! -z "$FALLBACK_CC_API_ENDPOINT" ]; then + CC_URL_STRING="$CC_API_ENDPOINT,$FALLBACK_CC_API_ENDPOINT" + fi + + CMD="/usr/app/node_modules/.bin/lodestar validator \ + --network $LODESTAR_NETWORK \ + --dataDir /validators/lodestar \ + --beacon-nodes $CC_URL_STRING \ + $FALLBACK_CC_STRING \ + --keystoresDir /validators/lodestar/validators \ + --secretsDir /validators/lodestar/secrets \ + --suggestedFeeRecipient $(cat /validators/$FEE_RECIPIENT_FILE) \ + $VC_ADDITIONAL_FLAGS" + +if [ ! -z "$VC_SUGGESTED_BLOCK_GAS_LIMIT" ]; then + CMD="$CMD --defaultGasLimit $VC_SUGGESTED_BLOCK_GAS_LIMIT" + fi + + if [ "$DOPPELGANGER_DETECTION" = "true" ]; then + CMD="$CMD --doppelgangerProtection" + fi + + if [ "$ENABLE_MEV_BOOST" = "true" ]; then + CMD="$CMD --builder" + fi + + if [ "$ENABLE_METRICS" = "true" ]; then + CMD="$CMD --metrics --metrics.address 0.0.0.0 --metrics.port $VC_METRICS_PORT" + fi + + if [ "$ENABLE_BITFLY_NODE_METRICS" = "true" ]; then + CMD="$CMD --monitoring.endpoint $BITFLY_NODE_METRICS_ENDPOINT?apikey=$BITFLY_NODE_METRICS_SECRET&machine=$BITFLY_NODE_METRICS_MACHINE_NAME" + fi + + exec ${CMD} --graffiti "$GRAFFITI" + +fi + + +# Nimbus startup +if [ "$CC_CLIENT" = "nimbus" ]; then + + # Nimbus won't start unless the validator directories already exist + mkdir -p /validators/nimbus/validators + mkdir -p /validators/nimbus/secrets + + # Set up the fallback arg + if [ ! -z "$FALLBACK_CC_API_ENDPOINT" ]; then + FALLBACK_CC_ARG="--beacon-node=$FALLBACK_CC_API_ENDPOINT" + fi + + CMD="/home/user/nimbus_validator_client \ + --non-interactive \ + --beacon-node=$CC_API_ENDPOINT $FALLBACK_CC_ARG \ + --data-dir=/ethclient/nimbus_vc \ + --validators-dir=/validators/nimbus/validators \ + --secrets-dir=/validators/nimbus/secrets \ + --doppelganger-detection=$DOPPELGANGER_DETECTION \ + --suggested-fee-recipient=$(cat /validators/$FEE_RECIPIENT_FILE) \ + --block-monitor-type=event \ + $VC_ADDITIONAL_FLAGS" + + if [ "$ENABLE_MEV_BOOST" = "true" ]; then + CMD="$CMD --payload-builder" + fi + + if [ "$ENABLE_METRICS" = "true" ]; then + CMD="$CMD --metrics --metrics-address=0.0.0.0 --metrics-port=$VC_METRICS_PORT" + fi + + # Graffiti breaks if it's in the CMD string instead of here because of spaces + exec ${CMD} --graffiti="$GRAFFITI" + +fi + + +# Prysm startup +if [ "$CC_CLIENT" = "prysm" ]; then + + # Make the Prysm dir + mkdir -p /validators/prysm-non-hd/ + + # Set up the CC + fallback string + CC_URL_STRING=$CC_RPC_ENDPOINT + if [ ! -z "$FALLBACK_CC_RPC_ENDPOINT" ]; then + CC_URL_STRING="$CC_RPC_ENDPOINT,$FALLBACK_CC_RPC_ENDPOINT" + fi + + CMD="/app/cmd/validator/validator \ + --accept-terms-of-use \ + $PRYSM_NETWORK \ + --datadir /validators/prysm-non-hd/direct \ + --wallet-dir /validators/prysm-non-hd \ + --wallet-password-file /validators/prysm-non-hd/direct/accounts/secret \ + --beacon-rpc-provider $CC_URL_STRING \ + --suggested-fee-recipient $(cat /validators/$FEE_RECIPIENT_FILE) \ + $VC_ADDITIONAL_FLAGS" + + if [ ! -z "$VC_SUGGESTED_BLOCK_GAS_LIMIT" ]; then + CMD="$CMD --suggested-gas-limit=$VC_SUGGESTED_BLOCK_GAS_LIMIT" + fi + + if [ "$ENABLE_MEV_BOOST" = "true" ]; then + CMD="$CMD --enable-builder" + fi + + if [ "$DOPPELGANGER_DETECTION" = "true" ]; then + CMD="$CMD --enable-doppelganger" + fi + + if [ "$ENABLE_METRICS" = "true" ]; then + CMD="$CMD --monitoring-host 0.0.0.0 --monitoring-port $VC_METRICS_PORT" + else + CMD="$CMD --disable-account-metrics" + fi + + if [ "$ADDON_GWW_ENABLED" = "true" ]; then + echo "ordered:\n - $GRAFFITI" > $GWW_GRAFFITI_FILE # Default graffiti value for Prysm + exec ${CMD} --graffiti-file=$GWW_GRAFFITI_FILE + else + exec ${CMD} --graffiti "$GRAFFITI" + fi + +fi + + +# Teku startup +if [ "$CC_CLIENT" = "teku" ]; then + + # Teku won't start unless the validator directories already exist + mkdir -p /validators/teku/keys + mkdir -p /validators/teku/passwords + + # Remove any lock files that were left over accidentally after an unclean shutdown + rm -f /validators/teku/keys/*.lock + + # Set up the CC + fallback string + CC_URL_STRING=$CC_API_ENDPOINT + if [ ! -z "$FALLBACK_CC_API_ENDPOINT" ]; then + CC_URL_STRING="$CC_API_ENDPOINT,$FALLBACK_CC_API_ENDPOINT" + fi + + CMD="/opt/teku/bin/teku validator-client \ + --network=$TEKU_NETWORK \ + --data-path=/validators/teku \ + --validator-keys=/validators/teku/keys:/validators/teku/passwords \ + --beacon-node-api-endpoints=$CC_URL_STRING \ + --validators-keystore-locking-enabled=false \ + --log-destination=CONSOLE \ + --validators-proposer-default-fee-recipient=$(cat /validators/$FEE_RECIPIENT_FILE) \ + $VC_ADDITIONAL_FLAGS" + + if [ "$DOPPELGANGER_DETECTION" = "true" ]; then + CMD="$CMD --doppelganger-detection-enabled" + fi + + if [ "$ENABLE_MEV_BOOST" = "true" ]; then + CMD="$CMD --validators-builder-registration-default-enabled=true" + fi + + if [ "$TEKU_USE_SLASHING_PROTECTION" = "true"]; then + CMD="$CMD --shut-down-when-validator-slashed-enabled=true" + fi + + if [ "$ENABLE_METRICS" = "true" ]; then + CMD="$CMD --metrics-enabled=true --metrics-interface=0.0.0.0 --metrics-port=$VC_METRICS_PORT --metrics-host-allowlist=*" + fi + + if [ "$ENABLE_BITFLY_NODE_METRICS" = "true" ]; then + CMD="$CMD --metrics-publish-endpoint=$BITFLY_NODE_METRICS_ENDPOINT?apikey=$BITFLY_NODE_METRICS_SECRET&machine=$BITFLY_NODE_METRICS_MACHINE_NAME" + fi + + if [ "$ADDON_GWW_ENABLED" = "true" ]; then + echo "$GRAFFITI" > $GWW_GRAFFITI_FILE # Default graffiti value for Teku + exec ${CMD} --validators-graffiti-file=$GWW_GRAFFITI_FILE + else + exec ${CMD} --validators-graffiti="$GRAFFITI" + fi + +fi + diff --git a/shared/services/rocketpool/assets/install/scripts/stop-validator.sh b/shared/services/rocketpool/assets/install/scripts/stop-validator.sh new file mode 100755 index 000000000..19934ac64 --- /dev/null +++ b/shared/services/rocketpool/assets/install/scripts/stop-validator.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +# This script is used to stop your Validator Client service. +# It is run in Native Mode only when the Smartnode needs to stop your Validator Client because of a misconfiguration or other error. +# It is not used in Docker Mode or Hybrid Mode. + +# The command below is an example only. +# Replace it with your own command to stop your Validator Client service. + +#sudo systemctl stop lighthouse-validator diff --git a/shared/services/rocketpool/assets/install/templates/addons/gww/addon_gww.tmpl b/shared/services/rocketpool/assets/install/templates/addons/gww/addon_gww.tmpl new file mode 100644 index 000000000..e088ea881 --- /dev/null +++ b/shared/services/rocketpool/assets/install/templates/addons/gww/addon_gww.tmpl @@ -0,0 +1,34 @@ +# Autogenerated - DO NOT MODIFY THIS FILE DIRECTLY +# If you want to overwrite some of these values with your own customizations, +# please add them to `override/api.yml`. +# +# See https://docs.docker.com/compose/extends/#adding-and-overriding-configuration +# for more information on overriding specific parameters of docker-compose files. + +services: + addon_gww: + image: {{.GraffitiWallWriter.GetConfig.ContainerTag}} + user: root + container_name: {{.Smartnode.ProjectName}}_addon_gww + restart: unless-stopped + volumes: + - {{.RocketPoolDirectory}}/addons/gww:/gww + networks: + - net + entrypoint: + - /go/bin/drawer + - --output_file=/gww/graffiti.txt + - --input_url={{.GraffitiWallWriter.GetConfig.InputURL}} + - --consensus_client={{if .ConsensusClientLocal}}{{.ConsensusClient}}{{else}}{{.ExternalConsensusClient}}{{end}} + - --nimbus_url=http://{{.GetConsensusHostname}}:{{or .ConsensusCommon.ApiPort}} + - --graffiti_prefix={{.GraffitiPrefix}} + - --network={{.Smartnode.Network}} + - --update_wall_time={{.GraffitiWallWriter.GetConfig.UpdateWallTime}} + - --update_input_time={{.GraffitiWallWriter.GetConfig.UpdateInputTime}} + - --update_pixel_time={{.GraffitiWallWriter.GetConfig.UpdatePixelTime}} + cap_drop: + - all + security_opt: + - no-new-privileges +networks: + net: diff --git a/shared/services/rocketpool/assets/install/templates/alertmanager.tmpl b/shared/services/rocketpool/assets/install/templates/alertmanager.tmpl new file mode 100644 index 000000000..9f4544124 --- /dev/null +++ b/shared/services/rocketpool/assets/install/templates/alertmanager.tmpl @@ -0,0 +1,25 @@ +# Autogenerated - DO NOT MODIFY THIS FILE DIRECTLY +# If you want to overwrite some of these values with your own customizations, +# please add them to `override/alertmanager.yml`. +# +# See https://docs.docker.com/compose/extends/#adding-and-overriding-configuration +# for more information on overriding specific parameters of docker-compose files. + +services: + alertmanager: + image: {{.Alertmanager.ContainerTag}} + container_name: {{.Smartnode.ProjectName}}_alertmanager + restart: unless-stopped + command: + # https://prometheus.io/docs/alerting/latest/configuration/ + - "--config.file=/etc/alertmanager/alertmanager.yml" + ports: [{{.Alertmanager.GetOpenPorts}}] + volumes: + - "{{.RocketPoolDirectory}}/alerting/alertmanager.yml:/etc/alertmanager/alertmanager.yml" + - "alertmanager-data:/alertmanager" + networks: + - net +networks: + net: +volumes: + alertmanager-data: diff --git a/shared/services/rocketpool/assets/install/templates/api.tmpl b/shared/services/rocketpool/assets/install/templates/api.tmpl new file mode 100644 index 000000000..648d17d9d --- /dev/null +++ b/shared/services/rocketpool/assets/install/templates/api.tmpl @@ -0,0 +1,30 @@ +# Autogenerated - DO NOT MODIFY THIS FILE DIRECTLY +# If you want to overwrite some of these values with your own customizations, +# please add them to `override/api.yml`. +# +# See https://docs.docker.com/compose/extends/#adding-and-overriding-configuration +# for more information on overriding specific parameters of docker-compose files. + +services: + api: + image: {{.Smartnode.GetSmartnodeContainerTag}} + container_name: {{.Smartnode.ProjectName}}_api + restart: unless-stopped + stop_signal: SIGKILL + stop_grace_period: 1s + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - {{.RocketPoolDirectory}}:/.rocketpool + - {{.Smartnode.DataPath}}:/.rocketpool/data + networks: + - net + entrypoint: /bin/sleep + command: "infinity" + cap_drop: + - all + cap_add: + - dac_override + security_opt: + - no-new-privileges +networks: + net: diff --git a/shared/services/rocketpool/assets/install/templates/eth1.tmpl b/shared/services/rocketpool/assets/install/templates/eth1.tmpl new file mode 100644 index 000000000..fa2134522 --- /dev/null +++ b/shared/services/rocketpool/assets/install/templates/eth1.tmpl @@ -0,0 +1,70 @@ +# Autogenerated - DO NOT MODIFY THIS FILE DIRECTLY +# If you want to overwrite some of these values with your own customizations, +# please add them to `override/eth1.yml`. +# +# See https://docs.docker.com/compose/extends/#adding-and-overriding-configuration +# for more information on overriding specific parameters of docker-compose files. + +services: + eth1: + image: {{.GetECContainerTag}} + user: root + container_name: {{.Smartnode.ProjectName}}_eth1 + restart: unless-stopped + stop_signal: {{.GetECStopSignal}} + stop_grace_period: 15m + {{- $p2p := (or .ExecutionCommon.P2pPort.Value "30303")}} + ports: [ "{{$p2p}}:{{$p2p}}/udp", "{{$p2p}}:{{$p2p}}/tcp"{{.GetECOpenAPIPorts}} ] + volumes: + - eth1clientdata:/ethclient + - {{.RocketPoolDirectory}}/scripts:/setup:ro + - {{.Smartnode.DataPath}}/secrets:/secrets + networks: + - net + environment: + - NETWORK={{.Smartnode.Network}} + - CLIENT={{.ExecutionClient}} + - ETHSTATS_LABEL={{.ExecutionCommon.EthstatsLabel}} + - ETHSTATS_LOGIN={{.ExecutionCommon.EthstatsLogin}} + - EC_MAX_PEERS={{.GetECMaxPeers}} + - EC_P2P_PORT={{$p2p}} + - EC_SUGGESTED_BLOCK_GAS_LIMIT={{.ExecutionCommon.SuggestedBlockGasLimit}} + - EC_ADDITIONAL_FLAGS={{.GetECAdditionalFlags}} + - EC_HTTP_PORT={{.ExecutionCommon.HttpPort}} + - EC_WS_PORT={{.ExecutionCommon.WsPort}} + - EC_ENGINE_PORT={{.ExecutionCommon.EnginePort}} + - EXTERNAL_IP={{.GetExternalIp}} + - ENABLE_METRICS={{.EnableMetrics}} + - EC_METRICS_PORT={{.EcMetricsPort}} + {{- /* Client-specific values */}} + {{- if eq .ExecutionClient.String "besu"}} + - BESU_MAX_BACK_LAYERS={{.Besu.MaxBackLayers}} + - BESU_JVM_HEAP_SIZE={{.Besu.JvmHeapSize}} + - BESU_ARCHIVE_MODE={{.Besu.ArchiveMode}} + {{- else if eq .ExecutionClient.String "nethermind"}} + - EC_CACHE_SIZE={{.Nethermind.CacheSize}} + - RP_NETHERMIND_PRUNE_MEM_SIZE={{.Nethermind.PruneMemSize}} + - RP_NETHERMIND_ADDITIONAL_MODULES={{.Nethermind.AdditionalModules}} + - RP_NETHERMIND_ADDITIONAL_URLS={{.Nethermind.AdditionalUrls}} + - RP_NETHERMIND_FULL_PRUNE_MEMORY_BUDGET={{.Nethermind.FullPruneMemoryBudget}} + - RP_NETHERMIND_FULL_PRUNING_MAX_DEGREE_PARALLELISM={{.Nethermind.FullPruningMaxDegreeOfParallelism}} + - RP_NETHERMIND_FULL_PRUNING_THRESHOLD_MB={{.Nethermind.FullPruningThresholdMb}} + {{- else if eq .ExecutionClient.String "geth"}} + - GETH_ARCHIVE_MODE={{.Geth.ArchiveMode}} + - GETH_EVM_TIMEOUT={{.Geth.EvmTimeout}} + {{- else if eq .ExecutionClient.String "reth"}} + - RETH_ARCHIVE_MODE={{.Reth.ArchiveMode}} + - RETH_MAX_INBOUND_PEERS={{.Reth.MaxInboundPeers}} + {{- end}} + entrypoint: sh + command: "/setup/start-ec.sh" + cap_drop: + - all + cap_add: + - dac_override + security_opt: + - no-new-privileges +networks: + net: +volumes: + eth1clientdata: diff --git a/shared/services/rocketpool/assets/install/templates/eth2.tmpl b/shared/services/rocketpool/assets/install/templates/eth2.tmpl new file mode 100644 index 000000000..1852ec903 --- /dev/null +++ b/shared/services/rocketpool/assets/install/templates/eth2.tmpl @@ -0,0 +1,110 @@ +# Autogenerated - DO NOT MODIFY THIS FILE DIRECTLY +# If you want to overwrite some of these values with your own customizations, +# please add them to `override/eth2.yml`. +# +# See https://docs.docker.com/compose/extends/#adding-and-overriding-configuration +# for more information on overriding specific parameters of docker-compose files. + +{{define "GENESIS_DL"}} + eth2_genesis_downloader: + image: curlimages/curl:8.13.0 + user: root + container_name: {{.Smartnode.ProjectName}}_genesis_downloader + volumes: + - eth2clientdata:/ethclient + - {{.RocketPoolDirectory}}/scripts:/setup:ro + network_mode: host + environment: + - NETWORK={{.Smartnode.Network}} + entrypoint: sh + command: "/setup/download-genesis.sh" +{{end}} + +services: + eth2: + image: {{.GetBeaconContainerTag}} + user: root + container_name: {{.Smartnode.ProjectName}}_eth2 + restart: unless-stopped + stop_grace_period: 3m + {{- $p2p := (or .ConsensusCommon.P2pPort.String "9001")}} + ports: + - "{{$p2p}}:{{$p2p}}/udp" + - "{{$p2p}}:{{$p2p}}/tcp" + {{- if eq .ConsensusClient.String "lighthouse"}} + - "{{.Lighthouse.P2pQuicPort}}:{{.Lighthouse.P2pQuicPort}}/udp" + {{- else if eq .ConsensusClient.String "prysm"}} + - "{{.Prysm.P2pQuicPort}}:{{.Prysm.P2pQuicPort}}/udp" + {{- end}} + {{- range $entry := .GetBnOpenPorts}} + - "{{$entry}}" + {{- end}} + volumes: + - {{.Smartnode.DataPath}}/validators:/validators + - {{.Smartnode.DataPath}}/secrets:/secrets:ro + - eth2clientdata:/ethclient + - {{.RocketPoolDirectory}}/scripts:/setup:ro + networks: + - net + environment: + - NETWORK={{.Smartnode.Network}} + - EC_CLIENT={{if .ExecutionClientLocal}}{{.ExecutionClient}}{{else}}X{{end}} + - CC_CLIENT={{if .ConsensusClientLocal}}{{.ConsensusClient}}{{else}}{{.ExternalConsensusClient}}{{end}} + - EC_HTTP_ENDPOINT={{.GetEcHttpEndpoint}} + - EC_WS_ENDPOINT={{.GetEcWsEndpoint}} + - EC_ENGINE_ENDPOINT=http://{{.GetExecutionHostname}}:{{.ExecutionCommon.EnginePort}} + - EC_ENGINE_WS_ENDPOINT=ws://{{.GetExecutionHostname}}:{{.ExecutionCommon.EnginePort}} + - CUSTOM_GRAFFITI={{.CustomGraffiti}} + - ROCKET_POOL_VERSION=v{{.RocketPoolVersion}} + - BN_SUGGESTED_BLOCK_GAS_LIMIT={{.ConsensusCommon.SuggestedBlockGasLimit}} + - BN_P2P_PORT={{$p2p}} + - BN_API_PORT={{.ConsensusCommon.ApiPort}} + - BN_MAX_PEERS={{.GetBNMaxPeers}} + - ENABLE_METRICS={{.EnableMetrics}} + - BN_METRICS_PORT={{.BnMetricsPort}} + - EXTERNAL_IP={{.GetExternalIp}} + - CHECKPOINT_SYNC_URL={{.ConsensusCommon.CheckpointSyncProvider}} + - DOPPELGANGER_DETECTION={{.IsDoppelgangerEnabled}} + - BN_ADDITIONAL_FLAGS={{.GetBNAdditionalFlags}} + - FEE_RECIPIENT_FILE={{.FeeRecipientFile}} + - ENABLE_BITFLY_NODE_METRICS={{.EnableBitflyNodeMetrics}} + - BITFLY_NODE_METRICS_SECRET={{.BitflyNodeMetrics.Secret}} + - BITFLY_NODE_METRICS_ENDPOINT={{.BitflyNodeMetrics.Endpoint}} + - BITFLY_NODE_METRICS_MACHINE_NAME={{.BitflyNodeMetrics.MachineName}} + - MEV_BOOST_URL={{.MevBoostUrl}} + - RETH_ADDRESS={{.Smartnode.GetRethAddress.Hex}} + - GRAFFITI={{.Graffiti}} + - ADDON_GWW_ENABLED={{.GraffitiWallWriter.GetEnabledParameter}} + {{- /* Client-specific values */}} + {{- if eq .ConsensusClient.String "teku"}} + - TEKU_JVM_HEAP_SIZE={{.Teku.JvmHeapSize}} + - TEKU_ARCHIVE_MODE={{.Teku.ArchiveMode}} + {{- else if eq .ConsensusClient.String "prysm"}} + - BN_RPC_PORT={{.Prysm.RpcPort}} + - BN_P2P_QUIC_PORT={{.Prysm.P2pQuicPort}} + {{- else if eq .ConsensusClient.String "nimbus"}} + - NIMBUS_PRUNING_MODE={{.Nimbus.PruningMode}} + {{- else if eq .ConsensusClient.String "lighthouse"}} + - BN_P2P_QUIC_PORT={{.Lighthouse.P2pQuicPort}} + {{- end}} + command: "/setup/start-bn.sh" + cap_drop: + - all + cap_add: + - dac_override + security_opt: + - no-new-privileges + {{- if ne .ConsensusClient.String "prysm"}} + entrypoint: sh + {{- else}} + {{- /* prysm has bash but not sh and may need us to download genesis for testnets */}} + entrypoint: bash + depends_on: + eth2_genesis_downloader: + condition: service_completed_successfully + {{- template "GENESIS_DL" . }} + {{- end}} +networks: + net: +volumes: + eth2clientdata: diff --git a/shared/services/rocketpool/assets/install/templates/exporter.tmpl b/shared/services/rocketpool/assets/install/templates/exporter.tmpl new file mode 100644 index 000000000..f254922f9 --- /dev/null +++ b/shared/services/rocketpool/assets/install/templates/exporter.tmpl @@ -0,0 +1,36 @@ +# Autogenerated - DO NOT MODIFY THIS FILE DIRECTLY +# If you want to overwrite some of these values with your own customizations, +# please add them to `override/exporter.yml`. +# +# See https://docs.docker.com/compose/extends/#adding-and-overriding-configuration +# for more information on overriding specific parameters of docker-compose files. + +services: + node-exporter: + image: {{.Exporter.ContainerTag}} + container_name: {{.Smartnode.ProjectName}}_exporter + cap_drop: + - ALL + user: "65534:65534" + restart: unless-stopped + command: + - "--path.procfs=/host/proc" + - "--path.sysfs=/host/sys" + - "--collector.textfile.directory=/host/textfile_collector" + - "--web.listen-address=:{{or .ExporterMetricsPort.Value "9103"}}" + {{- if .Exporter.RootFs.Value }} + - "--path.rootfs=/rootfs" + {{- end}} + {{- range $flag := .GetExporterAdditionalFlags}} + - "{{$flag}}" + {{- end}} + volumes: + - "/proc:/host/proc:ro,rslave" + - "/sys:/host/sys:ro,rslave" + - "/var/lib/node_exporter/textfile_collector:/host/textfile_collector:ro" + {{- if .Exporter.RootFs.Value }} + - "/:/rootfs:ro,rslave" + {{- end}} + network_mode: host +networks: + net: diff --git a/shared/services/rocketpool/assets/install/templates/grafana-prometheus-datasource.tmpl b/shared/services/rocketpool/assets/install/templates/grafana-prometheus-datasource.tmpl new file mode 100644 index 000000000..f90b695d5 --- /dev/null +++ b/shared/services/rocketpool/assets/install/templates/grafana-prometheus-datasource.tmpl @@ -0,0 +1,16 @@ +apiVersion: 1 + +deleteDatasources: + - name: Prometheus + orgId: 1 + +datasources: + - name: Prometheus + type: prometheus + access: proxy + orgId: 1 + url: http://prometheus:9091 + basicAuth: false + isDefault: true + version: 1 + editable: true diff --git a/shared/services/rocketpool/assets/install/templates/grafana.tmpl b/shared/services/rocketpool/assets/install/templates/grafana.tmpl new file mode 100644 index 000000000..e8d7ff0e2 --- /dev/null +++ b/shared/services/rocketpool/assets/install/templates/grafana.tmpl @@ -0,0 +1,28 @@ +# Autogenerated - DO NOT MODIFY THIS FILE DIRECTLY +# If you want to overwrite some of these values with your own customizations, +# please add them to `override/grafana.yml`. +# +# See https://docs.docker.com/compose/extends/#adding-and-overriding-configuration +# for more information on overriding specific parameters of docker-compose files. + +{{$port := (or .Grafana.Port.Value 3100) -}} +services: + grafana: + image: {{.Grafana.ContainerTag}} + container_name: {{.Smartnode.ProjectName}}_grafana + restart: unless-stopped + environment: + - GF_SERVER_HTTP_PORT={{$port}} + ports: + - "{{$port}}:{{$port}}/tcp" + volumes: + - "{{.RocketPoolDirectory}}/grafana-prometheus-datasource.yml:/etc/grafana/provisioning/datasources/prometheus.yml" + - "{{.RocketPoolDirectory}}/grafana-dashboards.yml:/etc/grafana/provisioning/dashboards/rocketpool.yml" + - "{{.RocketPoolDirectory}}/dashboards:/etc/grafana/default_dashboards" + - "grafana-storage:/var/lib/grafana" + networks: + - net +networks: + net: +volumes: + grafana-storage: diff --git a/shared/services/rocketpool/assets/install/templates/mev-boost.tmpl b/shared/services/rocketpool/assets/install/templates/mev-boost.tmpl new file mode 100644 index 000000000..5e8345be8 --- /dev/null +++ b/shared/services/rocketpool/assets/install/templates/mev-boost.tmpl @@ -0,0 +1,32 @@ +# Autogenerated - DO NOT MODIFY THIS FILE DIRECTLY +# If you want to overwrite some of these values with your own customizations, +# please add them to `override/mev-boost.yml`. +# +# See https://docs.docker.com/compose/extends/#adding-and-overriding-configuration +# for more information on overriding specific parameters of docker-compose files. + +services: + mev-boost: + image: {{.MevBoost.ContainerTag}} + container_name: {{.Smartnode.ProjectName}}_mev-boost + restart: unless-stopped + ports: [{{.GetMevBoostOpenPorts}}] + volumes: + - {{.RocketPoolDirectory}}/scripts:/setup:ro + networks: + - net + environment: + - NETWORK={{.Smartnode.Network}} + - MEV_BOOST_PORT={{.MevBoost.Port}} + - MEV_BOOST_RELAYS={{.MevBoost.GetRelayString}} + - MEV_BOOST_ADDITIONAL_FLAGS={{.MevBoost.AdditionalFlags}} + entrypoint: sh + command: "/setup/start-mev-boost.sh" + cap_drop: + - all + cap_add: + - dac_override + security_opt: + - no-new-privileges +networks: + net: diff --git a/shared/services/rocketpool/assets/install/templates/node.tmpl b/shared/services/rocketpool/assets/install/templates/node.tmpl new file mode 100644 index 000000000..1fe7025d4 --- /dev/null +++ b/shared/services/rocketpool/assets/install/templates/node.tmpl @@ -0,0 +1,27 @@ +# Autogenerated - DO NOT MODIFY THIS FILE DIRECTLY +# If you want to overwrite some of these values with your own customizations, +# please add them to `override/node.yml`. +# +# See https://docs.docker.com/compose/extends/#adding-and-overriding-configuration +# for more information on overriding specific parameters of docker-compose files. + +services: + node: + image: {{.Smartnode.GetSmartnodeContainerTag}} + container_name: {{.Smartnode.ProjectName}}_node + restart: unless-stopped + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - {{.RocketPoolDirectory}}:/.rocketpool + - {{.Smartnode.DataPath}}:/.rocketpool/data + networks: + - net + command: "-m 0.0.0.0 -r {{or .NodeMetricsPort.Value "9102"}} node" + cap_drop: + - all + cap_add: + - dac_override + security_opt: + - no-new-privileges +networks: + net: diff --git a/shared/services/rocketpool/assets/install/templates/prometheus.tmpl b/shared/services/rocketpool/assets/install/templates/prometheus.tmpl new file mode 100644 index 000000000..dc5cfc13a --- /dev/null +++ b/shared/services/rocketpool/assets/install/templates/prometheus.tmpl @@ -0,0 +1,32 @@ +# Autogenerated - DO NOT MODIFY THIS FILE DIRECTLY +# If you want to overwrite some of these values with your own customizations, +# please add them to `override/prometheus.yml`. +# +# See https://docs.docker.com/compose/extends/#adding-and-overriding-configuration +# for more information on overriding specific parameters of docker-compose files. + +services: + prometheus: + image: {{.Prometheus.ContainerTag}} + container_name: {{.Smartnode.ProjectName}}_prometheus + restart: unless-stopped + command: + - "--web.listen-address=:{{or .Prometheus.Port.Value "9091"}}" + - "--config.file=/etc/prometheus/prometheus.yml" + {{- range $flag := .GetPrometheusAdditionalFlags}} + - "{{$flag}}" + {{- end}} + ports: [{{.GetPrometheusOpenPorts}}] + volumes: + - "{{.RocketPoolDirectory}}/prometheus.yml:/etc/prometheus/prometheus.yml" + - "{{.RocketPoolDirectory}}/extra-scrape-jobs:/extra-scrape-jobs" + - "{{.RocketPoolDirectory}}/alerting/rules:/alerting/rules" + - "prometheus-data:/prometheus" + networks: + - net + extra_hosts: + - "host.docker.internal:host-gateway" +networks: + net: +volumes: + prometheus-data: diff --git a/shared/services/rocketpool/assets/install/templates/validator.tmpl b/shared/services/rocketpool/assets/install/templates/validator.tmpl new file mode 100644 index 000000000..d4f98608e --- /dev/null +++ b/shared/services/rocketpool/assets/install/templates/validator.tmpl @@ -0,0 +1,61 @@ +# Autogenerated - DO NOT MODIFY THIS FILE DIRECTLY +# If you want to overwrite some of these values with your own customizations, +# please add them to `override/validator.yml`. +# +# See https://docs.docker.com/compose/extends/#adding-and-overriding-configuration +# for more information on overriding specific parameters of docker-compose files. + +services: + validator: + image: {{.GetVCContainerTag}} + user: root + container_name: {{.Smartnode.ProjectName}}_validator + restart: unless-stopped + stop_grace_period: 3m + volumes: + - {{.Smartnode.DataPath}}/validators:/validators + - {{.RocketPoolDirectory}}/scripts:/setup:ro + - {{.RocketPoolDirectory}}/addons:/addons + networks: + - net + environment: + - NETWORK={{.Smartnode.Network}} + - EC_CLIENT={{if .ExecutionClientLocal}}{{.ExecutionClient}}{{else}}X{{end}} + - CC_CLIENT={{if .ConsensusClientLocal}}{{.ConsensusClient}}{{else}}{{.ExternalConsensusClient}}{{end}} + - CC_API_ENDPOINT={{.ConsensusClientApiUrl}} + - CC_RPC_ENDPOINT={{.ConsensusClientRpcUrl}} + - FALLBACK_CC_API_ENDPOINT={{.FallbackCcApiUrl}} + - FALLBACK_CC_RPC_ENDPOINT={{.FallbackCcRpcUrl}} + - CUSTOM_GRAFFITI={{.CustomGraffiti}} + - ROCKET_POOL_VERSION=v{{.RocketPoolVersion}} + - ENABLE_METRICS={{.EnableMetrics}} + - VC_SUGGESTED_BLOCK_GAS_LIMIT={{.ConsensusCommon.SuggestedBlockGasLimit}} + - VC_METRICS_PORT={{.VcMetricsPort}} + - DOPPELGANGER_DETECTION={{.IsDoppelgangerEnabled}} + - VC_ADDITIONAL_FLAGS={{.VcAdditionalFlags}} + - FEE_RECIPIENT_FILE={{.FeeRecipientFile}} + - ENABLE_BITFLY_NODE_METRICS={{.EnableBitflyNodeMetrics}} + - BITFLY_NODE_METRICS_SECRET={{.BitflyNodeMetrics.Secret}} + - BITFLY_NODE_METRICS_ENDPOINT={{.BitflyNodeMetrics.Endpoint}} + - BITFLY_NODE_METRICS_MACHINE_NAME={{.BitflyNodeMetrics.MachineName}} + - GRAFFITI={{.Graffiti}} + - ADDON_GWW_ENABLED={{.GraffitiWallWriter.GetEnabledParameter}} + - MEV_BOOST_URL={{.MevBoostUrl}} + - ENABLE_MEV_BOOST={{.EnableMevBoost}} + {{- if eq .ConsensusClient.String "teku"}} + - TEKU_USE_SLASHING_PROTECTION={{.Teku.UseSlashingProtection}} + {{- end}} + {{- if eq .ConsensusClient.String "prysm"}} + entrypoint: bash + {{- else}} + entrypoint: sh + {{- end}} + command: "/setup/start-vc.sh" + cap_drop: + - all + cap_add: + - dac_override + security_opt: + - no-new-privileges +networks: + net: diff --git a/shared/services/rocketpool/assets/install/templates/watchtower.tmpl b/shared/services/rocketpool/assets/install/templates/watchtower.tmpl new file mode 100644 index 000000000..01e7ee423 --- /dev/null +++ b/shared/services/rocketpool/assets/install/templates/watchtower.tmpl @@ -0,0 +1,26 @@ +# Autogenerated - DO NOT MODIFY THIS FILE DIRECTLY +# If you want to overwrite some of these values with your own customizations, +# please add them to `override/watchtower.yml`. +# +# See https://docs.docker.com/compose/extends/#adding-and-overriding-configuration +# for more information on overriding specific parameters of docker-compose files. + +services: + watchtower: + image: {{.Smartnode.GetSmartnodeContainerTag}} + container_name: {{.Smartnode.ProjectName}}_watchtower + restart: unless-stopped + volumes: + - {{.RocketPoolDirectory}}:/.rocketpool + - {{.Smartnode.DataPath}}:/.rocketpool/data + networks: + - net + command: "-m 0.0.0.0 -r {{or .WatchtowerMetricsPort.Value "9104"}} watchtower" + cap_drop: + - all + cap_add: + - dac_override + security_opt: + - no-new-privileges +networks: + net: diff --git a/shared/services/rocketpool/assets/rp-update-tracker/apt/apt-metrics.sh b/shared/services/rocketpool/assets/rp-update-tracker/apt/apt-metrics.sh new file mode 100644 index 000000000..6a6ac7f43 --- /dev/null +++ b/shared/services/rocketpool/assets/rp-update-tracker/apt/apt-metrics.sh @@ -0,0 +1,26 @@ +#!/bin/sh + +if [ -f /usr/lib/update-notifier/apt-check ]; then + # For Ubuntu systems + APT_CHECK=$(/usr/lib/update-notifier/apt-check 2>&1 || echo "0;0") + UPDATES=$(echo "$APT_CHECK" | cut -d ';' -f 1) + SECURITY=$(echo "$APT_CHECK" | cut -d ';' -f 2) +else + # For Debian systems + UPDATES=$(LANG=C apt-get dist-upgrade -s | grep -P '^\d+ upgraded'| cut -d" " -f1) + SECURITY=0 +fi + +REBOOT=$([ -f /var/run/reboot-required ] && echo 1 || echo 0) + +echo "# HELP os_upgrades_pending Apt package pending updates by origin." +echo "# TYPE os_upgrades_pending gauge" +echo "os_upgrades_pending ${UPDATES}" + +echo "# HELP os_security_upgrades_pending Apt package pending security updates by origin." +echo "# TYPE os_security_upgrades_pending gauge" +echo "os_security_upgrades_pending ${SECURITY}" + +echo "# HELP os_reboot_required Node reboot is required for software updates." +echo "# TYPE os_reboot_required gauge" +echo "os_reboot_required ${REBOOT}" diff --git a/shared/services/rocketpool/assets/rp-update-tracker/apt/apt-prometheus-metrics b/shared/services/rocketpool/assets/rp-update-tracker/apt/apt-prometheus-metrics new file mode 100644 index 000000000..c2a9d1388 --- /dev/null +++ b/shared/services/rocketpool/assets/rp-update-tracker/apt/apt-prometheus-metrics @@ -0,0 +1,9 @@ +APT::Update::Post-Invoke-Success { + "/usr/share/apt-metrics.sh | sponge /var/lib/node_exporter/textfile_collector/apt.prom || true"; + "/usr/share/rp-version-check.sh | sponge /var/lib/node_exporter/textfile_collector/rp.prom || true"; +}; + +DPkg::Post-Invoke { + "/usr/share/apt-metrics.sh | sponge /var/lib/node_exporter/textfile_collector/apt.prom || true"; + "/usr/share/rp-version-check.sh | sponge /var/lib/node_exporter/textfile_collector/rp.prom || true"; +}; \ No newline at end of file diff --git a/shared/services/rocketpool/assets/rp-update-tracker/dnf/dnf-metrics.sh b/shared/services/rocketpool/assets/rp-update-tracker/dnf/dnf-metrics.sh new file mode 100644 index 000000000..75c52eb76 --- /dev/null +++ b/shared/services/rocketpool/assets/rp-update-tracker/dnf/dnf-metrics.sh @@ -0,0 +1,45 @@ +#!/bin/sh + +# Expose DNF updates and restart info to Prometheus. +# +# Based on yum.sh by Slawomir Gonet +# and apt.sh by Ben Kochie +# (see https://github.com/prometheus-community/node-exporter-textfile-collector-scripts/blob/master/yum.sh) + +set -u -o pipefail + +# shellcheck disable=SC2016 +filter_awk_script=' +BEGIN { mute=1 } +/Obsoleting Packages/ { + mute=0 +} +mute && /^[[:print:]]+\.[[:print:]]+/ { + print $3 +} +' + +check_upgrades() { + /usr/bin/dnf -q check-update | + /usr/bin/xargs -n3 | + awk "${filter_awk_script}" | + sort | + uniq -c | + awk '{print "os_upgrades_pending{origin=\""$2"\"} "$1}' +} + +upgrades=$(check_upgrades) + +REBOOT=$(needs-restarting -r > /dev/null 2>&1 ; echo "$?") + +echo '# HELP os_upgrades_pending DNF package pending updates by origin.' +echo '# TYPE os_upgrades_pending gauge' +if [[ -n "${upgrades}" ]] ; then + echo "${upgrades}" +else + echo 'os_upgrades_pending{origin=""} 0' +fi + +echo '# HELP os_reboot_required Node reboot is required for software updates.' +echo '# TYPE os_reboot_required gauge' +echo "os_reboot_required ${REBOOT}" diff --git a/shared/services/rocketpool/assets/rp-update-tracker/dnf/rp-dnf-check.sh b/shared/services/rocketpool/assets/rp-update-tracker/dnf/rp-dnf-check.sh new file mode 100644 index 000000000..baed0fc93 --- /dev/null +++ b/shared/services/rocketpool/assets/rp-update-tracker/dnf/rp-dnf-check.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +/usr/share/dnf-metrics.sh | sponge /var/lib/node_exporter/textfile_collector/dnf.prom || true +/usr/share/rp-version-check.sh | sponge /var/lib/node_exporter/textfile_collector/rp.prom || true \ No newline at end of file diff --git a/shared/services/rocketpool/assets/rp-update-tracker/dnf/rp-update-tracker.service b/shared/services/rocketpool/assets/rp-update-tracker/dnf/rp-update-tracker.service new file mode 100644 index 000000000..402ecb24f --- /dev/null +++ b/shared/services/rocketpool/assets/rp-update-tracker/dnf/rp-update-tracker.service @@ -0,0 +1,10 @@ +[Unit] +Description=Checks for system and Rocket Pool updates periodically +Wants=rp-update-tracker.timer + +[Service] +Type=oneshot +ExecStart=/usr/share/rp-dnf-check.sh + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/shared/services/rocketpool/assets/rp-update-tracker/dnf/rp-update-tracker.timer b/shared/services/rocketpool/assets/rp-update-tracker/dnf/rp-update-tracker.timer new file mode 100644 index 000000000..170bd6efb --- /dev/null +++ b/shared/services/rocketpool/assets/rp-update-tracker/dnf/rp-update-tracker.timer @@ -0,0 +1,10 @@ +[Unit] +Description=Timer for the Rocket Pool updates tracker +Requires=rp-update-tracker.service + +[Timer] +Unit=rp-update-tracker.service +OnCalendar=*-*-* *:00:00 + +[Install] +WantedBy=timers.target \ No newline at end of file diff --git a/shared/services/rocketpool/assets/rp-update-tracker/rp-version-check.sh b/shared/services/rocketpool/assets/rp-update-tracker/rp-version-check.sh new file mode 100644 index 000000000..c09657694 --- /dev/null +++ b/shared/services/rocketpool/assets/rp-update-tracker/rp-version-check.sh @@ -0,0 +1,17 @@ +#!/bin/sh +echo "# HELP rocketpool_version_update New Rocket Pool version available" +echo "# TYPE rocketpool_version_update gauge" + +if [ "$(docker ps -aq -f status=running -f name=rocketpool_node)" ]; then + LATEST_VERSION=$(curl --silent "https://api.github.com/repos/rocket-pool/smartnode-install/releases/latest" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/') + CURRENT_VERSION=$(docker exec rocketpool_node /go/bin/rocketpool --version | sed -E 's/rocketpool version (.*)/v\1/') + + if [ "$LATEST_VERSION" = "$CURRENT_VERSION" ]; then + echo "rocketpool_version_update 0" + else + echo "rocketpool_version_update 1" + fi + +else + echo "rocketpool_version_update 0" +fi diff --git a/shared/services/rocketpool/assets/rp-update-tracker/yum/rp-update-tracker.service b/shared/services/rocketpool/assets/rp-update-tracker/yum/rp-update-tracker.service new file mode 100644 index 000000000..93fe59d2b --- /dev/null +++ b/shared/services/rocketpool/assets/rp-update-tracker/yum/rp-update-tracker.service @@ -0,0 +1,10 @@ +[Unit] +Description=Checks for system and Rocket Pool updates periodically +Wants=rp-update-tracker.timer + +[Service] +Type=oneshot +ExecStart=/usr/share/rp-yum-check.sh + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/shared/services/rocketpool/assets/rp-update-tracker/yum/rp-update-tracker.timer b/shared/services/rocketpool/assets/rp-update-tracker/yum/rp-update-tracker.timer new file mode 100644 index 000000000..170bd6efb --- /dev/null +++ b/shared/services/rocketpool/assets/rp-update-tracker/yum/rp-update-tracker.timer @@ -0,0 +1,10 @@ +[Unit] +Description=Timer for the Rocket Pool updates tracker +Requires=rp-update-tracker.service + +[Timer] +Unit=rp-update-tracker.service +OnCalendar=*-*-* *:00:00 + +[Install] +WantedBy=timers.target \ No newline at end of file diff --git a/shared/services/rocketpool/assets/rp-update-tracker/yum/rp-yum-check.sh b/shared/services/rocketpool/assets/rp-update-tracker/yum/rp-yum-check.sh new file mode 100644 index 000000000..edadeb068 --- /dev/null +++ b/shared/services/rocketpool/assets/rp-update-tracker/yum/rp-yum-check.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +/usr/share/yum-metrics.sh | sponge /var/lib/node_exporter/textfile_collector/yum.prom || true +/usr/share/rp-version-check.sh | sponge /var/lib/node_exporter/textfile_collector/rp.prom || true \ No newline at end of file diff --git a/shared/services/rocketpool/assets/rp-update-tracker/yum/yum-metrics.sh b/shared/services/rocketpool/assets/rp-update-tracker/yum/yum-metrics.sh new file mode 100644 index 000000000..36cdffb4d --- /dev/null +++ b/shared/services/rocketpool/assets/rp-update-tracker/yum/yum-metrics.sh @@ -0,0 +1,45 @@ +#!/bin/sh + +# Expose yum updates and restart info to Prometheus. +# +# Based on yum.sh by Slawomir Gonet +# and apt.sh by Ben Kochie +# (see https://github.com/prometheus-community/node-exporter-textfile-collector-scripts/blob/master/yum.sh) + +set -u -o pipefail + +# shellcheck disable=SC2016 +filter_awk_script=' +BEGIN { mute=1 } +/Obsoleting Packages/ { + mute=0 +} +mute && /^[[:print:]]+\.[[:print:]]+/ { + print $3 +} +' + +check_upgrades() { + /usr/bin/yum -q check-update | + /usr/bin/xargs -n3 | + awk "${filter_awk_script}" | + sort | + uniq -c | + awk '{print "os_upgrades_pending{origin=\""$2"\"} "$1}' +} + +upgrades=$(check_upgrades) + +REBOOT=$(needs-restarting -r > /dev/null 2>&1 ; echo "$?") + +echo '# HELP os_upgrades_pending Yum package pending updates by origin.' +echo '# TYPE os_upgrades_pending gauge' +if [[ -n "${upgrades}" ]] ; then + echo "${upgrades}" +else + echo 'os_upgrades_pending{origin=""} 0' +fi + +echo '# HELP os_reboot_required Node reboot is required for software updates.' +echo '# TYPE os_reboot_required gauge' +echo "os_reboot_required ${REBOOT}" diff --git a/shared/services/rocketpool/assets/scripts/install-update-tracker.sh b/shared/services/rocketpool/assets/scripts/install-update-tracker.sh new file mode 100755 index 000000000..ae1fb15da --- /dev/null +++ b/shared/services/rocketpool/assets/scripts/install-update-tracker.sh @@ -0,0 +1,230 @@ +#!/bin/sh + +# This script sets up the OS update and Rocket Pool update collector, along with +# integration with Prometheus's node-exporter and auto-running during apt or dnf +# executions. + + +# The path that the node exporter will be configured to look for textfiles in +TEXTFILE_COLLECTOR_PATH="/var/lib/node_exporter/textfile_collector" +UPDATE_SCRIPT_PATH="/usr/share" + + +COLOR_RED='\033[0;31m' +COLOR_YELLOW='\033[33m' +COLOR_RESET='\033[0m' + + +# Print a failure message to stderr and exit +fail() { + MESSAGE=$1 + >&2 echo -e "\n${COLOR_RED}**ERROR**\n$MESSAGE${COLOR_RESET}" + exit 1 +} + + +# Get the platform type +PLATFORM=$(uname -s) +if [ "$PLATFORM" = "Linux" ]; then + + # Check for /etc/os-release + if [ -f /etc/os-release ]; then + OS_ID=$(awk -F= '/^ID/{print $2}' /etc/os-release) + if [ $(echo $OS_ID | grep -c -E "ubuntu|debian|linuxmint") -gt "0" ]; then + INSTALLER="apt" + elif [ $(echo $OS_ID | grep -c -E "fedora|rhel|centos") -gt "0" ]; then + INSTALLER="dnf" + fi + + # Fall back to `lsb_release` + elif command -v lsb_release &>/dev/null ; then + OS_ID=$(lsb_release -si) + if [ $(echo $OS_ID | grep -c -E "ubuntu|debian|linuxmint") -gt "0" ]; then + INSTALLER="apt" + elif [ $(echo $OS_ID | grep -c -E "fedora|rhel|centos") -gt "0" ]; then + INSTALLER="dnf" + fi + + # Fall back to others + elif [ -f "/etc/centos-release" ]; then + INSTALLER="dnf" + elif [ -f "/etc/fedora-release" ]; then + INSTALLER="dnf" + fi + +fi + + +# Check for DNF or YUM +if [ "$INSTALLER" == "dnf" ]; then + if ! command -v dnf &>/dev/null ; then + if command -v yum &>/dev/null ; then + INSTALLER="yum" + else + fail "You're using a Fedora / CentOS / RHEL system ($OS_ID) but DNF or YUM don't seem to be installed."; + fi + fi +fi + + +# Check if SELinux is enabled +if [ $(selinuxenabled && echo $?) -ne 0 ]; then + SELINUX=false +else + SELINUX=true +fi + + +# Print progress +progress() { + STEP_NUMBER=$1 + MESSAGE=$2 + echo "Step $STEP_NUMBER of $TOTAL_STEPS: $MESSAGE" +} + + +# Install +install() { + + +# Parse arguments +while getopts "" FLAG; do + case "$FLAG" in + *) fail "Incorrect usage." ;; + esac +done + +# Get temporary data paths +PACKAGE_FILES_PATH="$(dirname $0)/rp-update-tracker" + + +case "$INSTALLER" in + + # Distros using apt + apt) + + # The total number of steps in the installation process + TOTAL_STEPS="2" + + # Install dependencies + progress 1 "Installing dependencies..." + { sudo apt -y update; } >&2 + # Debian doesn't have update-notifier-common + { sudo apt -y install update-notifier-common || true; } >&2 + { sudo apt -y install moreutils || fail "Could not install OS dependencies."; } >&2 + + # Install the update tracker files + progress 2 "Installing update tracker..." + { sudo mkdir -p "$TEXTFILE_COLLECTOR_PATH" || fail "Could not create textfile collector path."; } >&2 + { sudo mv "$PACKAGE_FILES_PATH/apt/apt-metrics.sh" "$UPDATE_SCRIPT_PATH" || fail "Could not move apt update collector."; } >&2 + { sudo mv "$PACKAGE_FILES_PATH/rp-version-check.sh" "$UPDATE_SCRIPT_PATH" || fail "Could not move Rocket Pool update collector."; } >&2 + { sudo mv "$PACKAGE_FILES_PATH/apt/apt-prometheus-metrics" "/etc/apt/apt.conf.d/60prometheus-metrics" || fail "Could not move apt trigger."; } >&2 + { sudo chmod +x "$UPDATE_SCRIPT_PATH/apt-metrics.sh" || fail "Could not set permissions on apt update collector."; } >&2 + { sudo chmod +x "$UPDATE_SCRIPT_PATH/rp-version-check.sh" || fail "Could not set permissions on Rocket Pool update collector."; } >&2 + + ;; + + # Distros using dnf + dnf) + + # The total number of steps in the installation process + TOTAL_STEPS="3" + + # Install dependencies + progress 1 "Installing dependencies..." + { sudo dnf -y check-update; } >&2 + { sudo dnf -y install dnf-utils || fail "Could not install OS dependencies."; } >&2 + # PowerTools and epel-release are needed for CentOS 8 to install moreutils, but it will fail for e.g. Fedora + { sudo dnf config-manager --set-enabled powertools || true; } >&2 + { sudo dnf -y install epel-release || true; } >&2 + { sudo dnf -y install moreutils || fail "Could not install moreutils."; } >&2 + + # Install the update tracker files + progress 2 "Installing update tracker..." + { sudo mkdir -p "$TEXTFILE_COLLECTOR_PATH" || fail "Could not create textfile collector path."; } >&2 + { sudo mv "$PACKAGE_FILES_PATH/dnf/dnf-metrics.sh" "$UPDATE_SCRIPT_PATH" || fail "Could not move dnf update collector."; } >&2 + { sudo mv "$PACKAGE_FILES_PATH/rp-version-check.sh" "$UPDATE_SCRIPT_PATH" || fail "Could not move Rocket Pool update collector."; } >&2 + { sudo mv "$PACKAGE_FILES_PATH/dnf/rp-dnf-check.sh" "$UPDATE_SCRIPT_PATH" || fail "Could not move update tracker script."; } >&2 + { sudo mv "$PACKAGE_FILES_PATH/dnf/rp-update-tracker.service" "/etc/systemd/system" || fail "Could not move update tracker service."; } >&2 + { sudo mv "$PACKAGE_FILES_PATH/dnf/rp-update-tracker.timer" "/etc/systemd/system" || fail "Could not move update tracker timer."; } >&2 + { sudo chmod +x "$UPDATE_SCRIPT_PATH/dnf-metrics.sh" || fail "Could not set permissions on dnf update collector."; } >&2 + { sudo chmod +x "$UPDATE_SCRIPT_PATH/rp-version-check.sh" || fail "Could not set permissions on Rocket Pool update collector."; } >&2 + { sudo chmod +x "$UPDATE_SCRIPT_PATH/rp-dnf-check.sh" || fail "Could not set permissions on Rocket Pool update tracker script."; } >&2 + + # Install the update checking service + progress 3 "Installing update tracker service..." + if [ "$SELINUX" = true ]; then + echo -e "${COLOR_YELLOW}Your system has SELinux enabled, so Rocket Pool can't automatically start the update tracker service." + echo "Please run the following commands manually:" + echo "" + echo -e '\tsudo restorecon /usr/share/rp-dnf-check.sh /usr/share/rp-version-check.sh /etc/systemd/system/rp-update-tracker.service /etc/systemd/system/rp-update-tracker.timer' + echo -e '\tsudo systemctl enable rp-update-tracker' + echo -e '\tsudo systemctl start rp-update-tracker' + echo -e "${COLOR_RESET}" + else + { sudo systemctl daemon-reload || fail "Couldn't update systemctl daemons."; } >&2 + { sudo systemctl enable rp-update-tracker || fail "Couldn't enable update tracker service."; } >&2 + { sudo systemctl start rp-update-tracker || fail "Couldn't start update tracker service."; } >&2 + fi + + ;; + + + # Legacy CentOS / Fedora / RHEL with Yum + yum) + + # The total number of steps in the installation process + TOTAL_STEPS="3" + + # Install dependencies + progress 1 "Installing dependencies..." + { sudo yum -y check-update; } >&2echo + { sudo yum -y install yum-utils || fail "Could not install OS dependencies."; } >&2 + # CentOS 7 requires epel-release, but ignore it if it doesn't exist for others + { sudo yum -y install epel-release || true; } >&2 + { sudo yum -y install moreutils || fail "Could not install moreutils."; } >&2 + + # Install the update tracker files + progress 2 "Installing update tracker..." + { sudo mkdir -p "$TEXTFILE_COLLECTOR_PATH" || fail "Could not create textfile collector path."; } >&2 + { sudo mv "$PACKAGE_FILES_PATH/yum/yum-metrics.sh" "$UPDATE_SCRIPT_PATH" || fail "Could not move yum update collector."; } >&2 + { sudo mv "$PACKAGE_FILES_PATH/rp-version-check.sh" "$UPDATE_SCRIPT_PATH" || fail "Could not move Rocket Pool update collector."; } >&2 + { sudo mv "$PACKAGE_FILES_PATH/yum/rp-yum-check.sh" "$UPDATE_SCRIPT_PATH" || fail "Could not move update tracker script."; } >&2 + { sudo mv "$PACKAGE_FILES_PATH/yum/rp-update-tracker.service" "/etc/systemd/system" || fail "Could not move update tracker service."; } >&2 + { sudo mv "$PACKAGE_FILES_PATH/yum/rp-update-tracker.timer" "/etc/systemd/system" || fail "Could not move update tracker timer."; } >&2 + { sudo chmod +x "$UPDATE_SCRIPT_PATH/yum-metrics.sh" || fail "Could not set permissions on dnf update collector."; } >&2 + { sudo chmod +x "$UPDATE_SCRIPT_PATH/rp-version-check.sh" || fail "Could not set permissions on Rocket Pool update collector."; } >&2 + { sudo chmod +x "$UPDATE_SCRIPT_PATH/rp-yum-check.sh" || fail "Could not set permissions on Rocket Pool update tracker script."; } >&2 + + # Install the update checking service + progress 3 "Installing update tracker service..." + if [ "$SELINUX" = true ]; then + echo -e "${COLOR_YELLOW}Your system has SELinux enabled, so Rocket Pool can't automatically start the update tracker service." + echo "Please run the following commands manually:" + echo "" + echo -e '\tsudo restorecon /usr/share/rp-yum-check.sh /usr/share/rp-version-check.sh /etc/systemd/system/rp-update-tracker.service /etc/systemd/system/rp-update-tracker.timer' + echo -e '\tsudo systemctl enable rp-update-tracker' + echo -e '\tsudo systemctl start rp-update-tracker' + echo -e "${COLOR_RESET}" + else + { sudo systemctl daemon-reload || fail "Couldn't update systemctl daemons."; } >&2 + { sudo systemctl enable rp-update-tracker || fail "Couldn't enable update tracker service."; } >&2 + { sudo systemctl start rp-update-tracker || fail "Couldn't start update tracker service."; } >&2 + fi + + ;; + + # Unsupported package manager + *) + RED='\033[0;31m' + echo "" + echo -e "${RED}**ERROR**" + echo "Update tracker installation is only supported for system that use the 'apt' or 'dnf' package managers." + echo "If your operating system uses one of these and you received this message in error, please notify the Rocket Pool team." + exit 1 + ;; + +esac +} + +install "$@" diff --git a/shared/services/rocketpool/assets/scripts/install.sh b/shared/services/rocketpool/assets/scripts/install.sh new file mode 100755 index 000000000..2b95c9a78 --- /dev/null +++ b/shared/services/rocketpool/assets/scripts/install.sh @@ -0,0 +1,428 @@ +#!/bin/sh + +## +# Rocket Pool service installation script +# Prints progress messages to stdout +# All command output is redirected to stderr +## + +COLOR_RED='\033[0;31m' +COLOR_YELLOW='\033[33m' +COLOR_RESET='\033[0m' + +# Print a failure message to stderr and exit +fail() { + MESSAGE=$1 + >&2 echo -e "\n${COLOR_RED}**ERROR**\n$MESSAGE${COLOR_RESET}" + exit 1 +} + + +# Get CPU architecture +UNAME_VAL=$(uname -m) +ARCH="" +case $UNAME_VAL in + x86_64) ARCH="amd64" ;; + aarch64) ARCH="arm64" ;; + arm64) ARCH="arm64" ;; + *) fail "CPU architecture not supported: $UNAME_VAL" ;; +esac + + +# Get the platform type +PLATFORM=$(uname -s) +if [ "$PLATFORM" = "Linux" ]; then + if command -v lsb_release &>/dev/null ; then + PLATFORM=$(lsb_release -si) + elif [ -f "/etc/centos-release" ]; then + PLATFORM="CentOS" + elif [ -f "/etc/fedora-release" ]; then + PLATFORM="Fedora" + fi +fi + +## +# Config +## + + +# The total number of steps in the installation process +TOTAL_STEPS="8" +# The Rocket Pool user data path +RP_PATH="$HOME/.rocketpool" +# The default network to run Rocket Pool on +NETWORK="mainnet" + +## +# Utils +## + + +# Print progress +progress() { + STEP_NUMBER=$1 + MESSAGE=$2 + echo "Step $STEP_NUMBER of $TOTAL_STEPS: $MESSAGE" +} + + +# Docker installation steps +add_user_docker() { + $SUDO_CMD usermod -aG docker $USER || fail "Could not add user to docker group." +} + + +# Detect installed privilege escalation programs +get_escalation_cmd() { + if type sudo > /dev/null 2>&1; then + SUDO_CMD="sudo" + elif type doas > /dev/null 2>&1; then + echo "NOTE: sudo not found, using doas instead" + SUDO_CMD="doas" + else + fail "Please make sure a privilege escalation command such as \"sudo\" is installed and available before installing Rocket Pool." + fi +} + +# Install +install() { + + +## +# Initialization +## + + +# Parse arguments +while getopts "dp:u:n:" FLAG; do + case "$FLAG" in + d) NO_DEPS=true ;; + p) RP_PATH="$OPTARG" ;; + u) DATA_PATH="$OPTARG" ;; + n) NETWORK="$OPTARG" ;; + *) fail "Incorrect usage." ;; + esac +done + +if [ -z "$DATA_PATH" ]; then + DATA_PATH="$RP_PATH/data" +fi + +# Get temporary data paths +PACKAGE_FILES_PATH="$(dirname $0)/install" + +## +# Installation +## + + +# OS dependencies +if [ -z "$NO_DEPS" ]; then + +>&2 get_escalation_cmd + +case "$PLATFORM" in + + # Ubuntu / Debian / Raspbian + Ubuntu|Debian|Raspbian) + + # Get platform name + PLATFORM_NAME=$(echo "$PLATFORM" | tr '[:upper:]' '[:lower:]') + + # Install OS dependencies + progress 1 "Installing OS dependencies..." + { $SUDO_CMD apt-get -y update || fail "Could not update OS package definitions."; } >&2 + { $SUDO_CMD apt-get -y install apt-transport-https ca-certificates curl gnupg gnupg-agent lsb-release software-properties-common chrony || fail "Could not install OS packages."; } >&2 + + # Check for existing Docker installation + progress 2 "Checking if Docker is installed..." + dpkg-query -W -f='${Status}' docker-ce 2>&1 | grep -q -P '^install ok installed$' > /dev/null + if [ $? != "0" ]; then + echo "Installing Docker..." + if [ ! -f /etc/apt/sources.list.d/docker.list ]; then + # Install the Docker repo + { $SUDO_CMD mkdir -p /etc/apt/keyrings || fail "Could not create APT keyrings directory."; } >&2 + { curl -fsSL "https://download.docker.com/linux/$PLATFORM_NAME/gpg" | $SUDO_CMD gpg --dearmor -o /etc/apt/keyrings/docker.gpg || fail "Could not add docker repository key."; } >&2 + { echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/$PLATFORM_NAME $(lsb_release -cs) stable" | $SUDO_CMD tee /etc/apt/sources.list.d/docker.list > /dev/null || fail "Could not add docker repository."; } >&2 + fi + { $SUDO_CMD apt-get -y update || fail "Could not update OS package definitions."; } >&2 + { $SUDO_CMD apt-get -y install docker-ce docker-ce-cli docker-compose-plugin containerd.io || fail "Could not install Docker packages."; } >&2 + fi + + # Check for existing docker-compose-plugin installation + progress 2 "Checking if docker-compose-plugin is installed..." + dpkg-query -W -f='${Status}' docker-compose-plugin 2>&1 | grep -q -P '^install ok installed$' > /dev/null + if [ $? != "0" ]; then + echo "Installing docker-compose-plugin..." + if [ ! -f /etc/apt/sources.list.d/docker.list ]; then + # Install the Docker repo, removing the legacy one if it exists + { $SUDO_CMD add-apt-repository --remove "deb [arch=$(dpkg --print-architecture)] https://download.docker.com/linux/$PLATFORM_NAME $(lsb_release -cs) stable"; } 2>/dev/null + { $SUDO_CMD mkdir -p /etc/apt/keyrings || fail "Could not create APT keyrings directory."; } >&2 + { curl -fsSL "https://download.docker.com/linux/$PLATFORM_NAME/gpg" | $SUDO_CMD gpg --dearmor -o /etc/apt/keyrings/docker.gpg || fail "Could not add docker repository key."; } >&2 + { echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/$PLATFORM_NAME $(lsb_release -cs) stable" | $SUDO_CMD tee /etc/apt/sources.list.d/docker.list > /dev/null || fail "Could not add Docker repository."; } >&2 + fi + { $SUDO_CMD apt-get -y update || fail "Could not update OS package definitions."; } >&2 + { $SUDO_CMD apt-get -y install docker-compose-plugin || fail "Could not install docker-compose-plugin."; } >&2 + { $SUDO_CMD systemctl restart docker || fail "Could not restart docker daemon."; } >&2 + else + echo "Already installed." + fi + + # Add user to docker group + progress 3 "Adding user to docker group..." + >&2 add_user_docker + + ;; + + # Centos + CentOS) + + # Install OS dependencies + progress 1 "Installing OS dependencies..." + { $SUDO_CMD yum install -y yum-utils chrony || fail "Could not install OS packages."; } >&2 + { $SUDO_CMD systemctl start chronyd || fail "Could not start chrony daemon."; } >&2 + + # Install docker + progress 2 "Installing docker..." + { $SUDO_CMD yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo || fail "Could not add docker repository."; } >&2 + { $SUDO_CMD yum install -y docker-ce docker-ce-cli docker-compose-plugin containerd.io || fail "Could not install docker packages."; } >&2 + { $SUDO_CMD systemctl start docker || fail "Could not start docker daemon."; } >&2 + { $SUDO_CMD systemctl enable docker || fail "Could not set docker daemon to auto-start on boot."; } >&2 + + # Check for existing docker-compose-plugin installation + progress 2 "Checking if docker-compose-plugin is installed..." + yum -q list installed docker-compose-plugin 2>/dev/null 1>/dev/null + if [ $? != "0" ]; then + echo "Installing docker-compose-plugin..." + { $SUDO_CMD yum install -y docker-compose-plugin || fail "Could not install docker-compose-plugin."; } >&2 + { $SUDO_CMD systemctl restart docker || fail "Could not restart docker daemon."; } >&2 + else + echo "Already installed." + fi + + # Add user to docker group + progress 3 "Adding user to docker group..." + >&2 add_user_docker + + ;; + + # Fedora + Fedora) + + # Install OS dependencies + progress 1 "Installing OS dependencies..." + { $SUDO_CMD dnf -y install dnf-plugins-core chrony || fail "Could not install OS packages."; } >&2 + { $SUDO_CMD systemctl start chronyd || fail "Could not start chrony daemon."; } >&2 + + # Install docker + progress 2 "Installing docker..." + { $SUDO_CMD dnf config-manager --add-repo https://download.docker.com/linux/fedora/docker-ce.repo || fail "Could not add docker repository."; } >&2 + { $SUDO_CMD dnf -y install docker-ce docker-ce-cli docker-compose-plugin containerd.io || fail "Could not install docker packages."; } >&2 + { $SUDO_CMD systemctl start docker || fail "Could not start docker daemon."; } >&2 + { $SUDO_CMD systemctl enable docker || fail "Could not set docker daemon to auto-start on boot."; } >&2 + + # Check for existing docker-compose-plugin installation + progress 2 "Checking if docker-compose-plugin is installed..." + dnf -q list installed docker-compose-plugin 2>/dev/null 1>/dev/null + if [ $? != "0" ]; then + echo "Installing docker-compose-plugin..." + { $SUDO_CMD dnf install -y docker-compose-plugin || fail "Could not install docker-compose-plugin."; } >&2 + { $SUDO_CMD systemctl restart docker || fail "Could not restart docker daemon."; } >&2 + else + echo "Already installed." + fi + + # Add user to docker group + progress 3 "Adding user to docker group..." + >&2 add_user_docker + + ;; + + # Unsupported OS + *) + RED='\033[0;31m' + echo "" + echo -e "${RED}**ERROR**" + echo "Automatic dependency installation for the $PLATFORM operating system is not supported." + echo "Please install docker and docker-compose-plugin manually, then try again with the '-d' flag to skip OS dependency installation." + echo "Be sure to add yourself to the docker group with '$SUDO_CMD usermod -aG docker $USER' after installing docker." + echo "Log out and back in, or restart your system after you run this command." + echo -e "${RESET}" + exit 1 + ;; + +esac +else + echo "Skipping steps 1 - 2 (OS dependencies & docker)" + case "$PLATFORM" in + # Ubuntu / Debian / Raspbian + Ubuntu|Debian|Raspbian) + + # Get platform name + PLATFORM_NAME=$(echo "$PLATFORM" | tr '[:upper:]' '[:lower:]') + + # Check for existing docker-compose-plugin installation + progress 3 "Checking if docker-compose-plugin is installed..." + dpkg-query -W -f='${Status}' docker-compose-plugin 2>&1 | grep -q -P '^install ok installed$' > /dev/null + if [ $? != "0" ]; then + >&2 get_escalation_cmd + echo "Installing docker-compose-plugin..." + if [ ! -f /etc/apt/sources.list.d/docker.list ]; then + # Install the Docker repo, removing the legacy one if it exists + { $SUDO_CMD add-apt-repository --remove "deb [arch=$(dpkg --print-architecture)] https://download.docker.com/linux/$PLATFORM_NAME $(lsb_release -cs) stable"; } 2>/dev/null + { $SUDO_CMD mkdir -p /etc/apt/keyrings || fail "Could not create APT keyrings directory."; } >&2 + { curl -fsSL "https://download.docker.com/linux/$PLATFORM_NAME/gpg" | $SUDO_CMD gpg --dearmor -o /etc/apt/keyrings/docker.gpg || fail "Could not add docker repository key."; } >&2 + { echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/$PLATFORM_NAME $(lsb_release -cs) stable" | $SUDO_CMD tee /etc/apt/sources.list.d/docker.list > /dev/null || fail "Could not add Docker repository."; } >&2 + fi + { $SUDO_CMD apt-get -y update || fail "Could not update OS package definitions."; } >&2 + { $SUDO_CMD apt-get -y install docker-compose-plugin || fail "Could not install docker-compose-plugin."; } >&2 + { $SUDO_CMD systemctl restart docker || fail "Could not restart docker daemon."; } >&2 + else + echo "Already installed." + fi + + ;; + + # Centos + CentOS) + + # Check for existing docker-compose-plugin installation + progress 3 "Checking if docker-compose-plugin is installed..." + yum -q list installed docker-compose-plugin 2>/dev/null 1>/dev/null + if [ $? != "0" ]; then + >&2 get_escalation_cmd + echo "Installing docker-compose-plugin..." + { $SUDO_CMD yum install -y docker-compose-plugin || fail "Could not install docker-compose-plugin."; } >&2 + { $SUDO_CMD systemctl restart docker || fail "Could not restart docker daemon."; } >&2 + else + echo "Already installed." + fi + + ;; + + # Fedora + Fedora) + + # Check for existing docker-compose-plugin installation + progress 3 "Checking if docker-compose-plugin is installed..." + dnf -q list installed docker-compose-plugin 2>/dev/null 1>/dev/null + if [ $? != "0" ]; then + >&2 get_escalation_cmd + echo "Installing docker-compose-plugin..." + { $SUDO_CMD dnf install -y docker-compose-plugin || fail "Could not install docker-compose-plugin."; } >&2 + { $SUDO_CMD systemctl restart docker || fail "Could not restart docker daemon."; } >&2 + else + echo "Already installed." + fi + + ;; + + # Everything else + *) + # Check for existing docker-compose-plugin installation + progress 3 "Checking if docker-compose-plugin is installed..." + if docker compose 2>/dev/null 1>/dev/null ; then + echo "Already installed." + else + RED='\033[0;31m' + echo "" + echo -e "${RED}**ERROR**" + echo "The docker-compose-plugin package is not installed. Starting with v1.7.0, the Smartnode requires this package because the legacy docker-compose script is no longer supported." + echo "Since automatic dependency installation for the $PLATFORM operating system is not supported, you will need to install it manually." + echo "Please install docker-compose-plugin manually, then try running `rocketpool service install -d` again to finish updating." + echo -e "${RESET}" + exit 1 + fi + + ;; + + esac +fi + + +# Check for existing installation +progress 5 "Checking for existing installation..." +if [ -d $RP_PATH ]; then + # Check for legacy files - key on the old config.yml + if [ -f "$RP_PATH/config.yml" ]; then + progress 5 "Old installation detected, backing it up and migrating to new config system..." + OLD_CONFIG_BACKUP_PATH="$RP_PATH/old_config_backup" + { mkdir -p $OLD_CONFIG_BACKUP_PATH || fail "Could not create old config backup folder."; } >&2 + + if [ -f "$RP_PATH/config.yml" ]; then + { mv "$RP_PATH/config.yml" "$OLD_CONFIG_BACKUP_PATH" || fail "Could not move config.yml to backup folder."; } >&2 + fi + if [ -f "$RP_PATH/settings.yml" ]; then + { mv "$RP_PATH/settings.yml" "$OLD_CONFIG_BACKUP_PATH" || fail "Could not move settings.yml to backup folder."; } >&2 + fi + if [ -f "$RP_PATH/docker-compose.yml" ]; then + { mv "$RP_PATH/docker-compose.yml" "$OLD_CONFIG_BACKUP_PATH" || fail "Could not move docker-compose.yml to backup folder."; } >&2 + fi + if [ -f "$RP_PATH/docker-compose-metrics.yml" ]; then + { mv "$RP_PATH/docker-compose-metrics.yml" "$OLD_CONFIG_BACKUP_PATH" || fail "Could not move docker-compose-metrics.yml to backup folder."; } >&2 + fi + if [ -f "$RP_PATH/docker-compose-fallback.yml" ]; then + { mv "$RP_PATH/docker-compose-fallback.yml" "$OLD_CONFIG_BACKUP_PATH" || fail "Could not move docker-compose-fallback.yml to backup folder."; } >&2 + fi + if [ -f "$RP_PATH/prometheus.tmpl" ]; then + { mv "$RP_PATH/prometheus.tmpl" "$OLD_CONFIG_BACKUP_PATH" || fail "Could not move prometheus.tmpl to backup folder."; } >&2 + fi + if [ -f "$RP_PATH/grafana-prometheus-datasource.yml" ]; then + { mv "$RP_PATH/grafana-prometheus-datasource.yml" "$OLD_CONFIG_BACKUP_PATH" || fail "Could not move grafana-prometheus-datasource.yml to backup folder."; } >&2 + fi + if [ -f "$RP_PATH/grafana-dashboards.yml" ]; then + { mv "$RP_PATH/grafana-dashboards.yml" "$OLD_CONFIG_BACKUP_PATH" || fail "Could not move grafana-dashboards.yml to backup folder."; } >&2 + fi + if [ -d "$RP_PATH/chains" ]; then + { mv "$RP_PATH/chains" "$OLD_CONFIG_BACKUP_PATH" || fail "Could not move chains directory to backup folder."; } >&2 + fi + fi + + # Back up existing config file + if [ -f "$RP_PATH/user-settings.yml" ]; then + progress 5 "Backing up configuration settings to user-settings-backup.yml..." + { cp "$RP_PATH/user-settings.yml" "$RP_PATH/user-settings-backup.yml" || fail "Could not backup configuration settings."; } >&2 + fi +fi + + +# Create ~/.rocketpool dir & files +progress 6 "Creating Rocket Pool user data directory..." +{ mkdir -p "$DATA_PATH/validators" || fail "Could not create the Rocket Pool user data directory."; } >&2 +{ mkdir -p "$RP_PATH/runtime" || fail "Could not create the Rocket Pool runtime directory."; } >&2 +{ mkdir -p "$DATA_PATH/secrets" || fail "Could not create the Rocket Pool secrets directory."; } >&2 +{ mkdir -p "$DATA_PATH/rewards-trees" || fail "Could not create the Rocket Pool rewards trees directory."; } >&2 +{ mkdir -p "$RP_PATH/extra-scrape-jobs" || fail "Could not create the Prometheus extra scrape jobs directory."; } >&2 +{ mkdir -p "$RP_PATH/alerting/rules" || fail "Could not create the alerting rules directory."; } >&2 +{ mkdir -p "$RP_PATH/dashboards" || fail "Could not create the grafana dashboards directory."; } >&2 + + +# Copy package files +progress 7 "Copying package files to Rocket Pool user data directory..." +{ cp -r "$PACKAGE_FILES_PATH/addons" "$RP_PATH" || fail "Could not copy addons folder to the Rocket Pool user data directory."; } >&2 +{ cp -r -n "$PACKAGE_FILES_PATH/override" "$RP_PATH" || rsync -r --ignore-existing "$PACKAGE_FILES_PATH/override" "$RP_PATH" || fail "Could not copy new override files to the Rocket Pool user data directory."; } >&2 +{ cp -r "$PACKAGE_FILES_PATH/scripts" "$RP_PATH" || fail "Could not copy scripts folder to the Rocket Pool user data directory."; } >&2 +{ cp -r "$PACKAGE_FILES_PATH/templates" "$RP_PATH" || fail "Could not copy templates folder to the Rocket Pool user data directory."; } >&2 +{ cp -r "$PACKAGE_FILES_PATH/alerting" "$RP_PATH" || fail "Could not copy alerting folder to the Rocket Pool user data directory."; } >&2 +{ cp \ + "$PACKAGE_FILES_PATH/grafana-prometheus-datasource.yml" \ + "$PACKAGE_FILES_PATH/prometheus.tmpl" \ + "$PACKAGE_FILES_PATH/grafana-dashboards.yml" "$RP_PATH" || fail "Could not copy base files to the Rocket Pool user data directory."; } >&2 +{ cp -r "$PACKAGE_FILES_PATH/dashboards" "$RP_PATH" || fail "Could not copy grafana dashboards folder to the Rocket Pool user data directory."; } >&2 +{ find "$RP_PATH/scripts" -name "*.sh" -exec chmod +x {} \; 2>/dev/null || fail "Could not set executable permissions on package files."; } >&2 +{ touch -a "$RP_PATH/.firstrun" || fail "Could not create the first-run flag file."; } >&2 + +# Clean up unnecessary files from old installations +progress 8 "Cleaning up obsolete files from previous installs..." +{ rm -rf "$DATA_PATH/fr-default" || echo "NOTE: Could not remove '$DATA_PATH/fr-default' which is no longer needed."; } >&2 +GRAFFITI_OWNER=$(stat -c "%U" $RP_PATH/addons/gww/graffiti.txt) +if [ "$GRAFFITI_OWNER" = "$USER" ]; then + { rm -f "$RP_PATH/addons/gww/graffiti.txt" || echo -e "${COLOR_YELLOW}WARNING: Could not remove '$RP_PATH/addons/gww/graffiti.txt' which was used by the Graffiti Wall Writer addon. You will need to remove this file manually if you intend to use the Graffiti Wall Writer.${COLOR_RESET}"; } >&2 +fi +} +# Remove deprecated version tags +find $RP_PATH/override/ -name "*.yml" -exec sed -i '/^version: "3\.7"$/d' {} \; +find $RP_PATH/templates/ -name "*.tmpl" -exec sed -i '/^version: "3\.7"$/d' {} \; + +install "$@" + diff --git a/shared/services/rocketpool/client.go b/shared/services/rocketpool/client.go index c03525922..d93d6232e 100644 --- a/shared/services/rocketpool/client.go +++ b/shared/services/rocketpool/client.go @@ -2,17 +2,15 @@ package rocketpool import ( "bufio" - "bytes" "errors" "fmt" "io" + "io/fs" "math" "math/big" - "net/http" "os" "os/exec" "path/filepath" - "strconv" "strings" "time" @@ -26,6 +24,7 @@ import ( "github.com/mitchellh/go-homedir" "github.com/rocket-pool/smartnode/addons/graffiti_wall_writer" "github.com/rocket-pool/smartnode/shared/services/config" + "github.com/rocket-pool/smartnode/shared/services/rocketpool/assets" "github.com/rocket-pool/smartnode/shared/services/rocketpool/template" "github.com/rocket-pool/smartnode/shared/types/api" cfgtypes "github.com/rocket-pool/smartnode/shared/types/config" @@ -34,9 +33,6 @@ import ( // Config const ( - InstallerURL string = "https://github.com/rocket-pool/smartnode-install/releases/download/%s/install.sh" - UpdateTrackerURL string = "https://github.com/rocket-pool/smartnode-install/releases/download/%s/install-update-tracker.sh" - SettingsFile string = "user-settings.yml" BackupSettingsFile string = "user-settings-backup.yml" PrometheusConfigTemplate string = "prometheus.tmpl" @@ -54,7 +50,8 @@ const ( templateSuffix string = ".tmpl" composeFileSuffix string = ".yml" - nethermindAdminUrl string = "http://127.0.0.1:7434" + nethermindAdminUrl string = "http://127.0.0.1:7434" + pruneStarterContainerSuffix string = "_nm_prune_starter" DebugColor = color.FgYellow ) @@ -292,45 +289,85 @@ func (c *Client) UpdatePrometheusConfiguration(config *config.RocketPoolConfig) return t.Write(config) } -// Install the Rocket Pool service -func (c *Client) InstallService(verbose, noDeps bool, version, path string, dataPath string) error { - - // Get installation script flags - flags := []string{ - "-v", shellescape.Quote(version), - } - if path != "" { - flags = append(flags, fmt.Sprintf("-p %s", shellescape.Quote(path))) - } - if noDeps { - flags = append(flags, "-d") +func (c *Client) runScript(script assets.ScriptWithContext, verbose bool, flags []string) error { + // Make a tmpdir + tmpdir, err := os.MkdirTemp("", "rocketpool-") + if err != nil { + return fmt.Errorf("error creating tmpdir: %w", err) } - if dataPath != "" { - flags = append(flags, fmt.Sprintf("-u %s", dataPath)) + if verbose { + fmt.Printf("Verbose mode enabled, tmpdir %s will not be removed\n", tmpdir) + } else { + defer os.RemoveAll(tmpdir) } - // Download the installation script - resp, err := http.Get(fmt.Sprintf(InstallerURL, version)) + // Create a file in the tmpdir + scriptPathName := filepath.Join(tmpdir, "script.sh") + scriptFile, err := os.Create(scriptPathName) if err != nil { - return err + return fmt.Errorf("error creating script file: %w", err) } - defer resp.Body.Close() - if resp.StatusCode != http.StatusOK { - return fmt.Errorf("unexpected http status downloading installation script: %d", resp.StatusCode) + if err := scriptFile.Chmod(0700); err != nil { + return fmt.Errorf("error setting script file permissions: %w", err) } - - // Sanity check that the script octet length matches content-length - script, err := io.ReadAll(resp.Body) + // write the script to the file + _, err = scriptFile.Write(script.Script) if err != nil { - return err + return fmt.Errorf("error writing script to file: %w", err) } + scriptFile.Close() - if fmt.Sprint(len(script)) != resp.Header.Get("content-length") { - return fmt.Errorf("downloaded script length %d did not match content-length header %s", len(script), resp.Header.Get("content-length")) + // Copy the context to the tmpdir + // If we upgrade to go 1.23+ we can probably use os.CopyFS() instead + err = fs.WalkDir(script.Context, ".", func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + dstpath := filepath.Join(tmpdir, path) + if d.IsDir() { + // If d is a directory, create it. + if verbose { + fmt.Printf("Creating directory: %s\n", path) + } + err = os.MkdirAll(dstpath, 0755) + if err != nil { + return err + } + return nil + } + + // d is a file, copy it. + if verbose { + fmt.Printf("Copying file: %s\n", path) + } + scriptFile, err := os.Create(dstpath) + if err != nil { + return err + } + content, err := fs.ReadFile(script.Context, path) + if err != nil { + return err + } + _, err = scriptFile.Write(content) + if err != nil { + return err + } + err = scriptFile.Close() + if err != nil { + return err + } + return nil + }) + if err != nil { + return fmt.Errorf("error copying script files: %w", err) } - // Initialize installation command - cmd, err := c.newCommand(fmt.Sprintf("sh -s -- %s", strings.Join(flags, " "))) + // Initialize command + cmdString := fmt.Sprintf("%s %s", scriptPathName, strings.Join(flags, " ")) + if verbose { + fmt.Printf("Running script: %s\n", cmdString) + } + cmd, err := c.newCommand(cmdString) if err != nil { return err } @@ -338,9 +375,6 @@ func (c *Client) InstallService(verbose, noDeps bool, version, path string, data _ = cmd.Close() }() - // Pass the script to sh via its stdin fd - cmd.SetStdin(bytes.NewReader(script)) - // Get command output pipes cmdOut, err := cmd.StdoutPipe() if err != nil { @@ -375,108 +409,40 @@ func (c *Client) InstallService(verbose, noDeps bool, version, path string, data // Run command and return error output err = cmd.Run() if err != nil { - return fmt.Errorf("Could not install Rocket Pool service: %s", errMessage) + return fmt.Errorf("Could not run script: %s", errMessage) } return nil } -// Install the update tracker -func (c *Client) InstallUpdateTracker(verbose bool, version string) error { +// Install the Rocket Pool service +func (c *Client) InstallService(verbose, noDeps bool, path string, dataPath string) error { // Get installation script flags - flags := []string{ - "-v", fmt.Sprintf("%s", shellescape.Quote(version)), - } - - // Download the installer package - url := fmt.Sprintf(UpdateTrackerURL, version) - resp, err := http.Get(url) - if err != nil { - return fmt.Errorf("error downloading installer package: %w", err) - } - defer resp.Body.Close() - if resp.StatusCode != http.StatusOK { - return fmt.Errorf("downloading installer package failed with code %d - [%s]", resp.StatusCode, resp.Status) - } - - // Create a temp file for it - path := filepath.Join(os.TempDir(), "install-update-tracker.sh") - tempFile, err := os.OpenFile(path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0755) - if err != nil { - return fmt.Errorf("error creating temporary file for installer script: %w", err) - } - defer tempFile.Close() - defer os.Remove(tempFile.Name()) - _, err = io.Copy(tempFile, resp.Body) - if err != nil { - return fmt.Errorf("error writing installer package to disk: %w", err) - } - tempFile.Close() - - // Initialize installation command - cmd, err := c.newCommand(fmt.Sprintf("sh -c \"%s %s\"", path, strings.Join(flags, " "))) - if err != nil { - return err + flags := []string{} + if path != "" { + flags = append(flags, fmt.Sprintf("-p %s", shellescape.Quote(path))) } - defer func() { - _ = cmd.Close() - }() - - // Get command output pipes - cmdOut, err := cmd.StdoutPipe() - if err != nil { - return err + if noDeps { + flags = append(flags, "-d") } - cmdErr, err := cmd.StderrPipe() - if err != nil { - return err + if dataPath != "" { + flags = append(flags, fmt.Sprintf("-u %s", dataPath)) } - // Print progress from stdout - go (func() { - scanner := bufio.NewScanner(cmdOut) - for scanner.Scan() { - fmt.Println(scanner.Text()) - } - })() - - // Read command & error output from stderr; render in verbose mode - var errMessage string - go (func() { - c := color.New(DebugColor) - scanner := bufio.NewScanner(cmdErr) - for scanner.Scan() { - errMessage = scanner.Text() - if verbose { - _, _ = c.Println(scanner.Text()) - } - } - })() + // Load the installation script + return c.runScript(assets.InstallScript(), verbose, flags) +} - // Run command and return error output - err = cmd.Run() - if err != nil { - return fmt.Errorf("Could not install Rocket Pool update tracker: %s", errMessage) - } - return nil +// Install the update tracker +func (c *Client) InstallUpdateTracker(verbose bool) error { + return c.runScript(assets.InstallUpdateTrackerScript(), verbose, nil) } // Start the Rocket Pool service func (c *Client) StartService(composeFiles []string) error { - /* - // Start the API container first - cmd, err := c.compose([]string{}, "up -d --quiet-pull") - if err != nil { - return fmt.Errorf("error creating compose command for API container: %w", err) - } - err = c.printOutput(cmd) - if err != nil { - return fmt.Errorf("error starting API container: %w", err) - } - */ // Start all of the containers cmd, err := c.compose(composeFiles, "up -d --remove-orphans --quiet-pull") if err != nil { @@ -505,11 +471,6 @@ func (c *Client) StopService(composeFiles []string) error { // Stop the Rocket Pool service and remove the config folder func (c *Client) TerminateService(composeFiles []string, configPath string) error { - // Get the command to run with root privileges - rootCmd, err := c.getEscalationCommand() - if err != nil { - return fmt.Errorf("could not get privilege escalation command: %w", err) - } // Terminate the Docker containers cmd, err := c.compose(composeFiles, "down -v") @@ -527,8 +488,9 @@ func (c *Client) TerminateService(composeFiles []string, configPath string) erro return fmt.Errorf("error loading Rocket Pool directory: %w", err) } fmt.Printf("Deleting Rocket Pool directory (%s)...\n", path) - cmd = fmt.Sprintf("%s rm -rf %s", rootCmd, path) - _, err = c.readOutput(cmd) + cmd = fmt.Sprintf("rm -rf %s", path) + // The directory contains root-owned paths, so delete it as root + _, err = c.readOutputSudo(cmd) if err != nil { return fmt.Errorf("error deleting Rocket Pool directory: %w", err) } @@ -852,10 +814,10 @@ func (c *Client) GetVolumeSize(volumeName string) (string, error) { } // Runs the prune provisioner -func (c *Client) RunPruneProvisioner(container string, volume string, image string) error { +func (c *Client) RunPruneProvisioner(container, volume string) error { // Run the prune provisioner - cmd := fmt.Sprintf("docker run --rm --name %s -v %s:/ethclient %s", container, volume, image) + cmd := fmt.Sprintf("docker run --rm --name %s -v %s:/ethclient alpine:latest sh -c 'touch /ethclient/prune.lock'", container, volume) output, err := c.readOutput(cmd) if err != nil { return err @@ -870,52 +832,78 @@ func (c *Client) RunPruneProvisioner(container string, volume string, image stri } -// Executes a Go program that triggers NM pruning -func (c *Client) RunNethermindPruneStarter(executionContainerName string, pruneStarterContainerName string) error { - cmd := fmt.Sprintf(`docker run --rm --name %s --network container:%s rocketpool/nm-prune-starter %s`, pruneStarterContainerName, executionContainerName, nethermindAdminUrl) +// Curls the Nethermind admin URL to trigger pruning +func (c *Client) RunNethermindPruneStarter(executionContainerName string) error { + retryCount := 5 + retryTime := 3 * time.Second - err := c.printOutput(cmd) - if err != nil { - return err - } - return nil -} + for i := 0; i < retryCount; i++ { + command := fmt.Sprintf(`-m 30 -H "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0","method":"admin_prune","params":[],"id":%d}' %s`, i+1, nethermindAdminUrl) + cmdText := fmt.Sprintf(`docker run --quiet --rm --name curl%s --network container:%s curlimages/curl -Ss %s`, pruneStarterContainerSuffix, executionContainerName, command) -// Runs the EC migrator -func (c *Client) RunEcMigrator(container string, volume string, targetDir string, mode string, image string) error { - cmd := fmt.Sprintf("docker run --rm --name %s -v %s:/ethclient -v %s:/mnt/external -e EC_MIGRATE_MODE='%s' %s", container, volume, targetDir, mode, image) - err := c.printOutput(cmd) - if err != nil { - return err - } + if i != 0 { + fmt.Printf("Trying again in %v... (%d/%d)\n", retryTime, i+1, retryCount) + time.Sleep(retryTime) + } - return nil -} + cmd, err := c.newCommand(cmdText) + if err != nil { + return fmt.Errorf("error creating command for prune starter: %w", err) + } -// Gets the size of the target directory via the EC migrator for importing, which should have the same permissions as exporting -func (c *Client) GetDirSizeViaEcMigrator(container string, targetDir string, image string) (uint64, error) { - cmd := fmt.Sprintf("docker run --rm --name %s -v %s:/mnt/external -e OPERATION='size' %s", container, targetDir, image) - output, err := c.readOutput(cmd) - if err != nil { - return 0, fmt.Errorf("Error getting source directory size: %w", err) - } + stdOut, stdErr, err := cmd.OutputPipes() + if err != nil { + return fmt.Errorf("error getting output pipes for prune starter: %w", err) + } - trimmedOutput := strings.TrimRight(string(output), "\n") - dirSize, err := strconv.ParseUint(trimmedOutput, 0, 64) - if err != nil { - return 0, fmt.Errorf("Error parsing directory size output [%s]: %w", trimmedOutput, err) - } + err = cmd.Start() + if err != nil { + return fmt.Errorf("error running prune starter: %w", err) + } + defer func() { + _ = cmd.Wait() + }() + + // Check for a curl error + stdErrTextBytes, err := io.ReadAll(stdErr) + if err != nil { + return fmt.Errorf("error reading error from prune starter: %w", err) + } + stdErrText := string(stdErrTextBytes) + if stdErrText != "" { + fmt.Printf("Error while curling the Nethermind admin URL: %s\n", stdErrText) + continue + } + + // Grab the response + stdOutText, err := io.ReadAll(stdOut) + if err != nil { + return fmt.Errorf("error reading response from prune starter: %w", err) + } + // Parse the response as JSON + var response map[string]any + err = json.Unmarshal(stdOutText, &response) + if err != nil { + return fmt.Errorf("error parsing response from prune starter: %w", err) + } + + if errObject, ok := response["error"].(map[string]any); ok { + fmt.Printf("Error starting prune: code %d, message = %s, data = %s\n", errObject["code"], errObject["message"], errObject["data"]) + continue + } else { + fmt.Printf("Success: Pruning is now \"%s\"\n", response["result"]) + fmt.Println("Your main execution client is now pruning. You can follow its progress with `rocketpool service logs eth1`.") + fmt.Println("NOTE: While pruning, you **cannot** interrupt the client (e.g. by restarting) or you risk corrupting the database!") + fmt.Println("You must let it run to completion!") + break + } - return dirSize, nil + } + return nil } // Deletes the node wallet and all validator keys, and restarts the Docker containers func (c *Client) PurgeAllKeys(composeFiles []string) error { - // Get the command to run with root privileges - rootCmd, err := c.getEscalationCommand() - if err != nil { - return fmt.Errorf("could not get privilege escalation command: %w", err) - } // Get the config cfg, _, err := c.LoadConfig() @@ -941,8 +929,9 @@ func (c *Client) PurgeAllKeys(composeFiles []string) error { return fmt.Errorf("error loading wallet path: %w", err) } fmt.Println("Deleting wallet...") - cmd := fmt.Sprintf("%s rm -f %s", rootCmd, walletPath) - _, err = c.readOutput(cmd) + cmd := fmt.Sprintf("rm -f %s", walletPath) + // The file is owned by root, so delete as root + _, err = c.readOutputSudo(cmd) if err != nil { return fmt.Errorf("error deleting wallet: %w", err) } @@ -953,8 +942,9 @@ func (c *Client) PurgeAllKeys(composeFiles []string) error { return fmt.Errorf("error loading password path: %w", err) } fmt.Println("Deleting password...") - cmd = fmt.Sprintf("%s rm -f %s", rootCmd, passwordPath) - _, err = c.readOutput(cmd) + cmd = fmt.Sprintf("rm -f %s", passwordPath) + // The file is owned by root, so delete as root + _, err = c.readOutputSudo(cmd) if err != nil { return fmt.Errorf("error deleting password: %w", err) } @@ -965,13 +955,19 @@ func (c *Client) PurgeAllKeys(composeFiles []string) error { return fmt.Errorf("error loading validators folder path: %w", err) } fmt.Println("Deleting validator keys...") - cmd = fmt.Sprintf("%s rm -rf %s/*", rootCmd, validatorsPath) - _, err = c.readOutput(cmd) + cmd = fmt.Sprintf("rm -rf %s/*", validatorsPath) + // The validators path can be created by the smartnode daemon (owned by root, 0600) + // So delete its contents as root, otherwise the * won't expand. + // NB: we delete the contents of the folder instead of recreating the folder + // This way, if the drive is full, we don't release the directory inode and fail to recreate it. + _, err = c.readOutputSudo(cmd) if err != nil { return fmt.Errorf("error deleting validator keys: %w", err) } - cmd = fmt.Sprintf("%s rm -rf %s/.[a-zA-Z0-9]*", rootCmd, validatorsPath) - _, err = c.readOutput(cmd) + // Also delete hidden files + cmd = fmt.Sprintf("rm -rf %s/.[a-zA-Z0-9]*", validatorsPath) + // also as root, so bash can expand the regex + _, err = c.readOutputSudo(cmd) if err != nil { return fmt.Errorf("error deleting hidden files in validator folder: %w", err) } @@ -1006,31 +1002,6 @@ func (c *Client) SetClientStatusFlags(ignoreSyncCheck bool, forceFallbacks bool) c.forceFallbacks = forceFallbacks } -// Get the command used to escalate privileges on the system -func (c *Client) getEscalationCommand() (string, error) { - // Check for sudo first - sudo := "sudo" - exists, err := c.checkIfCommandExists(sudo) - if err != nil { - return "", fmt.Errorf("error checking if %s exists: %w", sudo, err) - } - if exists { - return sudo, nil - } - - // Check for doas next - doas := "doas" - exists, err = c.checkIfCommandExists(doas) - if err != nil { - return "", fmt.Errorf("error checking if %s exists: %w", doas, err) - } - if exists { - return doas, nil - } - - return "", fmt.Errorf("no privilege escalation command found") -} - func (c *Client) checkIfCommandExists(command string) (bool, error) { // Run `type` to check for existence cmd := fmt.Sprintf("type %s", command) @@ -1419,6 +1390,26 @@ func (c *Client) printOutput(cmdText string) error { } +// Run a command as root and return its output +func (c *Client) readOutputSudo(rootCmdText string) ([]byte, error) { + var escCmd string + for _, escalationCommand := range []string{"sudo", "doas"} { + exists, err := c.checkIfCommandExists(escalationCommand) + if err != nil { + return nil, fmt.Errorf("error checking if %s exists: %w", escalationCommand, err) + } + if exists { + escCmd = escalationCommand + break + } + } + if escCmd == "" { + return nil, fmt.Errorf("no privilege escalation command found") + } + + return c.readOutput(fmt.Sprintf("%s bash -c %s", escCmd, shellescape.Quote(rootCmdText))) +} + // Run a command and return its output func (c *Client) readOutput(cmdText string) ([]byte, error) { diff --git a/shared/services/rocketpool/node.go b/shared/services/rocketpool/node.go index 425d1a43e..56ec56ecb 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" ) @@ -698,8 +698,8 @@ func (c *Client) NodeDeposit(amountWei *big.Int, minFee float64, salt *big.Int, } // Check whether the node can send tokens -func (c *Client) CanNodeSend(amountWei *big.Int, token string, toAddress common.Address) (api.CanNodeSendResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("node can-send %s %s %s", amountWei.String(), token, toAddress.Hex())) +func (c *Client) CanNodeSend(amountRaw float64, token string, toAddress common.Address) (api.CanNodeSendResponse, error) { + responseBytes, err := c.callAPI(fmt.Sprintf("node can-send %.10f %s %s", amountRaw, token, toAddress.Hex())) if err != nil { return api.CanNodeSendResponse{}, fmt.Errorf("Could not get can node send status: %w", err) } @@ -714,8 +714,8 @@ func (c *Client) CanNodeSend(amountWei *big.Int, token string, toAddress common. } // Send tokens from the node to an address -func (c *Client) NodeSend(amountWei *big.Int, token string, toAddress common.Address) (api.NodeSendResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("node send %s %s %s", amountWei.String(), token, toAddress.Hex())) +func (c *Client) NodeSend(amountRaw float64, token string, toAddress common.Address) (api.NodeSendResponse, error) { + responseBytes, err := c.callAPI(fmt.Sprintf("node send %.10f %s %s", amountRaw, token, toAddress.Hex())) if err != nil { return api.NodeSendResponse{}, fmt.Errorf("Could not send tokens from node: %w", err) } 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 b6e68d281..922ff5acd 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 ebb4fbfd0..8ad71fe49 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" ) @@ -447,7 +447,7 @@ type CreateVacantMinipoolResponse struct { type CanNodeSendResponse struct { Status string `json:"status"` Error string `json:"error"` - Balance *big.Int `json:"balance"` + Balance float64 `json:"balance"` TokenName string `json:"name"` TokenSymbol string `json:"symbol"` CanSend bool `json:"canSend"` 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/state.go b/shared/types/api/state.go index 3274586f3..fb9a19721 100644 --- a/shared/types/api/state.go +++ b/shared/types/api/state.go @@ -1,5 +1,25 @@ package api +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" +) + type BeaconStateResponse struct { Proof []string `json:"proof"` } + +type WithdrawalProofResponse struct { + Slot uint64 `json:"slot"` + WithdrawalSlot uint64 `json:"withdrawalSlot"` + ValidatorIndex uint64 `json:"validatorIndex"` + Amount *big.Int `json:"amount"` + Proof []string `json:"proof"` + + // Contract refers to this as _withdrawalNum + IndexInWithdrawalsArray uint `json:"indexInWithdrawalsArray"` + // Part of the Withdrawal calldata + WithdrawalIndex uint64 `json:"withdrawalIndex"` + WithdrawalAddress common.Address `json:"withdrawalAddress"` +} 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/types/eth2/fork/deneb/block_deneb.go b/shared/types/eth2/fork/deneb/block_deneb.go new file mode 100644 index 000000000..91c3ae90f --- /dev/null +++ b/shared/types/eth2/fork/deneb/block_deneb.go @@ -0,0 +1,89 @@ +package deneb + +import "github.com/rocket-pool/smartnode/shared/types/eth2/generic" + +// Important indices for proof generation: +const BeaconBlockBodyChunksCeil uint64 = 16 + +func (b *BeaconBlock) ProveWithdrawal(indexInWithdrawalsArray uint64) ([][]byte, error) { + tree, err := b.GetTree() + if err != nil { + return nil, err + } + + gid := uint64(1) + // Navigate to the body + gid = gid*generic.BeaconBlockChunksCeil + generic.BeaconBlockBodyIndex + // Then to the ExecutionPayload + gid = gid*BeaconBlockBodyChunksCeil + generic.BeaconBlockBodyExecutionPayloadIndex + // Then to the withdrawals array + gid = gid*generic.BeaconBlockBodyExecutionPayloadChunksCeil + generic.BeaconBlockBodyExecutionPayloadWithdrawalsIndex + // Then to the array contents + gid = gid * 2 + // Finally to the withdrawal in question + gid = gid*generic.BeaconBlockWithdrawalsArrayMax + indexInWithdrawalsArray + + proof, err := tree.Prove(int(gid)) + if err != nil { + return nil, err + } + + return proof.Hashes, nil +} + +// Types needed for withdrawal proofs +type BeaconBlock struct { + Slot uint64 `json:"slot"` + ProposerIndex uint64 `json:"proposer_index"` + ParentRoot [32]byte `json:"parent_root" ssz-size:"32"` + StateRoot [32]byte `json:"state_root" ssz-size:"32"` + Body *BeaconBlockBody `json:"body"` +} + +type SignedBeaconBlock struct { + Block *BeaconBlock `json:"message"` + Signature []byte `json:"signature" ssz-size:"96"` +} + +type BeaconBlockBody struct { + RandaoReveal []byte `json:"randao_reveal" ssz-size:"96"` + Eth1Data *generic.Eth1Data `json:"eth1_data"` + Graffiti [32]byte `json:"graffiti" ssz-size:"32"` + ProposerSlashings []*generic.ProposerSlashing `json:"proposer_slashings" ssz-max:"16"` + AttesterSlashings []*generic.AttesterSlashing `json:"attester_slashings" ssz-max:"2"` + Attestations []*generic.Attestation `json:"attestations" ssz-max:"128"` + Deposits []*generic.Deposit `json:"deposits" ssz-max:"16"` + VoluntaryExits []*generic.SignedVoluntaryExit `json:"voluntary_exits" ssz-max:"16"` + SyncAggregate *generic.SyncAggregate `json:"sync_aggregate"` + ExecutionPayload *ExecutionPayload `json:"execution_payload"` + BlsToExecutionChanges []*generic.SignedBLSToExecutionChange `json:"bls_to_execution_changes" ssz-max:"16"` + BlobKzgCommitments [][48]byte `json:"blob_kzg_commitments" ssz-max:"4096,48" ssz-size:"?,48"` +} + +type ExecutionPayload struct { + ParentHash [32]byte `ssz-size:"32" json:"parent_hash"` + FeeRecipient [20]byte `ssz-size:"20" json:"fee_recipient"` + StateRoot [32]byte `ssz-size:"32" json:"state_root"` + ReceiptsRoot [32]byte `ssz-size:"32" json:"receipts_root"` + LogsBloom [256]byte `ssz-size:"256" json:"logs_bloom"` + PrevRandao [32]byte `ssz-size:"32" json:"prev_randao"` + BlockNumber uint64 `json:"block_number"` + GasLimit uint64 `json:"gas_limit"` + GasUsed uint64 `json:"gas_used"` + Timestamp uint64 `json:"timestamp"` + ExtraData []byte `ssz-max:"32" json:"extra_data"` + BaseFeePerGas generic.Uint256 `ssz-size:"32" json:"base_fee_per_gas"` + BlockHash [32]byte `ssz-size:"32" json:"block_hash"` + Transactions [][]byte `ssz-max:"1048576,1073741824" ssz-size:"?,?" json:"transactions"` + Withdrawals []*generic.Withdrawal `json:"withdrawals" ssz-max:"16"` + BlobGasUsed uint64 `json:"blob_gas_used"` + ExcessBlobGas uint64 `json:"excess_blob_gas"` +} + +func (b *BeaconBlock) HasExecutionPayload() bool { + return b.Body.ExecutionPayload != nil +} + +func (b *BeaconBlock) Withdrawals() []*generic.Withdrawal { + return b.Body.ExecutionPayload.Withdrawals +} diff --git a/shared/types/eth2/fork/deneb/block_deneb_encoding.go b/shared/types/eth2/fork/deneb/block_deneb_encoding.go new file mode 100644 index 000000000..9f1f546e9 --- /dev/null +++ b/shared/types/eth2/fork/deneb/block_deneb_encoding.go @@ -0,0 +1,1203 @@ +// Code generated by fastssz. DO NOT EDIT. +// Hash: 9015b678c5cb9d652ed10fdd64e476014863a336c0fcf7c8ee3a36944565919b +// Version: 0.1.3 +package deneb + +import ( + ssz "github.com/ferranbt/fastssz" + "github.com/rocket-pool/smartnode/shared/types/eth2/generic" +) + +// MarshalSSZ ssz marshals the BeaconBlockDeneb object +func (b *BeaconBlock) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(b) +} + +// MarshalSSZTo ssz marshals the BeaconBlockDeneb object to a target array +func (b *BeaconBlock) MarshalSSZTo(buf []byte) (dst []byte, err error) { + dst = buf + offset := int(84) + + // Field (0) 'Slot' + dst = ssz.MarshalUint64(dst, b.Slot) + + // Field (1) 'ProposerIndex' + dst = ssz.MarshalUint64(dst, b.ProposerIndex) + + // Field (2) 'ParentRoot' + dst = append(dst, b.ParentRoot[:]...) + + // Field (3) 'StateRoot' + dst = append(dst, b.StateRoot[:]...) + + // Offset (4) 'Body' + dst = ssz.WriteOffset(dst, offset) + + // Field (4) 'Body' + if dst, err = b.Body.MarshalSSZTo(dst); err != nil { + return + } + + return +} + +// UnmarshalSSZ ssz unmarshals the BeaconBlockDeneb object +func (b *BeaconBlock) UnmarshalSSZ(buf []byte) error { + var err error + size := uint64(len(buf)) + if size < 84 { + return ssz.ErrSize + } + + tail := buf + var o4 uint64 + + // Field (0) 'Slot' + b.Slot = ssz.UnmarshallUint64(buf[0:8]) + + // Field (1) 'ProposerIndex' + b.ProposerIndex = ssz.UnmarshallUint64(buf[8:16]) + + // Field (2) 'ParentRoot' + copy(b.ParentRoot[:], buf[16:48]) + + // Field (3) 'StateRoot' + copy(b.StateRoot[:], buf[48:80]) + + // Offset (4) 'Body' + if o4 = ssz.ReadOffset(buf[80:84]); o4 > size { + return ssz.ErrOffset + } + + if o4 != 84 { + return ssz.ErrInvalidVariableOffset + } + + // Field (4) 'Body' + { + buf = tail[o4:] + if b.Body == nil { + b.Body = new(BeaconBlockBody) + } + if err = b.Body.UnmarshalSSZ(buf); err != nil { + return err + } + } + return err +} + +// SizeSSZ returns the ssz encoded size in bytes for the BeaconBlockDeneb object +func (b *BeaconBlock) SizeSSZ() (size int) { + size = 84 + + // Field (4) 'Body' + if b.Body == nil { + b.Body = new(BeaconBlockBody) + } + size += b.Body.SizeSSZ() + + return +} + +// HashTreeRoot ssz hashes the BeaconBlockDeneb object +func (b *BeaconBlock) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(b) +} + +// HashTreeRootWith ssz hashes the BeaconBlockDeneb object with a hasher +func (b *BeaconBlock) HashTreeRootWith(hh ssz.HashWalker) (err error) { + indx := hh.Index() + + // Field (0) 'Slot' + hh.PutUint64(b.Slot) + + // Field (1) 'ProposerIndex' + hh.PutUint64(b.ProposerIndex) + + // Field (2) 'ParentRoot' + hh.PutBytes(b.ParentRoot[:]) + + // Field (3) 'StateRoot' + hh.PutBytes(b.StateRoot[:]) + + // Field (4) 'Body' + if err = b.Body.HashTreeRootWith(hh); err != nil { + return + } + + hh.Merkleize(indx) + return +} + +// GetTree ssz hashes the BeaconBlockDeneb object +func (b *BeaconBlock) GetTree() (*ssz.Node, error) { + return ssz.ProofTree(b) +} + +// MarshalSSZ ssz marshals the SignedBeaconBlockDeneb object +func (s *SignedBeaconBlock) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(s) +} + +// MarshalSSZTo ssz marshals the SignedBeaconBlockDeneb object to a target array +func (s *SignedBeaconBlock) MarshalSSZTo(buf []byte) (dst []byte, err error) { + dst = buf + offset := int(100) + + // Offset (0) 'Block' + dst = ssz.WriteOffset(dst, offset) + + // Field (1) 'Signature' + if size := len(s.Signature); size != 96 { + err = ssz.ErrBytesLengthFn("SignedBeaconBlockDeneb.Signature", size, 96) + return + } + dst = append(dst, s.Signature...) + + // Field (0) 'Block' + if dst, err = s.Block.MarshalSSZTo(dst); err != nil { + return + } + + return +} + +// UnmarshalSSZ ssz unmarshals the SignedBeaconBlockDeneb object +func (s *SignedBeaconBlock) UnmarshalSSZ(buf []byte) error { + var err error + size := uint64(len(buf)) + if size < 100 { + return ssz.ErrSize + } + + tail := buf + var o0 uint64 + + // Offset (0) 'Block' + if o0 = ssz.ReadOffset(buf[0:4]); o0 > size { + return ssz.ErrOffset + } + + if o0 != 100 { + return ssz.ErrInvalidVariableOffset + } + + // Field (1) 'Signature' + if cap(s.Signature) == 0 { + s.Signature = make([]byte, 0, len(buf[4:100])) + } + s.Signature = append(s.Signature, buf[4:100]...) + + // Field (0) 'Block' + { + buf = tail[o0:] + if s.Block == nil { + s.Block = new(BeaconBlock) + } + if err = s.Block.UnmarshalSSZ(buf); err != nil { + return err + } + } + return err +} + +// SizeSSZ returns the ssz encoded size in bytes for the SignedBeaconBlockDeneb object +func (s *SignedBeaconBlock) SizeSSZ() (size int) { + size = 100 + + // Field (0) 'Block' + if s.Block == nil { + s.Block = new(BeaconBlock) + } + size += s.Block.SizeSSZ() + + return +} + +// HashTreeRoot ssz hashes the SignedBeaconBlockDeneb object +func (s *SignedBeaconBlock) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(s) +} + +// HashTreeRootWith ssz hashes the SignedBeaconBlockDeneb object with a hasher +func (s *SignedBeaconBlock) HashTreeRootWith(hh ssz.HashWalker) (err error) { + indx := hh.Index() + + // Field (0) 'Block' + if err = s.Block.HashTreeRootWith(hh); err != nil { + return + } + + // Field (1) 'Signature' + if size := len(s.Signature); size != 96 { + err = ssz.ErrBytesLengthFn("SignedBeaconBlockDeneb.Signature", size, 96) + return + } + hh.PutBytes(s.Signature) + + hh.Merkleize(indx) + return +} + +// GetTree ssz hashes the SignedBeaconBlockDeneb object +func (s *SignedBeaconBlock) GetTree() (*ssz.Node, error) { + return ssz.ProofTree(s) +} + +// MarshalSSZ ssz marshals the BeaconBlockBodyDeneb object +func (b *BeaconBlockBody) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(b) +} + +// MarshalSSZTo ssz marshals the BeaconBlockBodyDeneb object to a target array +func (b *BeaconBlockBody) MarshalSSZTo(buf []byte) (dst []byte, err error) { + dst = buf + offset := int(392) + + // Field (0) 'RandaoReveal' + if size := len(b.RandaoReveal); size != 96 { + err = ssz.ErrBytesLengthFn("BeaconBlockBodyDeneb.RandaoReveal", size, 96) + return + } + dst = append(dst, b.RandaoReveal...) + + // Field (1) 'Eth1Data' + if b.Eth1Data == nil { + b.Eth1Data = new(generic.Eth1Data) + } + if dst, err = b.Eth1Data.MarshalSSZTo(dst); err != nil { + return + } + + // Field (2) 'Graffiti' + dst = append(dst, b.Graffiti[:]...) + + // Offset (3) 'ProposerSlashings' + dst = ssz.WriteOffset(dst, offset) + offset += len(b.ProposerSlashings) * 416 + + // Offset (4) 'AttesterSlashings' + dst = ssz.WriteOffset(dst, offset) + for ii := 0; ii < len(b.AttesterSlashings); ii++ { + offset += 4 + offset += b.AttesterSlashings[ii].SizeSSZ() + } + + // Offset (5) 'Attestations' + dst = ssz.WriteOffset(dst, offset) + for ii := 0; ii < len(b.Attestations); ii++ { + offset += 4 + offset += b.Attestations[ii].SizeSSZ() + } + + // Offset (6) 'Deposits' + dst = ssz.WriteOffset(dst, offset) + offset += len(b.Deposits) * 1240 + + // Offset (7) 'VoluntaryExits' + dst = ssz.WriteOffset(dst, offset) + offset += len(b.VoluntaryExits) * 112 + + // Field (8) 'SyncAggregate' + if b.SyncAggregate == nil { + b.SyncAggregate = new(generic.SyncAggregate) + } + if dst, err = b.SyncAggregate.MarshalSSZTo(dst); err != nil { + return + } + + // Offset (9) 'ExecutionPayload' + dst = ssz.WriteOffset(dst, offset) + if b.ExecutionPayload == nil { + b.ExecutionPayload = new(ExecutionPayload) + } + offset += b.ExecutionPayload.SizeSSZ() + + // Offset (10) 'BlsToExecutionChanges' + dst = ssz.WriteOffset(dst, offset) + offset += len(b.BlsToExecutionChanges) * 172 + + // Offset (11) 'BlobKzgCommitments' + dst = ssz.WriteOffset(dst, offset) + + // Field (3) 'ProposerSlashings' + if size := len(b.ProposerSlashings); size > 16 { + err = ssz.ErrListTooBigFn("BeaconBlockBodyDeneb.ProposerSlashings", size, 16) + return + } + for ii := 0; ii < len(b.ProposerSlashings); ii++ { + if dst, err = b.ProposerSlashings[ii].MarshalSSZTo(dst); err != nil { + return + } + } + + // Field (4) 'AttesterSlashings' + if size := len(b.AttesterSlashings); size > 2 { + err = ssz.ErrListTooBigFn("BeaconBlockBodyDeneb.AttesterSlashings", size, 2) + return + } + { + offset = 4 * len(b.AttesterSlashings) + for ii := 0; ii < len(b.AttesterSlashings); ii++ { + dst = ssz.WriteOffset(dst, offset) + offset += b.AttesterSlashings[ii].SizeSSZ() + } + } + for ii := 0; ii < len(b.AttesterSlashings); ii++ { + if dst, err = b.AttesterSlashings[ii].MarshalSSZTo(dst); err != nil { + return + } + } + + // Field (5) 'Attestations' + if size := len(b.Attestations); size > 128 { + err = ssz.ErrListTooBigFn("BeaconBlockBodyDeneb.Attestations", size, 128) + return + } + { + offset = 4 * len(b.Attestations) + for ii := 0; ii < len(b.Attestations); ii++ { + dst = ssz.WriteOffset(dst, offset) + offset += b.Attestations[ii].SizeSSZ() + } + } + for ii := 0; ii < len(b.Attestations); ii++ { + if dst, err = b.Attestations[ii].MarshalSSZTo(dst); err != nil { + return + } + } + + // Field (6) 'Deposits' + if size := len(b.Deposits); size > 16 { + err = ssz.ErrListTooBigFn("BeaconBlockBodyDeneb.Deposits", size, 16) + return + } + for ii := 0; ii < len(b.Deposits); ii++ { + if dst, err = b.Deposits[ii].MarshalSSZTo(dst); err != nil { + return + } + } + + // Field (7) 'VoluntaryExits' + if size := len(b.VoluntaryExits); size > 16 { + err = ssz.ErrListTooBigFn("BeaconBlockBodyDeneb.VoluntaryExits", size, 16) + return + } + for ii := 0; ii < len(b.VoluntaryExits); ii++ { + if dst, err = b.VoluntaryExits[ii].MarshalSSZTo(dst); err != nil { + return + } + } + + // Field (9) 'ExecutionPayload' + if dst, err = b.ExecutionPayload.MarshalSSZTo(dst); err != nil { + return + } + + // Field (10) 'BlsToExecutionChanges' + if size := len(b.BlsToExecutionChanges); size > 16 { + err = ssz.ErrListTooBigFn("BeaconBlockBodyDeneb.BlsToExecutionChanges", size, 16) + return + } + for ii := 0; ii < len(b.BlsToExecutionChanges); ii++ { + if dst, err = b.BlsToExecutionChanges[ii].MarshalSSZTo(dst); err != nil { + return + } + } + + // Field (11) 'BlobKzgCommitments' + if size := len(b.BlobKzgCommitments); size > 4096 { + err = ssz.ErrListTooBigFn("BeaconBlockBodyDeneb.BlobKzgCommitments", size, 4096) + return + } + for ii := 0; ii < len(b.BlobKzgCommitments); ii++ { + dst = append(dst, b.BlobKzgCommitments[ii][:]...) + } + + return +} + +// UnmarshalSSZ ssz unmarshals the BeaconBlockBodyDeneb object +func (b *BeaconBlockBody) UnmarshalSSZ(buf []byte) error { + var err error + size := uint64(len(buf)) + if size < 392 { + return ssz.ErrSize + } + + tail := buf + var o3, o4, o5, o6, o7, o9, o10, o11 uint64 + + // Field (0) 'RandaoReveal' + if cap(b.RandaoReveal) == 0 { + b.RandaoReveal = make([]byte, 0, len(buf[0:96])) + } + b.RandaoReveal = append(b.RandaoReveal, buf[0:96]...) + + // Field (1) 'Eth1Data' + if b.Eth1Data == nil { + b.Eth1Data = new(generic.Eth1Data) + } + if err = b.Eth1Data.UnmarshalSSZ(buf[96:168]); err != nil { + return err + } + + // Field (2) 'Graffiti' + copy(b.Graffiti[:], buf[168:200]) + + // Offset (3) 'ProposerSlashings' + if o3 = ssz.ReadOffset(buf[200:204]); o3 > size { + return ssz.ErrOffset + } + + if o3 != 392 { + return ssz.ErrInvalidVariableOffset + } + + // Offset (4) 'AttesterSlashings' + if o4 = ssz.ReadOffset(buf[204:208]); o4 > size || o3 > o4 { + return ssz.ErrOffset + } + + // Offset (5) 'Attestations' + if o5 = ssz.ReadOffset(buf[208:212]); o5 > size || o4 > o5 { + return ssz.ErrOffset + } + + // Offset (6) 'Deposits' + if o6 = ssz.ReadOffset(buf[212:216]); o6 > size || o5 > o6 { + return ssz.ErrOffset + } + + // Offset (7) 'VoluntaryExits' + if o7 = ssz.ReadOffset(buf[216:220]); o7 > size || o6 > o7 { + return ssz.ErrOffset + } + + // Field (8) 'SyncAggregate' + if b.SyncAggregate == nil { + b.SyncAggregate = new(generic.SyncAggregate) + } + if err = b.SyncAggregate.UnmarshalSSZ(buf[220:380]); err != nil { + return err + } + + // Offset (9) 'ExecutionPayload' + if o9 = ssz.ReadOffset(buf[380:384]); o9 > size || o7 > o9 { + return ssz.ErrOffset + } + + // Offset (10) 'BlsToExecutionChanges' + if o10 = ssz.ReadOffset(buf[384:388]); o10 > size || o9 > o10 { + return ssz.ErrOffset + } + + // Offset (11) 'BlobKzgCommitments' + if o11 = ssz.ReadOffset(buf[388:392]); o11 > size || o10 > o11 { + return ssz.ErrOffset + } + + // Field (3) 'ProposerSlashings' + { + buf = tail[o3:o4] + num, err := ssz.DivideInt2(len(buf), 416, 16) + if err != nil { + return err + } + b.ProposerSlashings = make([]*generic.ProposerSlashing, num) + for ii := 0; ii < num; ii++ { + if b.ProposerSlashings[ii] == nil { + b.ProposerSlashings[ii] = new(generic.ProposerSlashing) + } + if err = b.ProposerSlashings[ii].UnmarshalSSZ(buf[ii*416 : (ii+1)*416]); err != nil { + return err + } + } + } + + // Field (4) 'AttesterSlashings' + { + buf = tail[o4:o5] + num, err := ssz.DecodeDynamicLength(buf, 2) + if err != nil { + return err + } + b.AttesterSlashings = make([]*generic.AttesterSlashing, num) + err = ssz.UnmarshalDynamic(buf, num, func(indx int, buf []byte) (err error) { + if b.AttesterSlashings[indx] == nil { + b.AttesterSlashings[indx] = new(generic.AttesterSlashing) + } + if err = b.AttesterSlashings[indx].UnmarshalSSZ(buf); err != nil { + return err + } + return nil + }) + if err != nil { + return err + } + } + + // Field (5) 'Attestations' + { + buf = tail[o5:o6] + num, err := ssz.DecodeDynamicLength(buf, 128) + if err != nil { + return err + } + b.Attestations = make([]*generic.Attestation, num) + err = ssz.UnmarshalDynamic(buf, num, func(indx int, buf []byte) (err error) { + if b.Attestations[indx] == nil { + b.Attestations[indx] = new(generic.Attestation) + } + if err = b.Attestations[indx].UnmarshalSSZ(buf); err != nil { + return err + } + return nil + }) + if err != nil { + return err + } + } + + // Field (6) 'Deposits' + { + buf = tail[o6:o7] + num, err := ssz.DivideInt2(len(buf), 1240, 16) + if err != nil { + return err + } + b.Deposits = make([]*generic.Deposit, num) + for ii := 0; ii < num; ii++ { + if b.Deposits[ii] == nil { + b.Deposits[ii] = new(generic.Deposit) + } + if err = b.Deposits[ii].UnmarshalSSZ(buf[ii*1240 : (ii+1)*1240]); err != nil { + return err + } + } + } + + // Field (7) 'VoluntaryExits' + { + buf = tail[o7:o9] + num, err := ssz.DivideInt2(len(buf), 112, 16) + if err != nil { + return err + } + b.VoluntaryExits = make([]*generic.SignedVoluntaryExit, num) + for ii := 0; ii < num; ii++ { + if b.VoluntaryExits[ii] == nil { + b.VoluntaryExits[ii] = new(generic.SignedVoluntaryExit) + } + if err = b.VoluntaryExits[ii].UnmarshalSSZ(buf[ii*112 : (ii+1)*112]); err != nil { + return err + } + } + } + + // Field (9) 'ExecutionPayload' + { + buf = tail[o9:o10] + if b.ExecutionPayload == nil { + b.ExecutionPayload = new(ExecutionPayload) + } + if err = b.ExecutionPayload.UnmarshalSSZ(buf); err != nil { + return err + } + } + + // Field (10) 'BlsToExecutionChanges' + { + buf = tail[o10:o11] + num, err := ssz.DivideInt2(len(buf), 172, 16) + if err != nil { + return err + } + b.BlsToExecutionChanges = make([]*generic.SignedBLSToExecutionChange, num) + for ii := 0; ii < num; ii++ { + if b.BlsToExecutionChanges[ii] == nil { + b.BlsToExecutionChanges[ii] = new(generic.SignedBLSToExecutionChange) + } + if err = b.BlsToExecutionChanges[ii].UnmarshalSSZ(buf[ii*172 : (ii+1)*172]); err != nil { + return err + } + } + } + + // Field (11) 'BlobKzgCommitments' + { + buf = tail[o11:] + num, err := ssz.DivideInt2(len(buf), 48, 4096) + if err != nil { + return err + } + b.BlobKzgCommitments = make([][48]byte, num) + for ii := 0; ii < num; ii++ { + copy(b.BlobKzgCommitments[ii][:], buf[ii*48:(ii+1)*48]) + } + } + return err +} + +// SizeSSZ returns the ssz encoded size in bytes for the BeaconBlockBodyDeneb object +func (b *BeaconBlockBody) SizeSSZ() (size int) { + size = 392 + + // Field (3) 'ProposerSlashings' + size += len(b.ProposerSlashings) * 416 + + // Field (4) 'AttesterSlashings' + for ii := 0; ii < len(b.AttesterSlashings); ii++ { + size += 4 + size += b.AttesterSlashings[ii].SizeSSZ() + } + + // Field (5) 'Attestations' + for ii := 0; ii < len(b.Attestations); ii++ { + size += 4 + size += b.Attestations[ii].SizeSSZ() + } + + // Field (6) 'Deposits' + size += len(b.Deposits) * 1240 + + // Field (7) 'VoluntaryExits' + size += len(b.VoluntaryExits) * 112 + + // Field (9) 'ExecutionPayload' + if b.ExecutionPayload == nil { + b.ExecutionPayload = new(ExecutionPayload) + } + size += b.ExecutionPayload.SizeSSZ() + + // Field (10) 'BlsToExecutionChanges' + size += len(b.BlsToExecutionChanges) * 172 + + // Field (11) 'BlobKzgCommitments' + size += len(b.BlobKzgCommitments) * 48 + + return +} + +// HashTreeRoot ssz hashes the BeaconBlockBodyDeneb object +func (b *BeaconBlockBody) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(b) +} + +// HashTreeRootWith ssz hashes the BeaconBlockBodyDeneb object with a hasher +func (b *BeaconBlockBody) HashTreeRootWith(hh ssz.HashWalker) (err error) { + indx := hh.Index() + + // Field (0) 'RandaoReveal' + if size := len(b.RandaoReveal); size != 96 { + err = ssz.ErrBytesLengthFn("BeaconBlockBodyDeneb.RandaoReveal", size, 96) + return + } + hh.PutBytes(b.RandaoReveal) + + // Field (1) 'Eth1Data' + if b.Eth1Data == nil { + b.Eth1Data = new(generic.Eth1Data) + } + if err = b.Eth1Data.HashTreeRootWith(hh); err != nil { + return + } + + // Field (2) 'Graffiti' + hh.PutBytes(b.Graffiti[:]) + + // Field (3) 'ProposerSlashings' + { + subIndx := hh.Index() + num := uint64(len(b.ProposerSlashings)) + if num > 16 { + err = ssz.ErrIncorrectListSize + return + } + for _, elem := range b.ProposerSlashings { + if err = elem.HashTreeRootWith(hh); err != nil { + return + } + } + hh.MerkleizeWithMixin(subIndx, num, 16) + } + + // Field (4) 'AttesterSlashings' + { + subIndx := hh.Index() + num := uint64(len(b.AttesterSlashings)) + if num > 2 { + err = ssz.ErrIncorrectListSize + return + } + for _, elem := range b.AttesterSlashings { + if err = elem.HashTreeRootWith(hh); err != nil { + return + } + } + hh.MerkleizeWithMixin(subIndx, num, 2) + } + + // Field (5) 'Attestations' + { + subIndx := hh.Index() + num := uint64(len(b.Attestations)) + if num > 128 { + err = ssz.ErrIncorrectListSize + return + } + for _, elem := range b.Attestations { + if err = elem.HashTreeRootWith(hh); err != nil { + return + } + } + hh.MerkleizeWithMixin(subIndx, num, 128) + } + + // Field (6) 'Deposits' + { + subIndx := hh.Index() + num := uint64(len(b.Deposits)) + if num > 16 { + err = ssz.ErrIncorrectListSize + return + } + for _, elem := range b.Deposits { + if err = elem.HashTreeRootWith(hh); err != nil { + return + } + } + hh.MerkleizeWithMixin(subIndx, num, 16) + } + + // Field (7) 'VoluntaryExits' + { + subIndx := hh.Index() + num := uint64(len(b.VoluntaryExits)) + if num > 16 { + err = ssz.ErrIncorrectListSize + return + } + for _, elem := range b.VoluntaryExits { + if err = elem.HashTreeRootWith(hh); err != nil { + return + } + } + hh.MerkleizeWithMixin(subIndx, num, 16) + } + + // Field (8) 'SyncAggregate' + if b.SyncAggregate == nil { + b.SyncAggregate = new(generic.SyncAggregate) + } + if err = b.SyncAggregate.HashTreeRootWith(hh); err != nil { + return + } + + // Field (9) 'ExecutionPayload' + if err = b.ExecutionPayload.HashTreeRootWith(hh); err != nil { + return + } + + // Field (10) 'BlsToExecutionChanges' + { + subIndx := hh.Index() + num := uint64(len(b.BlsToExecutionChanges)) + if num > 16 { + err = ssz.ErrIncorrectListSize + return + } + for _, elem := range b.BlsToExecutionChanges { + if err = elem.HashTreeRootWith(hh); err != nil { + return + } + } + hh.MerkleizeWithMixin(subIndx, num, 16) + } + + // Field (11) 'BlobKzgCommitments' + { + if size := len(b.BlobKzgCommitments); size > 4096 { + err = ssz.ErrListTooBigFn("BeaconBlockBodyDeneb.BlobKzgCommitments", size, 4096) + return + } + subIndx := hh.Index() + for _, i := range b.BlobKzgCommitments { + hh.PutBytes(i[:]) + } + numItems := uint64(len(b.BlobKzgCommitments)) + hh.MerkleizeWithMixin(subIndx, numItems, 4096) + } + + hh.Merkleize(indx) + return +} + +// GetTree ssz hashes the BeaconBlockBodyDeneb object +func (b *BeaconBlockBody) GetTree() (*ssz.Node, error) { + return ssz.ProofTree(b) +} + +// MarshalSSZ ssz marshals the ExecutionPayloadDeneb object +func (e *ExecutionPayload) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(e) +} + +// MarshalSSZTo ssz marshals the ExecutionPayloadDeneb object to a target array +func (e *ExecutionPayload) MarshalSSZTo(buf []byte) (dst []byte, err error) { + dst = buf + offset := int(528) + + // Field (0) 'ParentHash' + dst = append(dst, e.ParentHash[:]...) + + // Field (1) 'FeeRecipient' + dst = append(dst, e.FeeRecipient[:]...) + + // Field (2) 'StateRoot' + dst = append(dst, e.StateRoot[:]...) + + // Field (3) 'ReceiptsRoot' + dst = append(dst, e.ReceiptsRoot[:]...) + + // Field (4) 'LogsBloom' + dst = append(dst, e.LogsBloom[:]...) + + // Field (5) 'PrevRandao' + dst = append(dst, e.PrevRandao[:]...) + + // Field (6) 'BlockNumber' + dst = ssz.MarshalUint64(dst, e.BlockNumber) + + // Field (7) 'GasLimit' + dst = ssz.MarshalUint64(dst, e.GasLimit) + + // Field (8) 'GasUsed' + dst = ssz.MarshalUint64(dst, e.GasUsed) + + // Field (9) 'Timestamp' + dst = ssz.MarshalUint64(dst, e.Timestamp) + + // Offset (10) 'ExtraData' + dst = ssz.WriteOffset(dst, offset) + offset += len(e.ExtraData) + + // Field (11) 'BaseFeePerGas' + dst = append(dst, e.BaseFeePerGas[:]...) + + // Field (12) 'BlockHash' + dst = append(dst, e.BlockHash[:]...) + + // Offset (13) 'Transactions' + dst = ssz.WriteOffset(dst, offset) + for ii := 0; ii < len(e.Transactions); ii++ { + offset += 4 + offset += len(e.Transactions[ii]) + } + + // Offset (14) 'Withdrawals' + dst = ssz.WriteOffset(dst, offset) + + // Field (15) 'BlobGasUsed' + dst = ssz.MarshalUint64(dst, e.BlobGasUsed) + + // Field (16) 'ExcessBlobGas' + dst = ssz.MarshalUint64(dst, e.ExcessBlobGas) + + // Field (10) 'ExtraData' + if size := len(e.ExtraData); size > 32 { + err = ssz.ErrBytesLengthFn("ExecutionPayloadDeneb.ExtraData", size, 32) + return + } + dst = append(dst, e.ExtraData...) + + // Field (13) 'Transactions' + if size := len(e.Transactions); size > 1048576 { + err = ssz.ErrListTooBigFn("ExecutionPayloadDeneb.Transactions", size, 1048576) + return + } + { + offset = 4 * len(e.Transactions) + for ii := 0; ii < len(e.Transactions); ii++ { + dst = ssz.WriteOffset(dst, offset) + offset += len(e.Transactions[ii]) + } + } + for ii := 0; ii < len(e.Transactions); ii++ { + if size := len(e.Transactions[ii]); size > 1073741824 { + err = ssz.ErrBytesLengthFn("ExecutionPayloadDeneb.Transactions[ii]", size, 1073741824) + return + } + dst = append(dst, e.Transactions[ii]...) + } + + // Field (14) 'Withdrawals' + if size := len(e.Withdrawals); size > 16 { + err = ssz.ErrListTooBigFn("ExecutionPayloadDeneb.Withdrawals", size, 16) + return + } + for ii := 0; ii < len(e.Withdrawals); ii++ { + if dst, err = e.Withdrawals[ii].MarshalSSZTo(dst); err != nil { + return + } + } + + return +} + +// UnmarshalSSZ ssz unmarshals the ExecutionPayloadDeneb object +func (e *ExecutionPayload) UnmarshalSSZ(buf []byte) error { + var err error + size := uint64(len(buf)) + if size < 528 { + return ssz.ErrSize + } + + tail := buf + var o10, o13, o14 uint64 + + // Field (0) 'ParentHash' + copy(e.ParentHash[:], buf[0:32]) + + // Field (1) 'FeeRecipient' + copy(e.FeeRecipient[:], buf[32:52]) + + // Field (2) 'StateRoot' + copy(e.StateRoot[:], buf[52:84]) + + // Field (3) 'ReceiptsRoot' + copy(e.ReceiptsRoot[:], buf[84:116]) + + // Field (4) 'LogsBloom' + copy(e.LogsBloom[:], buf[116:372]) + + // Field (5) 'PrevRandao' + copy(e.PrevRandao[:], buf[372:404]) + + // Field (6) 'BlockNumber' + e.BlockNumber = ssz.UnmarshallUint64(buf[404:412]) + + // Field (7) 'GasLimit' + e.GasLimit = ssz.UnmarshallUint64(buf[412:420]) + + // Field (8) 'GasUsed' + e.GasUsed = ssz.UnmarshallUint64(buf[420:428]) + + // Field (9) 'Timestamp' + e.Timestamp = ssz.UnmarshallUint64(buf[428:436]) + + // Offset (10) 'ExtraData' + if o10 = ssz.ReadOffset(buf[436:440]); o10 > size { + return ssz.ErrOffset + } + + if o10 != 528 { + return ssz.ErrInvalidVariableOffset + } + + // Field (11) 'BaseFeePerGas' + copy(e.BaseFeePerGas[:], buf[440:472]) + + // Field (12) 'BlockHash' + copy(e.BlockHash[:], buf[472:504]) + + // Offset (13) 'Transactions' + if o13 = ssz.ReadOffset(buf[504:508]); o13 > size || o10 > o13 { + return ssz.ErrOffset + } + + // Offset (14) 'Withdrawals' + if o14 = ssz.ReadOffset(buf[508:512]); o14 > size || o13 > o14 { + return ssz.ErrOffset + } + + // Field (15) 'BlobGasUsed' + e.BlobGasUsed = ssz.UnmarshallUint64(buf[512:520]) + + // Field (16) 'ExcessBlobGas' + e.ExcessBlobGas = ssz.UnmarshallUint64(buf[520:528]) + + // Field (10) 'ExtraData' + { + buf = tail[o10:o13] + if len(buf) > 32 { + return ssz.ErrBytesLength + } + if cap(e.ExtraData) == 0 { + e.ExtraData = make([]byte, 0, len(buf)) + } + e.ExtraData = append(e.ExtraData, buf...) + } + + // Field (13) 'Transactions' + { + buf = tail[o13:o14] + num, err := ssz.DecodeDynamicLength(buf, 1048576) + if err != nil { + return err + } + e.Transactions = make([][]byte, num) + err = ssz.UnmarshalDynamic(buf, num, func(indx int, buf []byte) (err error) { + if len(buf) > 1073741824 { + return ssz.ErrBytesLength + } + if cap(e.Transactions[indx]) == 0 { + e.Transactions[indx] = make([]byte, 0, len(buf)) + } + e.Transactions[indx] = append(e.Transactions[indx], buf...) + return nil + }) + if err != nil { + return err + } + } + + // Field (14) 'Withdrawals' + { + buf = tail[o14:] + num, err := ssz.DivideInt2(len(buf), 44, 16) + if err != nil { + return err + } + e.Withdrawals = make([]*generic.Withdrawal, num) + for ii := 0; ii < num; ii++ { + if e.Withdrawals[ii] == nil { + e.Withdrawals[ii] = new(generic.Withdrawal) + } + if err = e.Withdrawals[ii].UnmarshalSSZ(buf[ii*44 : (ii+1)*44]); err != nil { + return err + } + } + } + return err +} + +// SizeSSZ returns the ssz encoded size in bytes for the ExecutionPayloadDeneb object +func (e *ExecutionPayload) SizeSSZ() (size int) { + size = 528 + + // Field (10) 'ExtraData' + size += len(e.ExtraData) + + // Field (13) 'Transactions' + for ii := 0; ii < len(e.Transactions); ii++ { + size += 4 + size += len(e.Transactions[ii]) + } + + // Field (14) 'Withdrawals' + size += len(e.Withdrawals) * 44 + + return +} + +// HashTreeRoot ssz hashes the ExecutionPayloadDeneb object +func (e *ExecutionPayload) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(e) +} + +// HashTreeRootWith ssz hashes the ExecutionPayloadDeneb object with a hasher +func (e *ExecutionPayload) HashTreeRootWith(hh ssz.HashWalker) (err error) { + indx := hh.Index() + + // Field (0) 'ParentHash' + hh.PutBytes(e.ParentHash[:]) + + // Field (1) 'FeeRecipient' + hh.PutBytes(e.FeeRecipient[:]) + + // Field (2) 'StateRoot' + hh.PutBytes(e.StateRoot[:]) + + // Field (3) 'ReceiptsRoot' + hh.PutBytes(e.ReceiptsRoot[:]) + + // Field (4) 'LogsBloom' + hh.PutBytes(e.LogsBloom[:]) + + // Field (5) 'PrevRandao' + hh.PutBytes(e.PrevRandao[:]) + + // Field (6) 'BlockNumber' + hh.PutUint64(e.BlockNumber) + + // Field (7) 'GasLimit' + hh.PutUint64(e.GasLimit) + + // Field (8) 'GasUsed' + hh.PutUint64(e.GasUsed) + + // Field (9) 'Timestamp' + hh.PutUint64(e.Timestamp) + + // Field (10) 'ExtraData' + { + elemIndx := hh.Index() + byteLen := uint64(len(e.ExtraData)) + if byteLen > 32 { + err = ssz.ErrIncorrectListSize + return + } + hh.Append(e.ExtraData) + hh.MerkleizeWithMixin(elemIndx, byteLen, (32+31)/32) + } + + // Field (11) 'BaseFeePerGas' + hh.PutBytes(e.BaseFeePerGas[:]) + + // Field (12) 'BlockHash' + hh.PutBytes(e.BlockHash[:]) + + // Field (13) 'Transactions' + { + subIndx := hh.Index() + num := uint64(len(e.Transactions)) + if num > 1048576 { + err = ssz.ErrIncorrectListSize + return + } + for _, elem := range e.Transactions { + { + elemIndx := hh.Index() + byteLen := uint64(len(elem)) + if byteLen > 1073741824 { + err = ssz.ErrIncorrectListSize + return + } + hh.AppendBytes32(elem) + hh.MerkleizeWithMixin(elemIndx, byteLen, (1073741824+31)/32) + } + } + hh.MerkleizeWithMixin(subIndx, num, 1048576) + } + + // Field (14) 'Withdrawals' + { + subIndx := hh.Index() + num := uint64(len(e.Withdrawals)) + if num > 16 { + err = ssz.ErrIncorrectListSize + return + } + for _, elem := range e.Withdrawals { + if err = elem.HashTreeRootWith(hh); err != nil { + return + } + } + hh.MerkleizeWithMixin(subIndx, num, 16) + } + + // Field (15) 'BlobGasUsed' + hh.PutUint64(e.BlobGasUsed) + + // Field (16) 'ExcessBlobGas' + hh.PutUint64(e.ExcessBlobGas) + + hh.Merkleize(indx) + return +} + +// GetTree ssz hashes the ExecutionPayloadDeneb object +func (e *ExecutionPayload) GetTree() (*ssz.Node, error) { + return ssz.ProofTree(e) +} diff --git a/shared/types/eth2/fork/deneb/state_deneb.go b/shared/types/eth2/fork/deneb/state_deneb.go new file mode 100644 index 000000000..f9924a173 --- /dev/null +++ b/shared/types/eth2/fork/deneb/state_deneb.go @@ -0,0 +1,282 @@ +package deneb + +import ( + "bytes" + "errors" + "fmt" + "reflect" + "sync/atomic" + + "github.com/rocket-pool/smartnode/shared/types/eth2/generic" + "github.com/rocket-pool/smartnode/shared/utils/math" +) + +const beaconStateChunkCeil uint64 = 32 + +// Taken from https://github.com/prysmaticlabs/prysm/blob/ac1717f1e44bd218b0bd3af0c4dec951c075f462/proto/prysm/v1alpha1/beacon_state.pb.go#L1574 +// Unexported fields stripped, as well as proto-related field tags. JSON and ssz-size tags are preserved, and nested types are replaced with local copies as well. +type BeaconState struct { + GenesisTime uint64 `json:"genesis_time"` + GenesisValidatorsRoot []byte `json:"genesis_validators_root" ssz-size:"32"` + Slot uint64 `json:"slot"` + Fork *generic.Fork `json:"fork"` + LatestBlockHeader *generic.BeaconBlockHeader `json:"latest_block_header"` + BlockRoots [8192][32]byte `json:"block_roots" ssz-size:"8192,32"` + StateRoots [8192][32]byte `json:"state_roots" ssz-size:"8192,32"` + HistoricalRoots [][]byte `json:"historical_roots" ssz-max:"16777216" ssz-size:"?,32"` + Eth1Data *generic.Eth1Data `json:"eth1_data"` + Eth1DataVotes []*generic.Eth1Data `json:"eth1_data_votes" ssz-max:"2048"` + Eth1DepositIndex uint64 `json:"eth1_deposit_index"` + Validators []*generic.Validator `json:"validators" ssz-max:"1099511627776"` + Balances []uint64 `json:"balances" ssz-max:"1099511627776"` + RandaoMixes [][]byte `json:"randao_mixes" ssz-size:"65536,32"` + Slashings []uint64 `json:"slashings" ssz-size:"8192"` + PreviousEpochParticipation []byte `json:"previous_epoch_participation" ssz-max:"1099511627776"` + CurrentEpochParticipation []byte `json:"current_epoch_participation" ssz-max:"1099511627776"` + JustificationBits [1]byte `json:"justification_bits" ssz-size:"1"` + PreviousJustifiedCheckpoint *generic.Checkpoint `json:"previous_justified_checkpoint"` + CurrentJustifiedCheckpoint *generic.Checkpoint `json:"current_justified_checkpoint"` + FinalizedCheckpoint *generic.Checkpoint `json:"finalized_checkpoint"` + InactivityScores []uint64 `json:"inactivity_scores" ssz-max:"1099511627776"` + CurrentSyncCommittee *generic.SyncCommittee `json:"current_sync_committee"` + NextSyncCommittee *generic.SyncCommittee `json:"next_sync_committee"` + LatestExecutionPayloadHeader *generic.ExecutionPayloadHeader `json:"latest_execution_payload_header"` + NextWithdrawalIndex uint64 `json:"next_withdrawal_index"` + NextWithdrawalValidatorIndex uint64 `json:"next_withdrawal_validator_index"` + HistoricalSummaries []*generic.HistoricalSummary `json:"historical_summaries" ssz-max:"16777216"` +} + +var beaconStateChunkSize atomic.Uint64 + +func getStateChunkSize() uint64 { + // Use a static value to avoid multiple reflection calls + storedChunkSize := beaconStateChunkSize.Load() + if storedChunkSize == 0 { + s := reflect.TypeOf(BeaconState{}).NumField() + beaconStateChunkSize.Store(uint64(s)) + storedChunkSize = uint64(s) + } + return storedChunkSize +} + +func GetGeneralizedIndexForValidators() uint64 { + // There's 28 fields, so rounding up to the next power of two is 32, a left-aligned node + // BeaconStateValidatorsIndex is the 11th field, so its generalized index is 32 + 11 = 43 + return math.GetPowerOfTwoCeil(getStateChunkSize()) + generic.BeaconStateValidatorsIndex +} + +func (state *BeaconState) validatorStateProof(index uint64) ([][]byte, error) { + + // Convert the state to a proof tree + root, err := state.GetTree() + if err != nil { + return nil, fmt.Errorf("could not get state tree: %w", err) + } + + // Find the validator's generalized index + generalizedIndex := generic.GetGeneralizedIndexForValidator(index, GetGeneralizedIndexForValidators()) + + // Grab the proof for that index + proof, err := root.Prove(int(generalizedIndex)) + if err != nil { + return nil, fmt.Errorf("could not get proof for validator: %w", err) + } + + // Sanity check that the proof leaf matches the expected validator + validatorHashTreeRoot, err := state.Validators[index].HashTreeRoot() + if err != nil { + return nil, fmt.Errorf("could not get hash tree root for validator: %w", err) + } + if !bytes.Equal(proof.Leaf, validatorHashTreeRoot[:]) { + return nil, fmt.Errorf("proof leaf does not match expected validator") + } + + return proof.Hashes, nil + +} + +// ValidatorWithdrawableEpochProof computes the merkle proof for a validator's withdrawable epoch +// at a specific index in the validator registry. +func (state *BeaconState) ValidatorWithdrawableEpochProof(index uint64) ([][]byte, error) { + + if index >= uint64(len(state.Validators)) { + return nil, errors.New("validator index out of bounds") + } + + // Get the validator's withdrawable epoch proof + withdrawableEpochProof, err := state.Validators[index].ValidatorWithdrawableEpochProof() + if err != nil { + return nil, fmt.Errorf("could not get validator withdrawable epoch proof: %w", err) + } + + stateProof, err := state.validatorStateProof(index) + if err != nil { + return nil, fmt.Errorf("could not get validator state proof: %w", err) + } + + // The EL proves against BeaconBlockHeader root, so we need to merge the state proof with that. + generalizedIndex := generic.BeaconBlockHeaderStateRootGeneralizedIndex + root, err := state.LatestBlockHeader.GetTree() + if err != nil { + return nil, fmt.Errorf("could not get block header tree: %w", err) + } + blockHeaderProof, err := root.Prove(int(generalizedIndex)) + if err != nil { + return nil, fmt.Errorf("could not get proof for block header: %w", err) + } + + out := append(withdrawableEpochProof, stateProof...) + out = append(out, blockHeaderProof.Hashes...) + + return out, nil +} + +func (state *BeaconState) blockHeaderToStateProof(blockHeader *generic.BeaconBlockHeader) ([][]byte, error) { + generalizedIndex := generic.BeaconBlockHeaderStateRootGeneralizedIndex + root, err := blockHeader.GetTree() + if err != nil { + return nil, fmt.Errorf("could not get block header tree: %w", err) + } + blockHeaderProof, err := root.Prove(int(generalizedIndex)) + if err != nil { + return nil, fmt.Errorf("could not get proof for block header: %w", err) + } + return blockHeaderProof.Hashes, nil +} + +// ValidatorCredentialsProof computes the merkle proof for a validator's credentials +// at a specific index in the validator registry. +func (state *BeaconState) ValidatorCredentialsProof(index uint64) ([][]byte, error) { + + if index >= uint64(len(state.Validators)) { + return nil, errors.New("validator index out of bounds") + } + + // Get the validator's credentials proof + credentialsProof, err := state.Validators[index].ValidatorCredentialsPubkeyProof() + if err != nil { + return nil, fmt.Errorf("could not get validator credentials proof: %w", err) + } + + stateProof, err := state.validatorStateProof(index) + if err != nil { + return nil, fmt.Errorf("could not get validator state proof: %w", err) + } + + // The EL proves against BeaconBlockHeader root, so we need to merge the state proof with that. + blockHeaderProof, err := state.blockHeaderToStateProof(state.LatestBlockHeader) + if err != nil { + return nil, fmt.Errorf("could not get block header proof: %w", err) + } + + out := append(credentialsProof, stateProof...) + out = append(out, blockHeaderProof...) + + return out, nil +} + +func (state *BeaconState) HistoricalSummaryProof(slot uint64) ([][]byte, error) { + isHistorical := slot+generic.SlotsPerHistoricalRoot <= state.Slot + if !isHistorical { + return nil, fmt.Errorf("slot %d is less than %d slots in the past from the state at slot %d, you must build a proof from the block_roots field instead", slot, generic.SlotsPerHistoricalRoot, state.Slot) + } + tree, err := state.GetTree() + if err != nil { + return nil, fmt.Errorf("could not get state tree: %w", err) + } + + // Navigate to the historical_summaries + gid := uint64(1) + gid = gid*beaconStateChunkCeil + generic.BeaconStateHistoricalSummariesFieldIndex + // Navigate into the historical summaries vector. + arrayIndex := (slot / generic.SlotsPerHistoricalRoot) + gid = gid*2*generic.BeaconStateHistoricalSummariesMaxLength + arrayIndex + + proof, err := tree.Prove(int(gid)) + if err != nil { + return nil, fmt.Errorf("could not get proof for historical block root: %w", err) + } + + // The EL proves against BeaconBlockHeader root, so we need to merge the state proof with that. + blockHeaderProof, err := state.blockHeaderToStateProof(state.LatestBlockHeader) + if err != nil { + return nil, fmt.Errorf("could not get block header proof: %w", err) + } + return append(proof.Hashes, blockHeaderProof...), nil +} + +func (state *BeaconState) HistoricalSummaryBlockRootProof(slot int) ([][]byte, error) { + // If the state isn't aligned at the end of an 8192 slot era, throw an error + if state.Slot%generic.SlotsPerHistoricalRoot != generic.SlotsPerHistoricalRoot-1 { + return nil, fmt.Errorf("state is not aligned at the end of an 8192 slot era") + } + + if slot < int(state.Slot)-int(generic.SlotsPerHistoricalRoot)-1 || slot+int(generic.SlotsPerHistoricalRoot)-1 >= int(state.Slot) { + return nil, fmt.Errorf("slot %d is out of range for historical summary proof", slot) + } + + hsls := generic.HistoricalSummaryLists{ + BlockRoots: state.BlockRoots, + StateRoots: state.StateRoots, + } + + idx := slot % int(generic.SlotsPerHistoricalRoot) + tree, err := hsls.GetTree() + if err != nil { + return nil, fmt.Errorf("could not get historical summary lists tree: %w", err) + } + + gid := uint64(1) + gid = gid * 2 // Now at block_roots + gid = gid * generic.SlotsPerHistoricalRoot // Now at the first block_root + gid = gid + uint64(idx) // Now at the correct block_root + + proof, err := tree.Prove(int(gid)) + if err != nil { + return nil, fmt.Errorf("could not get proof for historical summary: %w", err) + } + + return proof.Hashes, nil +} + +func (state *BeaconState) BlockRootProof(slot uint64) ([][]byte, error) { + isHistorical := slot+generic.SlotsPerHistoricalRoot <= state.Slot + if isHistorical { + return nil, fmt.Errorf("slot %d is more than %d slots in the past from the state at slot %d, you must build a proof from the historical_summaries instead", slot, generic.SlotsPerHistoricalRoot, state.Slot) + } + + tree, err := state.GetTree() + if err != nil { + return nil, fmt.Errorf("could not get state tree: %w", err) + } + + gid := uint64(1) + + // Navigate to the block_roots + gid = gid*beaconStateChunkCeil + generic.BeaconStateBlockRootsFieldIndex + + // We're now at the block_roots vector, which is the root of a slotsPerHistoricalRoot slots vector. + // The index we care about is given by slot % slotsPerHistoricalRoot. + gid = gid*generic.BeaconStateBlockRootsMaxLength + (slot % generic.SlotsPerHistoricalRoot) + + proof, err := tree.Prove(int(gid)) + if err != nil { + return nil, fmt.Errorf("could not get proof for block root: %w", err) + } + + // Finally, prove from the block header to the state root. + blockHeaderProof, err := state.blockHeaderToStateProof(state.LatestBlockHeader) + if err != nil { + return nil, fmt.Errorf("could not get block header proof: %w", err) + } + + return append(proof.Hashes, blockHeaderProof...), nil +} + +func (state *BeaconState) GetValidators() []*generic.Validator { + return state.Validators +} + +func (state *BeaconState) GetSlot() uint64 { + return state.Slot +} diff --git a/shared/types/eth2/fork/deneb/state_deneb_encoding.go b/shared/types/eth2/fork/deneb/state_deneb_encoding.go new file mode 100644 index 000000000..5d6a3adaa --- /dev/null +++ b/shared/types/eth2/fork/deneb/state_deneb_encoding.go @@ -0,0 +1,889 @@ +// Code generated by fastssz. DO NOT EDIT. +// Hash: 9015b678c5cb9d652ed10fdd64e476014863a336c0fcf7c8ee3a36944565919b +// Version: 0.1.3 +package deneb + +import ( + ssz "github.com/ferranbt/fastssz" + "github.com/rocket-pool/smartnode/shared/types/eth2/generic" +) + +// MarshalSSZ ssz marshals the BeaconStateDeneb object +func (b *BeaconState) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(b) +} + +// MarshalSSZTo ssz marshals the BeaconStateDeneb object to a target array +func (b *BeaconState) MarshalSSZTo(buf []byte) (dst []byte, err error) { + dst = buf + offset := int(2736653) + + // Field (0) 'GenesisTime' + dst = ssz.MarshalUint64(dst, b.GenesisTime) + + // Field (1) 'GenesisValidatorsRoot' + if size := len(b.GenesisValidatorsRoot); size != 32 { + err = ssz.ErrBytesLengthFn("BeaconStateDeneb.GenesisValidatorsRoot", size, 32) + return + } + dst = append(dst, b.GenesisValidatorsRoot...) + + // Field (2) 'Slot' + dst = ssz.MarshalUint64(dst, b.Slot) + + // Field (3) 'Fork' + if b.Fork == nil { + b.Fork = new(generic.Fork) + } + if dst, err = b.Fork.MarshalSSZTo(dst); err != nil { + return + } + + // Field (4) 'LatestBlockHeader' + if b.LatestBlockHeader == nil { + b.LatestBlockHeader = new(generic.BeaconBlockHeader) + } + if dst, err = b.LatestBlockHeader.MarshalSSZTo(dst); err != nil { + return + } + + // Field (5) 'BlockRoots' + for ii := 0; ii < 8192; ii++ { + dst = append(dst, b.BlockRoots[ii][:]...) + } + + // Field (6) 'StateRoots' + for ii := 0; ii < 8192; ii++ { + dst = append(dst, b.StateRoots[ii][:]...) + } + + // Offset (7) 'HistoricalRoots' + dst = ssz.WriteOffset(dst, offset) + offset += len(b.HistoricalRoots) * 32 + + // Field (8) 'Eth1Data' + if b.Eth1Data == nil { + b.Eth1Data = new(generic.Eth1Data) + } + if dst, err = b.Eth1Data.MarshalSSZTo(dst); err != nil { + return + } + + // Offset (9) 'Eth1DataVotes' + dst = ssz.WriteOffset(dst, offset) + offset += len(b.Eth1DataVotes) * 72 + + // Field (10) 'Eth1DepositIndex' + dst = ssz.MarshalUint64(dst, b.Eth1DepositIndex) + + // Offset (11) 'Validators' + dst = ssz.WriteOffset(dst, offset) + offset += len(b.Validators) * 121 + + // Offset (12) 'Balances' + dst = ssz.WriteOffset(dst, offset) + offset += len(b.Balances) * 8 + + // Field (13) 'RandaoMixes' + if size := len(b.RandaoMixes); size != 65536 { + err = ssz.ErrVectorLengthFn("BeaconStateDeneb.RandaoMixes", size, 65536) + return + } + for ii := 0; ii < 65536; ii++ { + if size := len(b.RandaoMixes[ii]); size != 32 { + err = ssz.ErrBytesLengthFn("BeaconStateDeneb.RandaoMixes[ii]", size, 32) + return + } + dst = append(dst, b.RandaoMixes[ii]...) + } + + // Field (14) 'Slashings' + if size := len(b.Slashings); size != 8192 { + err = ssz.ErrVectorLengthFn("BeaconStateDeneb.Slashings", size, 8192) + return + } + for ii := 0; ii < 8192; ii++ { + dst = ssz.MarshalUint64(dst, b.Slashings[ii]) + } + + // Offset (15) 'PreviousEpochParticipation' + dst = ssz.WriteOffset(dst, offset) + offset += len(b.PreviousEpochParticipation) + + // Offset (16) 'CurrentEpochParticipation' + dst = ssz.WriteOffset(dst, offset) + offset += len(b.CurrentEpochParticipation) + + // Field (17) 'JustificationBits' + dst = append(dst, b.JustificationBits[:]...) + + // Field (18) 'PreviousJustifiedCheckpoint' + if b.PreviousJustifiedCheckpoint == nil { + b.PreviousJustifiedCheckpoint = new(generic.Checkpoint) + } + if dst, err = b.PreviousJustifiedCheckpoint.MarshalSSZTo(dst); err != nil { + return + } + + // Field (19) 'CurrentJustifiedCheckpoint' + if b.CurrentJustifiedCheckpoint == nil { + b.CurrentJustifiedCheckpoint = new(generic.Checkpoint) + } + if dst, err = b.CurrentJustifiedCheckpoint.MarshalSSZTo(dst); err != nil { + return + } + + // Field (20) 'FinalizedCheckpoint' + if b.FinalizedCheckpoint == nil { + b.FinalizedCheckpoint = new(generic.Checkpoint) + } + if dst, err = b.FinalizedCheckpoint.MarshalSSZTo(dst); err != nil { + return + } + + // Offset (21) 'InactivityScores' + dst = ssz.WriteOffset(dst, offset) + offset += len(b.InactivityScores) * 8 + + // Field (22) 'CurrentSyncCommittee' + if b.CurrentSyncCommittee == nil { + b.CurrentSyncCommittee = new(generic.SyncCommittee) + } + if dst, err = b.CurrentSyncCommittee.MarshalSSZTo(dst); err != nil { + return + } + + // Field (23) 'NextSyncCommittee' + if b.NextSyncCommittee == nil { + b.NextSyncCommittee = new(generic.SyncCommittee) + } + if dst, err = b.NextSyncCommittee.MarshalSSZTo(dst); err != nil { + return + } + + // Offset (24) 'LatestExecutionPayloadHeader' + dst = ssz.WriteOffset(dst, offset) + if b.LatestExecutionPayloadHeader == nil { + b.LatestExecutionPayloadHeader = new(generic.ExecutionPayloadHeader) + } + offset += b.LatestExecutionPayloadHeader.SizeSSZ() + + // Field (25) 'NextWithdrawalIndex' + dst = ssz.MarshalUint64(dst, b.NextWithdrawalIndex) + + // Field (26) 'NextWithdrawalValidatorIndex' + dst = ssz.MarshalUint64(dst, b.NextWithdrawalValidatorIndex) + + // Offset (27) 'HistoricalSummaries' + dst = ssz.WriteOffset(dst, offset) + + // Field (7) 'HistoricalRoots' + if size := len(b.HistoricalRoots); size > 16777216 { + err = ssz.ErrListTooBigFn("BeaconStateDeneb.HistoricalRoots", size, 16777216) + return + } + for ii := 0; ii < len(b.HistoricalRoots); ii++ { + if size := len(b.HistoricalRoots[ii]); size != 32 { + err = ssz.ErrBytesLengthFn("BeaconStateDeneb.HistoricalRoots[ii]", size, 32) + return + } + dst = append(dst, b.HistoricalRoots[ii]...) + } + + // Field (9) 'Eth1DataVotes' + if size := len(b.Eth1DataVotes); size > 2048 { + err = ssz.ErrListTooBigFn("BeaconStateDeneb.Eth1DataVotes", size, 2048) + return + } + for ii := 0; ii < len(b.Eth1DataVotes); ii++ { + if dst, err = b.Eth1DataVotes[ii].MarshalSSZTo(dst); err != nil { + return + } + } + + // Field (11) 'Validators' + if size := len(b.Validators); size > 1099511627776 { + err = ssz.ErrListTooBigFn("BeaconStateDeneb.Validators", size, 1099511627776) + return + } + for ii := 0; ii < len(b.Validators); ii++ { + if dst, err = b.Validators[ii].MarshalSSZTo(dst); err != nil { + return + } + } + + // Field (12) 'Balances' + if size := len(b.Balances); size > 1099511627776 { + err = ssz.ErrListTooBigFn("BeaconStateDeneb.Balances", size, 1099511627776) + return + } + for ii := 0; ii < len(b.Balances); ii++ { + dst = ssz.MarshalUint64(dst, b.Balances[ii]) + } + + // Field (15) 'PreviousEpochParticipation' + if size := len(b.PreviousEpochParticipation); size > 1099511627776 { + err = ssz.ErrBytesLengthFn("BeaconStateDeneb.PreviousEpochParticipation", size, 1099511627776) + return + } + dst = append(dst, b.PreviousEpochParticipation...) + + // Field (16) 'CurrentEpochParticipation' + if size := len(b.CurrentEpochParticipation); size > 1099511627776 { + err = ssz.ErrBytesLengthFn("BeaconStateDeneb.CurrentEpochParticipation", size, 1099511627776) + return + } + dst = append(dst, b.CurrentEpochParticipation...) + + // Field (21) 'InactivityScores' + if size := len(b.InactivityScores); size > 1099511627776 { + err = ssz.ErrListTooBigFn("BeaconStateDeneb.InactivityScores", size, 1099511627776) + return + } + for ii := 0; ii < len(b.InactivityScores); ii++ { + dst = ssz.MarshalUint64(dst, b.InactivityScores[ii]) + } + + // Field (24) 'LatestExecutionPayloadHeader' + if dst, err = b.LatestExecutionPayloadHeader.MarshalSSZTo(dst); err != nil { + return + } + + // Field (27) 'HistoricalSummaries' + if size := len(b.HistoricalSummaries); size > 16777216 { + err = ssz.ErrListTooBigFn("BeaconStateDeneb.HistoricalSummaries", size, 16777216) + return + } + for ii := 0; ii < len(b.HistoricalSummaries); ii++ { + if dst, err = b.HistoricalSummaries[ii].MarshalSSZTo(dst); err != nil { + return + } + } + + return +} + +// UnmarshalSSZ ssz unmarshals the BeaconStateDeneb object +func (b *BeaconState) UnmarshalSSZ(buf []byte) error { + var err error + size := uint64(len(buf)) + if size < 2736653 { + return ssz.ErrSize + } + + tail := buf + var o7, o9, o11, o12, o15, o16, o21, o24, o27 uint64 + + // Field (0) 'GenesisTime' + b.GenesisTime = ssz.UnmarshallUint64(buf[0:8]) + + // Field (1) 'GenesisValidatorsRoot' + if cap(b.GenesisValidatorsRoot) == 0 { + b.GenesisValidatorsRoot = make([]byte, 0, len(buf[8:40])) + } + b.GenesisValidatorsRoot = append(b.GenesisValidatorsRoot, buf[8:40]...) + + // Field (2) 'Slot' + b.Slot = ssz.UnmarshallUint64(buf[40:48]) + + // Field (3) 'Fork' + if b.Fork == nil { + b.Fork = new(generic.Fork) + } + if err = b.Fork.UnmarshalSSZ(buf[48:64]); err != nil { + return err + } + + // Field (4) 'LatestBlockHeader' + if b.LatestBlockHeader == nil { + b.LatestBlockHeader = new(generic.BeaconBlockHeader) + } + if err = b.LatestBlockHeader.UnmarshalSSZ(buf[64:176]); err != nil { + return err + } + + // Field (5) 'BlockRoots' + + for ii := 0; ii < 8192; ii++ { + copy(b.BlockRoots[ii][:], buf[176:262320][ii*32:(ii+1)*32]) + } + + // Field (6) 'StateRoots' + + for ii := 0; ii < 8192; ii++ { + copy(b.StateRoots[ii][:], buf[262320:524464][ii*32:(ii+1)*32]) + } + + // Offset (7) 'HistoricalRoots' + if o7 = ssz.ReadOffset(buf[524464:524468]); o7 > size { + return ssz.ErrOffset + } + + if o7 != 2736653 { + return ssz.ErrInvalidVariableOffset + } + + // Field (8) 'Eth1Data' + if b.Eth1Data == nil { + b.Eth1Data = new(generic.Eth1Data) + } + if err = b.Eth1Data.UnmarshalSSZ(buf[524468:524540]); err != nil { + return err + } + + // Offset (9) 'Eth1DataVotes' + if o9 = ssz.ReadOffset(buf[524540:524544]); o9 > size || o7 > o9 { + return ssz.ErrOffset + } + + // Field (10) 'Eth1DepositIndex' + b.Eth1DepositIndex = ssz.UnmarshallUint64(buf[524544:524552]) + + // Offset (11) 'Validators' + if o11 = ssz.ReadOffset(buf[524552:524556]); o11 > size || o9 > o11 { + return ssz.ErrOffset + } + + // Offset (12) 'Balances' + if o12 = ssz.ReadOffset(buf[524556:524560]); o12 > size || o11 > o12 { + return ssz.ErrOffset + } + + // Field (13) 'RandaoMixes' + b.RandaoMixes = make([][]byte, 65536) + for ii := 0; ii < 65536; ii++ { + if cap(b.RandaoMixes[ii]) == 0 { + b.RandaoMixes[ii] = make([]byte, 0, len(buf[524560:2621712][ii*32:(ii+1)*32])) + } + b.RandaoMixes[ii] = append(b.RandaoMixes[ii], buf[524560:2621712][ii*32:(ii+1)*32]...) + } + + // Field (14) 'Slashings' + b.Slashings = ssz.ExtendUint64(b.Slashings, 8192) + for ii := 0; ii < 8192; ii++ { + b.Slashings[ii] = ssz.UnmarshallUint64(buf[2621712:2687248][ii*8 : (ii+1)*8]) + } + + // Offset (15) 'PreviousEpochParticipation' + if o15 = ssz.ReadOffset(buf[2687248:2687252]); o15 > size || o12 > o15 { + return ssz.ErrOffset + } + + // Offset (16) 'CurrentEpochParticipation' + if o16 = ssz.ReadOffset(buf[2687252:2687256]); o16 > size || o15 > o16 { + return ssz.ErrOffset + } + + // Field (17) 'JustificationBits' + copy(b.JustificationBits[:], buf[2687256:2687257]) + + // Field (18) 'PreviousJustifiedCheckpoint' + if b.PreviousJustifiedCheckpoint == nil { + b.PreviousJustifiedCheckpoint = new(generic.Checkpoint) + } + if err = b.PreviousJustifiedCheckpoint.UnmarshalSSZ(buf[2687257:2687297]); err != nil { + return err + } + + // Field (19) 'CurrentJustifiedCheckpoint' + if b.CurrentJustifiedCheckpoint == nil { + b.CurrentJustifiedCheckpoint = new(generic.Checkpoint) + } + if err = b.CurrentJustifiedCheckpoint.UnmarshalSSZ(buf[2687297:2687337]); err != nil { + return err + } + + // Field (20) 'FinalizedCheckpoint' + if b.FinalizedCheckpoint == nil { + b.FinalizedCheckpoint = new(generic.Checkpoint) + } + if err = b.FinalizedCheckpoint.UnmarshalSSZ(buf[2687337:2687377]); err != nil { + return err + } + + // Offset (21) 'InactivityScores' + if o21 = ssz.ReadOffset(buf[2687377:2687381]); o21 > size || o16 > o21 { + return ssz.ErrOffset + } + + // Field (22) 'CurrentSyncCommittee' + if b.CurrentSyncCommittee == nil { + b.CurrentSyncCommittee = new(generic.SyncCommittee) + } + if err = b.CurrentSyncCommittee.UnmarshalSSZ(buf[2687381:2712005]); err != nil { + return err + } + + // Field (23) 'NextSyncCommittee' + if b.NextSyncCommittee == nil { + b.NextSyncCommittee = new(generic.SyncCommittee) + } + if err = b.NextSyncCommittee.UnmarshalSSZ(buf[2712005:2736629]); err != nil { + return err + } + + // Offset (24) 'LatestExecutionPayloadHeader' + if o24 = ssz.ReadOffset(buf[2736629:2736633]); o24 > size || o21 > o24 { + return ssz.ErrOffset + } + + // Field (25) 'NextWithdrawalIndex' + b.NextWithdrawalIndex = ssz.UnmarshallUint64(buf[2736633:2736641]) + + // Field (26) 'NextWithdrawalValidatorIndex' + b.NextWithdrawalValidatorIndex = ssz.UnmarshallUint64(buf[2736641:2736649]) + + // Offset (27) 'HistoricalSummaries' + if o27 = ssz.ReadOffset(buf[2736649:2736653]); o27 > size || o24 > o27 { + return ssz.ErrOffset + } + + // Field (7) 'HistoricalRoots' + { + buf = tail[o7:o9] + num, err := ssz.DivideInt2(len(buf), 32, 16777216) + if err != nil { + return err + } + b.HistoricalRoots = make([][]byte, num) + for ii := 0; ii < num; ii++ { + if cap(b.HistoricalRoots[ii]) == 0 { + b.HistoricalRoots[ii] = make([]byte, 0, len(buf[ii*32:(ii+1)*32])) + } + b.HistoricalRoots[ii] = append(b.HistoricalRoots[ii], buf[ii*32:(ii+1)*32]...) + } + } + + // Field (9) 'Eth1DataVotes' + { + buf = tail[o9:o11] + num, err := ssz.DivideInt2(len(buf), 72, 2048) + if err != nil { + return err + } + b.Eth1DataVotes = make([]*generic.Eth1Data, num) + for ii := 0; ii < num; ii++ { + if b.Eth1DataVotes[ii] == nil { + b.Eth1DataVotes[ii] = new(generic.Eth1Data) + } + if err = b.Eth1DataVotes[ii].UnmarshalSSZ(buf[ii*72 : (ii+1)*72]); err != nil { + return err + } + } + } + + // Field (11) 'Validators' + { + buf = tail[o11:o12] + num, err := ssz.DivideInt2(len(buf), 121, 1099511627776) + if err != nil { + return err + } + b.Validators = make([]*generic.Validator, num) + for ii := 0; ii < num; ii++ { + if b.Validators[ii] == nil { + b.Validators[ii] = new(generic.Validator) + } + if err = b.Validators[ii].UnmarshalSSZ(buf[ii*121 : (ii+1)*121]); err != nil { + return err + } + } + } + + // Field (12) 'Balances' + { + buf = tail[o12:o15] + num, err := ssz.DivideInt2(len(buf), 8, 1099511627776) + if err != nil { + return err + } + b.Balances = ssz.ExtendUint64(b.Balances, num) + for ii := 0; ii < num; ii++ { + b.Balances[ii] = ssz.UnmarshallUint64(buf[ii*8 : (ii+1)*8]) + } + } + + // Field (15) 'PreviousEpochParticipation' + { + buf = tail[o15:o16] + if len(buf) > 1099511627776 { + return ssz.ErrBytesLength + } + if cap(b.PreviousEpochParticipation) == 0 { + b.PreviousEpochParticipation = make([]byte, 0, len(buf)) + } + b.PreviousEpochParticipation = append(b.PreviousEpochParticipation, buf...) + } + + // Field (16) 'CurrentEpochParticipation' + { + buf = tail[o16:o21] + if len(buf) > 1099511627776 { + return ssz.ErrBytesLength + } + if cap(b.CurrentEpochParticipation) == 0 { + b.CurrentEpochParticipation = make([]byte, 0, len(buf)) + } + b.CurrentEpochParticipation = append(b.CurrentEpochParticipation, buf...) + } + + // Field (21) 'InactivityScores' + { + buf = tail[o21:o24] + num, err := ssz.DivideInt2(len(buf), 8, 1099511627776) + if err != nil { + return err + } + b.InactivityScores = ssz.ExtendUint64(b.InactivityScores, num) + for ii := 0; ii < num; ii++ { + b.InactivityScores[ii] = ssz.UnmarshallUint64(buf[ii*8 : (ii+1)*8]) + } + } + + // Field (24) 'LatestExecutionPayloadHeader' + { + buf = tail[o24:o27] + if b.LatestExecutionPayloadHeader == nil { + b.LatestExecutionPayloadHeader = new(generic.ExecutionPayloadHeader) + } + if err = b.LatestExecutionPayloadHeader.UnmarshalSSZ(buf); err != nil { + return err + } + } + + // Field (27) 'HistoricalSummaries' + { + buf = tail[o27:] + num, err := ssz.DivideInt2(len(buf), 64, 16777216) + if err != nil { + return err + } + b.HistoricalSummaries = make([]*generic.HistoricalSummary, num) + for ii := 0; ii < num; ii++ { + if b.HistoricalSummaries[ii] == nil { + b.HistoricalSummaries[ii] = new(generic.HistoricalSummary) + } + if err = b.HistoricalSummaries[ii].UnmarshalSSZ(buf[ii*64 : (ii+1)*64]); err != nil { + return err + } + } + } + return err +} + +// SizeSSZ returns the ssz encoded size in bytes for the BeaconStateDeneb object +func (b *BeaconState) SizeSSZ() (size int) { + size = 2736653 + + // Field (7) 'HistoricalRoots' + size += len(b.HistoricalRoots) * 32 + + // Field (9) 'Eth1DataVotes' + size += len(b.Eth1DataVotes) * 72 + + // Field (11) 'Validators' + size += len(b.Validators) * 121 + + // Field (12) 'Balances' + size += len(b.Balances) * 8 + + // Field (15) 'PreviousEpochParticipation' + size += len(b.PreviousEpochParticipation) + + // Field (16) 'CurrentEpochParticipation' + size += len(b.CurrentEpochParticipation) + + // Field (21) 'InactivityScores' + size += len(b.InactivityScores) * 8 + + // Field (24) 'LatestExecutionPayloadHeader' + if b.LatestExecutionPayloadHeader == nil { + b.LatestExecutionPayloadHeader = new(generic.ExecutionPayloadHeader) + } + size += b.LatestExecutionPayloadHeader.SizeSSZ() + + // Field (27) 'HistoricalSummaries' + size += len(b.HistoricalSummaries) * 64 + + return +} + +// HashTreeRoot ssz hashes the BeaconStateDeneb object +func (b *BeaconState) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(b) +} + +// HashTreeRootWith ssz hashes the BeaconStateDeneb object with a hasher +func (b *BeaconState) HashTreeRootWith(hh ssz.HashWalker) (err error) { + indx := hh.Index() + + // Field (0) 'GenesisTime' + hh.PutUint64(b.GenesisTime) + + // Field (1) 'GenesisValidatorsRoot' + if size := len(b.GenesisValidatorsRoot); size != 32 { + err = ssz.ErrBytesLengthFn("BeaconStateDeneb.GenesisValidatorsRoot", size, 32) + return + } + hh.PutBytes(b.GenesisValidatorsRoot) + + // Field (2) 'Slot' + hh.PutUint64(b.Slot) + + // Field (3) 'Fork' + if b.Fork == nil { + b.Fork = new(generic.Fork) + } + if err = b.Fork.HashTreeRootWith(hh); err != nil { + return + } + + // Field (4) 'LatestBlockHeader' + if b.LatestBlockHeader == nil { + b.LatestBlockHeader = new(generic.BeaconBlockHeader) + } + if err = b.LatestBlockHeader.HashTreeRootWith(hh); err != nil { + return + } + + // Field (5) 'BlockRoots' + { + subIndx := hh.Index() + for _, i := range b.BlockRoots { + hh.Append(i[:]) + } + hh.Merkleize(subIndx) + } + + // Field (6) 'StateRoots' + { + subIndx := hh.Index() + for _, i := range b.StateRoots { + hh.Append(i[:]) + } + hh.Merkleize(subIndx) + } + + // Field (7) 'HistoricalRoots' + { + if size := len(b.HistoricalRoots); size > 16777216 { + err = ssz.ErrListTooBigFn("BeaconStateDeneb.HistoricalRoots", size, 16777216) + return + } + subIndx := hh.Index() + for _, i := range b.HistoricalRoots { + if len(i) != 32 { + err = ssz.ErrBytesLength + return + } + hh.Append(i) + } + numItems := uint64(len(b.HistoricalRoots)) + hh.MerkleizeWithMixin(subIndx, numItems, 16777216) + } + + // Field (8) 'Eth1Data' + if b.Eth1Data == nil { + b.Eth1Data = new(generic.Eth1Data) + } + if err = b.Eth1Data.HashTreeRootWith(hh); err != nil { + return + } + + // Field (9) 'Eth1DataVotes' + { + subIndx := hh.Index() + num := uint64(len(b.Eth1DataVotes)) + if num > 2048 { + err = ssz.ErrIncorrectListSize + return + } + for _, elem := range b.Eth1DataVotes { + if err = elem.HashTreeRootWith(hh); err != nil { + return + } + } + hh.MerkleizeWithMixin(subIndx, num, 2048) + } + + // Field (10) 'Eth1DepositIndex' + hh.PutUint64(b.Eth1DepositIndex) + + // Field (11) 'Validators' + { + subIndx := hh.Index() + num := uint64(len(b.Validators)) + if num > 1099511627776 { + err = ssz.ErrIncorrectListSize + return + } + for _, elem := range b.Validators { + if err = elem.HashTreeRootWith(hh); err != nil { + return + } + } + hh.MerkleizeWithMixin(subIndx, num, 1099511627776) + } + + // Field (12) 'Balances' + { + if size := len(b.Balances); size > 1099511627776 { + err = ssz.ErrListTooBigFn("BeaconStateDeneb.Balances", size, 1099511627776) + return + } + subIndx := hh.Index() + for _, i := range b.Balances { + hh.AppendUint64(i) + } + hh.FillUpTo32() + numItems := uint64(len(b.Balances)) + hh.MerkleizeWithMixin(subIndx, numItems, ssz.CalculateLimit(1099511627776, numItems, 8)) + } + + // Field (13) 'RandaoMixes' + { + if size := len(b.RandaoMixes); size != 65536 { + err = ssz.ErrVectorLengthFn("BeaconStateDeneb.RandaoMixes", size, 65536) + return + } + subIndx := hh.Index() + for _, i := range b.RandaoMixes { + if len(i) != 32 { + err = ssz.ErrBytesLength + return + } + hh.Append(i) + } + hh.Merkleize(subIndx) + } + + // Field (14) 'Slashings' + { + if size := len(b.Slashings); size != 8192 { + err = ssz.ErrVectorLengthFn("BeaconStateDeneb.Slashings", size, 8192) + return + } + subIndx := hh.Index() + for _, i := range b.Slashings { + hh.AppendUint64(i) + } + hh.Merkleize(subIndx) + } + + // Field (15) 'PreviousEpochParticipation' + { + elemIndx := hh.Index() + byteLen := uint64(len(b.PreviousEpochParticipation)) + if byteLen > 1099511627776 { + err = ssz.ErrIncorrectListSize + return + } + hh.Append(b.PreviousEpochParticipation) + hh.MerkleizeWithMixin(elemIndx, byteLen, (1099511627776+31)/32) + } + + // Field (16) 'CurrentEpochParticipation' + { + elemIndx := hh.Index() + byteLen := uint64(len(b.CurrentEpochParticipation)) + if byteLen > 1099511627776 { + err = ssz.ErrIncorrectListSize + return + } + hh.Append(b.CurrentEpochParticipation) + hh.MerkleizeWithMixin(elemIndx, byteLen, (1099511627776+31)/32) + } + + // Field (17) 'JustificationBits' + hh.PutBytes(b.JustificationBits[:]) + + // Field (18) 'PreviousJustifiedCheckpoint' + if b.PreviousJustifiedCheckpoint == nil { + b.PreviousJustifiedCheckpoint = new(generic.Checkpoint) + } + if err = b.PreviousJustifiedCheckpoint.HashTreeRootWith(hh); err != nil { + return + } + + // Field (19) 'CurrentJustifiedCheckpoint' + if b.CurrentJustifiedCheckpoint == nil { + b.CurrentJustifiedCheckpoint = new(generic.Checkpoint) + } + if err = b.CurrentJustifiedCheckpoint.HashTreeRootWith(hh); err != nil { + return + } + + // Field (20) 'FinalizedCheckpoint' + if b.FinalizedCheckpoint == nil { + b.FinalizedCheckpoint = new(generic.Checkpoint) + } + if err = b.FinalizedCheckpoint.HashTreeRootWith(hh); err != nil { + return + } + + // Field (21) 'InactivityScores' + { + if size := len(b.InactivityScores); size > 1099511627776 { + err = ssz.ErrListTooBigFn("BeaconStateDeneb.InactivityScores", size, 1099511627776) + return + } + subIndx := hh.Index() + for _, i := range b.InactivityScores { + hh.AppendUint64(i) + } + hh.FillUpTo32() + numItems := uint64(len(b.InactivityScores)) + hh.MerkleizeWithMixin(subIndx, numItems, ssz.CalculateLimit(1099511627776, numItems, 8)) + } + + // Field (22) 'CurrentSyncCommittee' + if b.CurrentSyncCommittee == nil { + b.CurrentSyncCommittee = new(generic.SyncCommittee) + } + if err = b.CurrentSyncCommittee.HashTreeRootWith(hh); err != nil { + return + } + + // Field (23) 'NextSyncCommittee' + if b.NextSyncCommittee == nil { + b.NextSyncCommittee = new(generic.SyncCommittee) + } + if err = b.NextSyncCommittee.HashTreeRootWith(hh); err != nil { + return + } + + // Field (24) 'LatestExecutionPayloadHeader' + if err = b.LatestExecutionPayloadHeader.HashTreeRootWith(hh); err != nil { + return + } + + // Field (25) 'NextWithdrawalIndex' + hh.PutUint64(b.NextWithdrawalIndex) + + // Field (26) 'NextWithdrawalValidatorIndex' + hh.PutUint64(b.NextWithdrawalValidatorIndex) + + // Field (27) 'HistoricalSummaries' + { + subIndx := hh.Index() + num := uint64(len(b.HistoricalSummaries)) + if num > 16777216 { + err = ssz.ErrIncorrectListSize + return + } + for _, elem := range b.HistoricalSummaries { + if err = elem.HashTreeRootWith(hh); err != nil { + return + } + } + hh.MerkleizeWithMixin(subIndx, num, 16777216) + } + + hh.Merkleize(indx) + return +} + +// GetTree ssz hashes the BeaconStateDeneb object +func (b *BeaconState) GetTree() (*ssz.Node, error) { + return ssz.ProofTree(b) +} diff --git a/shared/types/eth2/fork/electra/block_electra.go b/shared/types/eth2/fork/electra/block_electra.go new file mode 100644 index 000000000..1177848ca --- /dev/null +++ b/shared/types/eth2/fork/electra/block_electra.go @@ -0,0 +1,101 @@ +package electra + +import "github.com/rocket-pool/smartnode/shared/types/eth2/generic" + +// Important indices for proof generation: +const BeaconBlockBodyChunksCeil uint64 = 16 + +func (b *BeaconBlock) ProveWithdrawal(indexInWithdrawalsArray uint64) ([][]byte, error) { + tree, err := b.GetTree() + if err != nil { + return nil, err + } + + gid := uint64(1) + // Navigate to the body + gid = gid*generic.BeaconBlockChunksCeil + generic.BeaconBlockBodyIndex + // Then to the ExecutionPayload + gid = gid*BeaconBlockBodyChunksCeil + generic.BeaconBlockBodyExecutionPayloadIndex + // Then to the withdrawals array + gid = gid*generic.BeaconBlockBodyExecutionPayloadChunksCeil + generic.BeaconBlockBodyExecutionPayloadWithdrawalsIndex + // Then to the array contents + gid = gid * 2 + // Finally to the withdrawal in question + gid = gid*generic.BeaconBlockWithdrawalsArrayMax + indexInWithdrawalsArray + + proof, err := tree.Prove(int(gid)) + if err != nil { + return nil, err + } + + return proof.Hashes, nil +} + +// Types needed for withdrawal proofs +type BeaconBlock struct { + Slot uint64 `json:"slot"` + ProposerIndex uint64 `json:"proposer_index"` + ParentRoot [32]byte `json:"parent_root" ssz-size:"32"` + StateRoot [32]byte `json:"state_root" ssz-size:"32"` + Body *BeaconBlockBody `json:"body"` +} + +type SignedBeaconBlock struct { + Block *BeaconBlock `json:"message"` + Signature []byte `json:"signature" ssz-size:"96"` +} + +type BeaconBlockBody struct { + RandaoReveal []byte `json:"randao_reveal" ssz-size:"96"` + Eth1Data *generic.Eth1Data `json:"eth1_data"` + Graffiti [32]byte `json:"graffiti" ssz-size:"32"` + ProposerSlashings []*generic.ProposerSlashing `json:"proposer_slashings" ssz-max:"16"` + AttesterSlashings []*AttesterSlashing `json:"attester_slashings" ssz-max:"1"` + Attestations []*generic.Attestation `json:"attestations" ssz-max:"8"` + Deposits []*generic.Deposit `json:"deposits" ssz-max:"16"` + VoluntaryExits []*generic.SignedVoluntaryExit `json:"voluntary_exits" ssz-max:"16"` + SyncAggregate *generic.SyncAggregate `json:"sync_aggregate"` + ExecutionPayload *generic.ExecutionPayload `json:"execution_payload"` + BlsToExecutionChanges []*generic.SignedBLSToExecutionChange `json:"bls_to_execution_changes" ssz-max:"16"` + BlobKzgCommitments [][48]byte `json:"blob_kzg_commitments" ssz-max:"4096,48" ssz-size:"?,48"` + ExecutionRequests *ExecutionRequests `json:"execution_requests"` +} + +type ExecutionRequests struct { + Deposits []*DepositRequest `json:"deposits" ssz-max:"8192"` + Withdrawals []*WithdrawalRequest `json:"withdrawals" ssz-max:"16"` + Consolidations []*ConsolidationRequest `json:"consolidations" ssz-max:"2"` +} + +type DepositRequest struct { + Pubkey []byte `json:"pubkey" ssz-size:"48"` + WithdrawalCredentials []byte `json:"withdrawal_credentials" ssz-size:"32"` + Amount uint64 `json:"amount"` + Signature []byte `json:"signature" ssz-size:"96"` + Index uint64 `json:"index"` +} + +type WithdrawalRequest struct { + SourceAddress []byte `json:"source_address" ssz-size:"20"` + ValidatorPubkey []byte `json:"validator_pubkey" ssz-size:"48"` + Amount uint64 `json:"amount"` +} + +type ConsolidationRequest struct { + SourceAddress []byte `json:"source_address" ssz-size:"20"` + SourcePubkey []byte `json:"source_pubkey" ssz-size:"48"` + TargetPubkey []byte `json:"target_pubkey" ssz-size:"48"` +} + +type AttesterSlashing struct { + Attestation1 *generic.IndexedAttestation `json:"attestation_1"` + Attestation2 *generic.IndexedAttestation `json:"attestation_2"` +} + +func (b *BeaconBlock) HasExecutionPayload() bool { + return b.Body.ExecutionPayload != nil +} + +func (b *BeaconBlock) Withdrawals() []*generic.Withdrawal { + return b.Body.ExecutionPayload.Withdrawals +} diff --git a/shared/types/eth2/fork/electra/block_electra_encoding.go b/shared/types/eth2/fork/electra/block_electra_encoding.go new file mode 100644 index 000000000..13cdd71b5 --- /dev/null +++ b/shared/types/eth2/fork/electra/block_electra_encoding.go @@ -0,0 +1,1557 @@ +// Code generated by fastssz. DO NOT EDIT. +// Hash: 2e0f36372f8ac8d6eedeefe79607ce66518d0694ccd5c39d16be04be1dbc1e65 +// Version: 0.1.3 +package electra + +import ( + ssz "github.com/ferranbt/fastssz" + "github.com/rocket-pool/smartnode/shared/types/eth2/generic" +) + +// MarshalSSZ ssz marshals the BeaconBlockElectra object +func (b *BeaconBlock) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(b) +} + +// MarshalSSZTo ssz marshals the BeaconBlockElectra object to a target array +func (b *BeaconBlock) MarshalSSZTo(buf []byte) (dst []byte, err error) { + dst = buf + offset := int(84) + + // Field (0) 'Slot' + dst = ssz.MarshalUint64(dst, b.Slot) + + // Field (1) 'ProposerIndex' + dst = ssz.MarshalUint64(dst, b.ProposerIndex) + + // Field (2) 'ParentRoot' + dst = append(dst, b.ParentRoot[:]...) + + // Field (3) 'StateRoot' + dst = append(dst, b.StateRoot[:]...) + + // Offset (4) 'Body' + dst = ssz.WriteOffset(dst, offset) + + // Field (4) 'Body' + if dst, err = b.Body.MarshalSSZTo(dst); err != nil { + return + } + + return +} + +// UnmarshalSSZ ssz unmarshals the BeaconBlockElectra object +func (b *BeaconBlock) UnmarshalSSZ(buf []byte) error { + var err error + size := uint64(len(buf)) + if size < 84 { + return ssz.ErrSize + } + + tail := buf + var o4 uint64 + + // Field (0) 'Slot' + b.Slot = ssz.UnmarshallUint64(buf[0:8]) + + // Field (1) 'ProposerIndex' + b.ProposerIndex = ssz.UnmarshallUint64(buf[8:16]) + + // Field (2) 'ParentRoot' + copy(b.ParentRoot[:], buf[16:48]) + + // Field (3) 'StateRoot' + copy(b.StateRoot[:], buf[48:80]) + + // Offset (4) 'Body' + if o4 = ssz.ReadOffset(buf[80:84]); o4 > size { + return ssz.ErrOffset + } + + if o4 != 84 { + return ssz.ErrInvalidVariableOffset + } + + // Field (4) 'Body' + { + buf = tail[o4:] + if b.Body == nil { + b.Body = new(BeaconBlockBody) + } + if err = b.Body.UnmarshalSSZ(buf); err != nil { + return err + } + } + return err +} + +// SizeSSZ returns the ssz encoded size in bytes for the BeaconBlockElectra object +func (b *BeaconBlock) SizeSSZ() (size int) { + size = 84 + + // Field (4) 'Body' + if b.Body == nil { + b.Body = new(BeaconBlockBody) + } + size += b.Body.SizeSSZ() + + return +} + +// HashTreeRoot ssz hashes the BeaconBlockElectra object +func (b *BeaconBlock) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(b) +} + +// HashTreeRootWith ssz hashes the BeaconBlockElectra object with a hasher +func (b *BeaconBlock) HashTreeRootWith(hh ssz.HashWalker) (err error) { + indx := hh.Index() + + // Field (0) 'Slot' + hh.PutUint64(b.Slot) + + // Field (1) 'ProposerIndex' + hh.PutUint64(b.ProposerIndex) + + // Field (2) 'ParentRoot' + hh.PutBytes(b.ParentRoot[:]) + + // Field (3) 'StateRoot' + hh.PutBytes(b.StateRoot[:]) + + // Field (4) 'Body' + if err = b.Body.HashTreeRootWith(hh); err != nil { + return + } + + hh.Merkleize(indx) + return +} + +// GetTree ssz hashes the BeaconBlockElectra object +func (b *BeaconBlock) GetTree() (*ssz.Node, error) { + return ssz.ProofTree(b) +} + +// MarshalSSZ ssz marshals the SignedBeaconBlockElectra object +func (s *SignedBeaconBlock) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(s) +} + +// MarshalSSZTo ssz marshals the SignedBeaconBlockElectra object to a target array +func (s *SignedBeaconBlock) MarshalSSZTo(buf []byte) (dst []byte, err error) { + dst = buf + offset := int(100) + + // Offset (0) 'Block' + dst = ssz.WriteOffset(dst, offset) + + // Field (1) 'Signature' + if size := len(s.Signature); size != 96 { + err = ssz.ErrBytesLengthFn("SignedBeaconBlockElectra.Signature", size, 96) + return + } + dst = append(dst, s.Signature...) + + // Field (0) 'Block' + if dst, err = s.Block.MarshalSSZTo(dst); err != nil { + return + } + + return +} + +// UnmarshalSSZ ssz unmarshals the SignedBeaconBlockElectra object +func (s *SignedBeaconBlock) UnmarshalSSZ(buf []byte) error { + var err error + size := uint64(len(buf)) + if size < 100 { + return ssz.ErrSize + } + + tail := buf + var o0 uint64 + + // Offset (0) 'Block' + if o0 = ssz.ReadOffset(buf[0:4]); o0 > size { + return ssz.ErrOffset + } + + if o0 != 100 { + return ssz.ErrInvalidVariableOffset + } + + // Field (1) 'Signature' + if cap(s.Signature) == 0 { + s.Signature = make([]byte, 0, len(buf[4:100])) + } + s.Signature = append(s.Signature, buf[4:100]...) + + // Field (0) 'Block' + { + buf = tail[o0:] + if s.Block == nil { + s.Block = new(BeaconBlock) + } + if err = s.Block.UnmarshalSSZ(buf); err != nil { + return err + } + } + return err +} + +// SizeSSZ returns the ssz encoded size in bytes for the SignedBeaconBlockElectra object +func (s *SignedBeaconBlock) SizeSSZ() (size int) { + size = 100 + + // Field (0) 'Block' + if s.Block == nil { + s.Block = new(BeaconBlock) + } + size += s.Block.SizeSSZ() + + return +} + +// HashTreeRoot ssz hashes the SignedBeaconBlockElectra object +func (s *SignedBeaconBlock) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(s) +} + +// HashTreeRootWith ssz hashes the SignedBeaconBlockElectra object with a hasher +func (s *SignedBeaconBlock) HashTreeRootWith(hh ssz.HashWalker) (err error) { + indx := hh.Index() + + // Field (0) 'Block' + if err = s.Block.HashTreeRootWith(hh); err != nil { + return + } + + // Field (1) 'Signature' + if size := len(s.Signature); size != 96 { + err = ssz.ErrBytesLengthFn("SignedBeaconBlockElectra.Signature", size, 96) + return + } + hh.PutBytes(s.Signature) + + hh.Merkleize(indx) + return +} + +// GetTree ssz hashes the SignedBeaconBlockElectra object +func (s *SignedBeaconBlock) GetTree() (*ssz.Node, error) { + return ssz.ProofTree(s) +} + +// MarshalSSZ ssz marshals the BeaconBlockBodyElectra object +func (b *BeaconBlockBody) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(b) +} + +// MarshalSSZTo ssz marshals the BeaconBlockBodyElectra object to a target array +func (b *BeaconBlockBody) MarshalSSZTo(buf []byte) (dst []byte, err error) { + dst = buf + offset := int(396) + + // Field (0) 'RandaoReveal' + if size := len(b.RandaoReveal); size != 96 { + err = ssz.ErrBytesLengthFn("BeaconBlockBodyElectra.RandaoReveal", size, 96) + return + } + dst = append(dst, b.RandaoReveal...) + + // Field (1) 'Eth1Data' + if b.Eth1Data == nil { + b.Eth1Data = new(generic.Eth1Data) + } + if dst, err = b.Eth1Data.MarshalSSZTo(dst); err != nil { + return + } + + // Field (2) 'Graffiti' + dst = append(dst, b.Graffiti[:]...) + + // Offset (3) 'ProposerSlashings' + dst = ssz.WriteOffset(dst, offset) + offset += len(b.ProposerSlashings) * 416 + + // Offset (4) 'AttesterSlashings' + dst = ssz.WriteOffset(dst, offset) + for ii := 0; ii < len(b.AttesterSlashings); ii++ { + offset += 4 + offset += b.AttesterSlashings[ii].SizeSSZ() + } + + // Offset (5) 'Attestations' + dst = ssz.WriteOffset(dst, offset) + for ii := 0; ii < len(b.Attestations); ii++ { + offset += 4 + offset += b.Attestations[ii].SizeSSZ() + } + + // Offset (6) 'Deposits' + dst = ssz.WriteOffset(dst, offset) + offset += len(b.Deposits) * 1240 + + // Offset (7) 'VoluntaryExits' + dst = ssz.WriteOffset(dst, offset) + offset += len(b.VoluntaryExits) * 112 + + // Field (8) 'SyncAggregate' + if b.SyncAggregate == nil { + b.SyncAggregate = new(generic.SyncAggregate) + } + if dst, err = b.SyncAggregate.MarshalSSZTo(dst); err != nil { + return + } + + // Offset (9) 'ExecutionPayload' + dst = ssz.WriteOffset(dst, offset) + if b.ExecutionPayload == nil { + b.ExecutionPayload = new(generic.ExecutionPayload) + } + offset += b.ExecutionPayload.SizeSSZ() + + // Offset (10) 'BlsToExecutionChanges' + dst = ssz.WriteOffset(dst, offset) + offset += len(b.BlsToExecutionChanges) * 172 + + // Offset (11) 'BlobKzgCommitments' + dst = ssz.WriteOffset(dst, offset) + offset += len(b.BlobKzgCommitments) * 48 + + // Offset (12) 'ExecutionRequests' + dst = ssz.WriteOffset(dst, offset) + + // Field (3) 'ProposerSlashings' + if size := len(b.ProposerSlashings); size > 16 { + err = ssz.ErrListTooBigFn("BeaconBlockBodyElectra.ProposerSlashings", size, 16) + return + } + for ii := 0; ii < len(b.ProposerSlashings); ii++ { + if dst, err = b.ProposerSlashings[ii].MarshalSSZTo(dst); err != nil { + return + } + } + + // Field (4) 'AttesterSlashings' + if size := len(b.AttesterSlashings); size > 1 { + err = ssz.ErrListTooBigFn("BeaconBlockBodyElectra.AttesterSlashings", size, 1) + return + } + { + offset = 4 * len(b.AttesterSlashings) + for ii := 0; ii < len(b.AttesterSlashings); ii++ { + dst = ssz.WriteOffset(dst, offset) + offset += b.AttesterSlashings[ii].SizeSSZ() + } + } + for ii := 0; ii < len(b.AttesterSlashings); ii++ { + if dst, err = b.AttesterSlashings[ii].MarshalSSZTo(dst); err != nil { + return + } + } + + // Field (5) 'Attestations' + if size := len(b.Attestations); size > 8 { + err = ssz.ErrListTooBigFn("BeaconBlockBodyElectra.Attestations", size, 8) + return + } + { + offset = 4 * len(b.Attestations) + for ii := 0; ii < len(b.Attestations); ii++ { + dst = ssz.WriteOffset(dst, offset) + offset += b.Attestations[ii].SizeSSZ() + } + } + for ii := 0; ii < len(b.Attestations); ii++ { + if dst, err = b.Attestations[ii].MarshalSSZTo(dst); err != nil { + return + } + } + + // Field (6) 'Deposits' + if size := len(b.Deposits); size > 16 { + err = ssz.ErrListTooBigFn("BeaconBlockBodyElectra.Deposits", size, 16) + return + } + for ii := 0; ii < len(b.Deposits); ii++ { + if dst, err = b.Deposits[ii].MarshalSSZTo(dst); err != nil { + return + } + } + + // Field (7) 'VoluntaryExits' + if size := len(b.VoluntaryExits); size > 16 { + err = ssz.ErrListTooBigFn("BeaconBlockBodyElectra.VoluntaryExits", size, 16) + return + } + for ii := 0; ii < len(b.VoluntaryExits); ii++ { + if dst, err = b.VoluntaryExits[ii].MarshalSSZTo(dst); err != nil { + return + } + } + + // Field (9) 'ExecutionPayload' + if dst, err = b.ExecutionPayload.MarshalSSZTo(dst); err != nil { + return + } + + // Field (10) 'BlsToExecutionChanges' + if size := len(b.BlsToExecutionChanges); size > 16 { + err = ssz.ErrListTooBigFn("BeaconBlockBodyElectra.BlsToExecutionChanges", size, 16) + return + } + for ii := 0; ii < len(b.BlsToExecutionChanges); ii++ { + if dst, err = b.BlsToExecutionChanges[ii].MarshalSSZTo(dst); err != nil { + return + } + } + + // Field (11) 'BlobKzgCommitments' + if size := len(b.BlobKzgCommitments); size > 4096 { + err = ssz.ErrListTooBigFn("BeaconBlockBodyElectra.BlobKzgCommitments", size, 4096) + return + } + for ii := 0; ii < len(b.BlobKzgCommitments); ii++ { + dst = append(dst, b.BlobKzgCommitments[ii][:]...) + } + + // Field (12) 'ExecutionRequests' + if dst, err = b.ExecutionRequests.MarshalSSZTo(dst); err != nil { + return + } + + return +} + +// UnmarshalSSZ ssz unmarshals the BeaconBlockBodyElectra object +func (b *BeaconBlockBody) UnmarshalSSZ(buf []byte) error { + var err error + size := uint64(len(buf)) + if size < 396 { + return ssz.ErrSize + } + + tail := buf + var o3, o4, o5, o6, o7, o9, o10, o11, o12 uint64 + + // Field (0) 'RandaoReveal' + if cap(b.RandaoReveal) == 0 { + b.RandaoReveal = make([]byte, 0, len(buf[0:96])) + } + b.RandaoReveal = append(b.RandaoReveal, buf[0:96]...) + + // Field (1) 'Eth1Data' + if b.Eth1Data == nil { + b.Eth1Data = new(generic.Eth1Data) + } + if err = b.Eth1Data.UnmarshalSSZ(buf[96:168]); err != nil { + return err + } + + // Field (2) 'Graffiti' + copy(b.Graffiti[:], buf[168:200]) + + // Offset (3) 'ProposerSlashings' + if o3 = ssz.ReadOffset(buf[200:204]); o3 > size { + return ssz.ErrOffset + } + + if o3 != 396 { + return ssz.ErrInvalidVariableOffset + } + + // Offset (4) 'AttesterSlashings' + if o4 = ssz.ReadOffset(buf[204:208]); o4 > size || o3 > o4 { + return ssz.ErrOffset + } + + // Offset (5) 'Attestations' + if o5 = ssz.ReadOffset(buf[208:212]); o5 > size || o4 > o5 { + return ssz.ErrOffset + } + + // Offset (6) 'Deposits' + if o6 = ssz.ReadOffset(buf[212:216]); o6 > size || o5 > o6 { + return ssz.ErrOffset + } + + // Offset (7) 'VoluntaryExits' + if o7 = ssz.ReadOffset(buf[216:220]); o7 > size || o6 > o7 { + return ssz.ErrOffset + } + + // Field (8) 'SyncAggregate' + if b.SyncAggregate == nil { + b.SyncAggregate = new(generic.SyncAggregate) + } + if err = b.SyncAggregate.UnmarshalSSZ(buf[220:380]); err != nil { + return err + } + + // Offset (9) 'ExecutionPayload' + if o9 = ssz.ReadOffset(buf[380:384]); o9 > size || o7 > o9 { + return ssz.ErrOffset + } + + // Offset (10) 'BlsToExecutionChanges' + if o10 = ssz.ReadOffset(buf[384:388]); o10 > size || o9 > o10 { + return ssz.ErrOffset + } + + // Offset (11) 'BlobKzgCommitments' + if o11 = ssz.ReadOffset(buf[388:392]); o11 > size || o10 > o11 { + return ssz.ErrOffset + } + + // Offset (12) 'ExecutionRequests' + if o12 = ssz.ReadOffset(buf[392:396]); o12 > size || o11 > o12 { + return ssz.ErrOffset + } + + // Field (3) 'ProposerSlashings' + { + buf = tail[o3:o4] + num, err := ssz.DivideInt2(len(buf), 416, 16) + if err != nil { + return err + } + b.ProposerSlashings = make([]*generic.ProposerSlashing, num) + for ii := 0; ii < num; ii++ { + if b.ProposerSlashings[ii] == nil { + b.ProposerSlashings[ii] = new(generic.ProposerSlashing) + } + if err = b.ProposerSlashings[ii].UnmarshalSSZ(buf[ii*416 : (ii+1)*416]); err != nil { + return err + } + } + } + + // Field (4) 'AttesterSlashings' + { + buf = tail[o4:o5] + num, err := ssz.DecodeDynamicLength(buf, 1) + if err != nil { + return err + } + b.AttesterSlashings = make([]*AttesterSlashing, num) + err = ssz.UnmarshalDynamic(buf, num, func(indx int, buf []byte) (err error) { + if b.AttesterSlashings[indx] == nil { + b.AttesterSlashings[indx] = new(AttesterSlashing) + } + if err = b.AttesterSlashings[indx].UnmarshalSSZ(buf); err != nil { + return err + } + return nil + }) + if err != nil { + return err + } + } + + // Field (5) 'Attestations' + { + buf = tail[o5:o6] + num, err := ssz.DecodeDynamicLength(buf, 8) + if err != nil { + return err + } + b.Attestations = make([]*generic.Attestation, num) + err = ssz.UnmarshalDynamic(buf, num, func(indx int, buf []byte) (err error) { + if b.Attestations[indx] == nil { + b.Attestations[indx] = new(generic.Attestation) + } + if err = b.Attestations[indx].UnmarshalSSZ(buf); err != nil { + return err + } + return nil + }) + if err != nil { + return err + } + } + + // Field (6) 'Deposits' + { + buf = tail[o6:o7] + num, err := ssz.DivideInt2(len(buf), 1240, 16) + if err != nil { + return err + } + b.Deposits = make([]*generic.Deposit, num) + for ii := 0; ii < num; ii++ { + if b.Deposits[ii] == nil { + b.Deposits[ii] = new(generic.Deposit) + } + if err = b.Deposits[ii].UnmarshalSSZ(buf[ii*1240 : (ii+1)*1240]); err != nil { + return err + } + } + } + + // Field (7) 'VoluntaryExits' + { + buf = tail[o7:o9] + num, err := ssz.DivideInt2(len(buf), 112, 16) + if err != nil { + return err + } + b.VoluntaryExits = make([]*generic.SignedVoluntaryExit, num) + for ii := 0; ii < num; ii++ { + if b.VoluntaryExits[ii] == nil { + b.VoluntaryExits[ii] = new(generic.SignedVoluntaryExit) + } + if err = b.VoluntaryExits[ii].UnmarshalSSZ(buf[ii*112 : (ii+1)*112]); err != nil { + return err + } + } + } + + // Field (9) 'ExecutionPayload' + { + buf = tail[o9:o10] + if b.ExecutionPayload == nil { + b.ExecutionPayload = new(generic.ExecutionPayload) + } + if err = b.ExecutionPayload.UnmarshalSSZ(buf); err != nil { + return err + } + } + + // Field (10) 'BlsToExecutionChanges' + { + buf = tail[o10:o11] + num, err := ssz.DivideInt2(len(buf), 172, 16) + if err != nil { + return err + } + b.BlsToExecutionChanges = make([]*generic.SignedBLSToExecutionChange, num) + for ii := 0; ii < num; ii++ { + if b.BlsToExecutionChanges[ii] == nil { + b.BlsToExecutionChanges[ii] = new(generic.SignedBLSToExecutionChange) + } + if err = b.BlsToExecutionChanges[ii].UnmarshalSSZ(buf[ii*172 : (ii+1)*172]); err != nil { + return err + } + } + } + + // Field (11) 'BlobKzgCommitments' + { + buf = tail[o11:o12] + num, err := ssz.DivideInt2(len(buf), 48, 4096) + if err != nil { + return err + } + b.BlobKzgCommitments = make([][48]byte, num) + for ii := 0; ii < num; ii++ { + copy(b.BlobKzgCommitments[ii][:], buf[ii*48:(ii+1)*48]) + } + } + + // Field (12) 'ExecutionRequests' + { + buf = tail[o12:] + if b.ExecutionRequests == nil { + b.ExecutionRequests = new(ExecutionRequests) + } + if err = b.ExecutionRequests.UnmarshalSSZ(buf); err != nil { + return err + } + } + return err +} + +// SizeSSZ returns the ssz encoded size in bytes for the BeaconBlockBodyElectra object +func (b *BeaconBlockBody) SizeSSZ() (size int) { + size = 396 + + // Field (3) 'ProposerSlashings' + size += len(b.ProposerSlashings) * 416 + + // Field (4) 'AttesterSlashings' + for ii := 0; ii < len(b.AttesterSlashings); ii++ { + size += 4 + size += b.AttesterSlashings[ii].SizeSSZ() + } + + // Field (5) 'Attestations' + for ii := 0; ii < len(b.Attestations); ii++ { + size += 4 + size += b.Attestations[ii].SizeSSZ() + } + + // Field (6) 'Deposits' + size += len(b.Deposits) * 1240 + + // Field (7) 'VoluntaryExits' + size += len(b.VoluntaryExits) * 112 + + // Field (9) 'ExecutionPayload' + if b.ExecutionPayload == nil { + b.ExecutionPayload = new(generic.ExecutionPayload) + } + size += b.ExecutionPayload.SizeSSZ() + + // Field (10) 'BlsToExecutionChanges' + size += len(b.BlsToExecutionChanges) * 172 + + // Field (11) 'BlobKzgCommitments' + size += len(b.BlobKzgCommitments) * 48 + + // Field (12) 'ExecutionRequests' + if b.ExecutionRequests == nil { + b.ExecutionRequests = new(ExecutionRequests) + } + size += b.ExecutionRequests.SizeSSZ() + + return +} + +// HashTreeRoot ssz hashes the BeaconBlockBodyElectra object +func (b *BeaconBlockBody) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(b) +} + +// HashTreeRootWith ssz hashes the BeaconBlockBodyElectra object with a hasher +func (b *BeaconBlockBody) HashTreeRootWith(hh ssz.HashWalker) (err error) { + indx := hh.Index() + + // Field (0) 'RandaoReveal' + if size := len(b.RandaoReveal); size != 96 { + err = ssz.ErrBytesLengthFn("BeaconBlockBodyElectra.RandaoReveal", size, 96) + return + } + hh.PutBytes(b.RandaoReveal) + + // Field (1) 'Eth1Data' + if b.Eth1Data == nil { + b.Eth1Data = new(generic.Eth1Data) + } + if err = b.Eth1Data.HashTreeRootWith(hh); err != nil { + return + } + + // Field (2) 'Graffiti' + hh.PutBytes(b.Graffiti[:]) + + // Field (3) 'ProposerSlashings' + { + subIndx := hh.Index() + num := uint64(len(b.ProposerSlashings)) + if num > 16 { + err = ssz.ErrIncorrectListSize + return + } + for _, elem := range b.ProposerSlashings { + if err = elem.HashTreeRootWith(hh); err != nil { + return + } + } + hh.MerkleizeWithMixin(subIndx, num, 16) + } + + // Field (4) 'AttesterSlashings' + { + subIndx := hh.Index() + num := uint64(len(b.AttesterSlashings)) + if num > 1 { + err = ssz.ErrIncorrectListSize + return + } + for _, elem := range b.AttesterSlashings { + if err = elem.HashTreeRootWith(hh); err != nil { + return + } + } + hh.MerkleizeWithMixin(subIndx, num, 1) + } + + // Field (5) 'Attestations' + { + subIndx := hh.Index() + num := uint64(len(b.Attestations)) + if num > 8 { + err = ssz.ErrIncorrectListSize + return + } + for _, elem := range b.Attestations { + if err = elem.HashTreeRootWith(hh); err != nil { + return + } + } + hh.MerkleizeWithMixin(subIndx, num, 8) + } + + // Field (6) 'Deposits' + { + subIndx := hh.Index() + num := uint64(len(b.Deposits)) + if num > 16 { + err = ssz.ErrIncorrectListSize + return + } + for _, elem := range b.Deposits { + if err = elem.HashTreeRootWith(hh); err != nil { + return + } + } + hh.MerkleizeWithMixin(subIndx, num, 16) + } + + // Field (7) 'VoluntaryExits' + { + subIndx := hh.Index() + num := uint64(len(b.VoluntaryExits)) + if num > 16 { + err = ssz.ErrIncorrectListSize + return + } + for _, elem := range b.VoluntaryExits { + if err = elem.HashTreeRootWith(hh); err != nil { + return + } + } + hh.MerkleizeWithMixin(subIndx, num, 16) + } + + // Field (8) 'SyncAggregate' + if b.SyncAggregate == nil { + b.SyncAggregate = new(generic.SyncAggregate) + } + if err = b.SyncAggregate.HashTreeRootWith(hh); err != nil { + return + } + + // Field (9) 'ExecutionPayload' + if err = b.ExecutionPayload.HashTreeRootWith(hh); err != nil { + return + } + + // Field (10) 'BlsToExecutionChanges' + { + subIndx := hh.Index() + num := uint64(len(b.BlsToExecutionChanges)) + if num > 16 { + err = ssz.ErrIncorrectListSize + return + } + for _, elem := range b.BlsToExecutionChanges { + if err = elem.HashTreeRootWith(hh); err != nil { + return + } + } + hh.MerkleizeWithMixin(subIndx, num, 16) + } + + // Field (11) 'BlobKzgCommitments' + { + if size := len(b.BlobKzgCommitments); size > 4096 { + err = ssz.ErrListTooBigFn("BeaconBlockBodyElectra.BlobKzgCommitments", size, 4096) + return + } + subIndx := hh.Index() + for _, i := range b.BlobKzgCommitments { + hh.PutBytes(i[:]) + } + numItems := uint64(len(b.BlobKzgCommitments)) + hh.MerkleizeWithMixin(subIndx, numItems, 4096) + } + + // Field (12) 'ExecutionRequests' + if err = b.ExecutionRequests.HashTreeRootWith(hh); err != nil { + return + } + + hh.Merkleize(indx) + return +} + +// GetTree ssz hashes the BeaconBlockBodyElectra object +func (b *BeaconBlockBody) GetTree() (*ssz.Node, error) { + return ssz.ProofTree(b) +} + +// MarshalSSZ ssz marshals the ExecutionRequests object +func (e *ExecutionRequests) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(e) +} + +// MarshalSSZTo ssz marshals the ExecutionRequests object to a target array +func (e *ExecutionRequests) MarshalSSZTo(buf []byte) (dst []byte, err error) { + dst = buf + offset := int(12) + + // Offset (0) 'Deposits' + dst = ssz.WriteOffset(dst, offset) + offset += len(e.Deposits) * 192 + + // Offset (1) 'Withdrawals' + dst = ssz.WriteOffset(dst, offset) + offset += len(e.Withdrawals) * 76 + + // Offset (2) 'Consolidations' + dst = ssz.WriteOffset(dst, offset) + + // Field (0) 'Deposits' + if size := len(e.Deposits); size > 8192 { + err = ssz.ErrListTooBigFn("ExecutionRequests.Deposits", size, 8192) + return + } + for ii := 0; ii < len(e.Deposits); ii++ { + if dst, err = e.Deposits[ii].MarshalSSZTo(dst); err != nil { + return + } + } + + // Field (1) 'Withdrawals' + if size := len(e.Withdrawals); size > 16 { + err = ssz.ErrListTooBigFn("ExecutionRequests.Withdrawals", size, 16) + return + } + for ii := 0; ii < len(e.Withdrawals); ii++ { + if dst, err = e.Withdrawals[ii].MarshalSSZTo(dst); err != nil { + return + } + } + + // Field (2) 'Consolidations' + if size := len(e.Consolidations); size > 2 { + err = ssz.ErrListTooBigFn("ExecutionRequests.Consolidations", size, 2) + return + } + for ii := 0; ii < len(e.Consolidations); ii++ { + if dst, err = e.Consolidations[ii].MarshalSSZTo(dst); err != nil { + return + } + } + + return +} + +// UnmarshalSSZ ssz unmarshals the ExecutionRequests object +func (e *ExecutionRequests) UnmarshalSSZ(buf []byte) error { + var err error + size := uint64(len(buf)) + if size < 12 { + return ssz.ErrSize + } + + tail := buf + var o0, o1, o2 uint64 + + // Offset (0) 'Deposits' + if o0 = ssz.ReadOffset(buf[0:4]); o0 > size { + return ssz.ErrOffset + } + + if o0 != 12 { + return ssz.ErrInvalidVariableOffset + } + + // Offset (1) 'Withdrawals' + if o1 = ssz.ReadOffset(buf[4:8]); o1 > size || o0 > o1 { + return ssz.ErrOffset + } + + // Offset (2) 'Consolidations' + if o2 = ssz.ReadOffset(buf[8:12]); o2 > size || o1 > o2 { + return ssz.ErrOffset + } + + // Field (0) 'Deposits' + { + buf = tail[o0:o1] + num, err := ssz.DivideInt2(len(buf), 192, 8192) + if err != nil { + return err + } + e.Deposits = make([]*DepositRequest, num) + for ii := 0; ii < num; ii++ { + if e.Deposits[ii] == nil { + e.Deposits[ii] = new(DepositRequest) + } + if err = e.Deposits[ii].UnmarshalSSZ(buf[ii*192 : (ii+1)*192]); err != nil { + return err + } + } + } + + // Field (1) 'Withdrawals' + { + buf = tail[o1:o2] + num, err := ssz.DivideInt2(len(buf), 76, 16) + if err != nil { + return err + } + e.Withdrawals = make([]*WithdrawalRequest, num) + for ii := 0; ii < num; ii++ { + if e.Withdrawals[ii] == nil { + e.Withdrawals[ii] = new(WithdrawalRequest) + } + if err = e.Withdrawals[ii].UnmarshalSSZ(buf[ii*76 : (ii+1)*76]); err != nil { + return err + } + } + } + + // Field (2) 'Consolidations' + { + buf = tail[o2:] + num, err := ssz.DivideInt2(len(buf), 116, 2) + if err != nil { + return err + } + e.Consolidations = make([]*ConsolidationRequest, num) + for ii := 0; ii < num; ii++ { + if e.Consolidations[ii] == nil { + e.Consolidations[ii] = new(ConsolidationRequest) + } + if err = e.Consolidations[ii].UnmarshalSSZ(buf[ii*116 : (ii+1)*116]); err != nil { + return err + } + } + } + return err +} + +// SizeSSZ returns the ssz encoded size in bytes for the ExecutionRequests object +func (e *ExecutionRequests) SizeSSZ() (size int) { + size = 12 + + // Field (0) 'Deposits' + size += len(e.Deposits) * 192 + + // Field (1) 'Withdrawals' + size += len(e.Withdrawals) * 76 + + // Field (2) 'Consolidations' + size += len(e.Consolidations) * 116 + + return +} + +// HashTreeRoot ssz hashes the ExecutionRequests object +func (e *ExecutionRequests) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(e) +} + +// HashTreeRootWith ssz hashes the ExecutionRequests object with a hasher +func (e *ExecutionRequests) HashTreeRootWith(hh ssz.HashWalker) (err error) { + indx := hh.Index() + + // Field (0) 'Deposits' + { + subIndx := hh.Index() + num := uint64(len(e.Deposits)) + if num > 8192 { + err = ssz.ErrIncorrectListSize + return + } + for _, elem := range e.Deposits { + if err = elem.HashTreeRootWith(hh); err != nil { + return + } + } + hh.MerkleizeWithMixin(subIndx, num, 8192) + } + + // Field (1) 'Withdrawals' + { + subIndx := hh.Index() + num := uint64(len(e.Withdrawals)) + if num > 16 { + err = ssz.ErrIncorrectListSize + return + } + for _, elem := range e.Withdrawals { + if err = elem.HashTreeRootWith(hh); err != nil { + return + } + } + hh.MerkleizeWithMixin(subIndx, num, 16) + } + + // Field (2) 'Consolidations' + { + subIndx := hh.Index() + num := uint64(len(e.Consolidations)) + if num > 2 { + err = ssz.ErrIncorrectListSize + return + } + for _, elem := range e.Consolidations { + if err = elem.HashTreeRootWith(hh); err != nil { + return + } + } + hh.MerkleizeWithMixin(subIndx, num, 2) + } + + hh.Merkleize(indx) + return +} + +// GetTree ssz hashes the ExecutionRequests object +func (e *ExecutionRequests) GetTree() (*ssz.Node, error) { + return ssz.ProofTree(e) +} + +// MarshalSSZ ssz marshals the DepositRequest object +func (d *DepositRequest) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(d) +} + +// MarshalSSZTo ssz marshals the DepositRequest object to a target array +func (d *DepositRequest) MarshalSSZTo(buf []byte) (dst []byte, err error) { + dst = buf + + // Field (0) 'Pubkey' + if size := len(d.Pubkey); size != 48 { + err = ssz.ErrBytesLengthFn("DepositRequest.Pubkey", size, 48) + return + } + dst = append(dst, d.Pubkey...) + + // Field (1) 'WithdrawalCredentials' + if size := len(d.WithdrawalCredentials); size != 32 { + err = ssz.ErrBytesLengthFn("DepositRequest.WithdrawalCredentials", size, 32) + return + } + dst = append(dst, d.WithdrawalCredentials...) + + // Field (2) 'Amount' + dst = ssz.MarshalUint64(dst, d.Amount) + + // Field (3) 'Signature' + if size := len(d.Signature); size != 96 { + err = ssz.ErrBytesLengthFn("DepositRequest.Signature", size, 96) + return + } + dst = append(dst, d.Signature...) + + // Field (4) 'Index' + dst = ssz.MarshalUint64(dst, d.Index) + + return +} + +// UnmarshalSSZ ssz unmarshals the DepositRequest object +func (d *DepositRequest) UnmarshalSSZ(buf []byte) error { + var err error + size := uint64(len(buf)) + if size != 192 { + return ssz.ErrSize + } + + // Field (0) 'Pubkey' + if cap(d.Pubkey) == 0 { + d.Pubkey = make([]byte, 0, len(buf[0:48])) + } + d.Pubkey = append(d.Pubkey, buf[0:48]...) + + // Field (1) 'WithdrawalCredentials' + if cap(d.WithdrawalCredentials) == 0 { + d.WithdrawalCredentials = make([]byte, 0, len(buf[48:80])) + } + d.WithdrawalCredentials = append(d.WithdrawalCredentials, buf[48:80]...) + + // Field (2) 'Amount' + d.Amount = ssz.UnmarshallUint64(buf[80:88]) + + // Field (3) 'Signature' + if cap(d.Signature) == 0 { + d.Signature = make([]byte, 0, len(buf[88:184])) + } + d.Signature = append(d.Signature, buf[88:184]...) + + // Field (4) 'Index' + d.Index = ssz.UnmarshallUint64(buf[184:192]) + + return err +} + +// SizeSSZ returns the ssz encoded size in bytes for the DepositRequest object +func (d *DepositRequest) SizeSSZ() (size int) { + size = 192 + return +} + +// HashTreeRoot ssz hashes the DepositRequest object +func (d *DepositRequest) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(d) +} + +// HashTreeRootWith ssz hashes the DepositRequest object with a hasher +func (d *DepositRequest) HashTreeRootWith(hh ssz.HashWalker) (err error) { + indx := hh.Index() + + // Field (0) 'Pubkey' + if size := len(d.Pubkey); size != 48 { + err = ssz.ErrBytesLengthFn("DepositRequest.Pubkey", size, 48) + return + } + hh.PutBytes(d.Pubkey) + + // Field (1) 'WithdrawalCredentials' + if size := len(d.WithdrawalCredentials); size != 32 { + err = ssz.ErrBytesLengthFn("DepositRequest.WithdrawalCredentials", size, 32) + return + } + hh.PutBytes(d.WithdrawalCredentials) + + // Field (2) 'Amount' + hh.PutUint64(d.Amount) + + // Field (3) 'Signature' + if size := len(d.Signature); size != 96 { + err = ssz.ErrBytesLengthFn("DepositRequest.Signature", size, 96) + return + } + hh.PutBytes(d.Signature) + + // Field (4) 'Index' + hh.PutUint64(d.Index) + + hh.Merkleize(indx) + return +} + +// GetTree ssz hashes the DepositRequest object +func (d *DepositRequest) GetTree() (*ssz.Node, error) { + return ssz.ProofTree(d) +} + +// MarshalSSZ ssz marshals the WithdrawalRequest object +func (w *WithdrawalRequest) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(w) +} + +// MarshalSSZTo ssz marshals the WithdrawalRequest object to a target array +func (w *WithdrawalRequest) MarshalSSZTo(buf []byte) (dst []byte, err error) { + dst = buf + + // Field (0) 'SourceAddress' + if size := len(w.SourceAddress); size != 20 { + err = ssz.ErrBytesLengthFn("WithdrawalRequest.SourceAddress", size, 20) + return + } + dst = append(dst, w.SourceAddress...) + + // Field (1) 'ValidatorPubkey' + if size := len(w.ValidatorPubkey); size != 48 { + err = ssz.ErrBytesLengthFn("WithdrawalRequest.ValidatorPubkey", size, 48) + return + } + dst = append(dst, w.ValidatorPubkey...) + + // Field (2) 'Amount' + dst = ssz.MarshalUint64(dst, w.Amount) + + return +} + +// UnmarshalSSZ ssz unmarshals the WithdrawalRequest object +func (w *WithdrawalRequest) UnmarshalSSZ(buf []byte) error { + var err error + size := uint64(len(buf)) + if size != 76 { + return ssz.ErrSize + } + + // Field (0) 'SourceAddress' + if cap(w.SourceAddress) == 0 { + w.SourceAddress = make([]byte, 0, len(buf[0:20])) + } + w.SourceAddress = append(w.SourceAddress, buf[0:20]...) + + // Field (1) 'ValidatorPubkey' + if cap(w.ValidatorPubkey) == 0 { + w.ValidatorPubkey = make([]byte, 0, len(buf[20:68])) + } + w.ValidatorPubkey = append(w.ValidatorPubkey, buf[20:68]...) + + // Field (2) 'Amount' + w.Amount = ssz.UnmarshallUint64(buf[68:76]) + + return err +} + +// SizeSSZ returns the ssz encoded size in bytes for the WithdrawalRequest object +func (w *WithdrawalRequest) SizeSSZ() (size int) { + size = 76 + return +} + +// HashTreeRoot ssz hashes the WithdrawalRequest object +func (w *WithdrawalRequest) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(w) +} + +// HashTreeRootWith ssz hashes the WithdrawalRequest object with a hasher +func (w *WithdrawalRequest) HashTreeRootWith(hh ssz.HashWalker) (err error) { + indx := hh.Index() + + // Field (0) 'SourceAddress' + if size := len(w.SourceAddress); size != 20 { + err = ssz.ErrBytesLengthFn("WithdrawalRequest.SourceAddress", size, 20) + return + } + hh.PutBytes(w.SourceAddress) + + // Field (1) 'ValidatorPubkey' + if size := len(w.ValidatorPubkey); size != 48 { + err = ssz.ErrBytesLengthFn("WithdrawalRequest.ValidatorPubkey", size, 48) + return + } + hh.PutBytes(w.ValidatorPubkey) + + // Field (2) 'Amount' + hh.PutUint64(w.Amount) + + hh.Merkleize(indx) + return +} + +// GetTree ssz hashes the WithdrawalRequest object +func (w *WithdrawalRequest) GetTree() (*ssz.Node, error) { + return ssz.ProofTree(w) +} + +// MarshalSSZ ssz marshals the ConsolidationRequest object +func (c *ConsolidationRequest) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(c) +} + +// MarshalSSZTo ssz marshals the ConsolidationRequest object to a target array +func (c *ConsolidationRequest) MarshalSSZTo(buf []byte) (dst []byte, err error) { + dst = buf + + // Field (0) 'SourceAddress' + if size := len(c.SourceAddress); size != 20 { + err = ssz.ErrBytesLengthFn("ConsolidationRequest.SourceAddress", size, 20) + return + } + dst = append(dst, c.SourceAddress...) + + // Field (1) 'SourcePubkey' + if size := len(c.SourcePubkey); size != 48 { + err = ssz.ErrBytesLengthFn("ConsolidationRequest.SourcePubkey", size, 48) + return + } + dst = append(dst, c.SourcePubkey...) + + // Field (2) 'TargetPubkey' + if size := len(c.TargetPubkey); size != 48 { + err = ssz.ErrBytesLengthFn("ConsolidationRequest.TargetPubkey", size, 48) + return + } + dst = append(dst, c.TargetPubkey...) + + return +} + +// UnmarshalSSZ ssz unmarshals the ConsolidationRequest object +func (c *ConsolidationRequest) UnmarshalSSZ(buf []byte) error { + var err error + size := uint64(len(buf)) + if size != 116 { + return ssz.ErrSize + } + + // Field (0) 'SourceAddress' + if cap(c.SourceAddress) == 0 { + c.SourceAddress = make([]byte, 0, len(buf[0:20])) + } + c.SourceAddress = append(c.SourceAddress, buf[0:20]...) + + // Field (1) 'SourcePubkey' + if cap(c.SourcePubkey) == 0 { + c.SourcePubkey = make([]byte, 0, len(buf[20:68])) + } + c.SourcePubkey = append(c.SourcePubkey, buf[20:68]...) + + // Field (2) 'TargetPubkey' + if cap(c.TargetPubkey) == 0 { + c.TargetPubkey = make([]byte, 0, len(buf[68:116])) + } + c.TargetPubkey = append(c.TargetPubkey, buf[68:116]...) + + return err +} + +// SizeSSZ returns the ssz encoded size in bytes for the ConsolidationRequest object +func (c *ConsolidationRequest) SizeSSZ() (size int) { + size = 116 + return +} + +// HashTreeRoot ssz hashes the ConsolidationRequest object +func (c *ConsolidationRequest) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(c) +} + +// HashTreeRootWith ssz hashes the ConsolidationRequest object with a hasher +func (c *ConsolidationRequest) HashTreeRootWith(hh ssz.HashWalker) (err error) { + indx := hh.Index() + + // Field (0) 'SourceAddress' + if size := len(c.SourceAddress); size != 20 { + err = ssz.ErrBytesLengthFn("ConsolidationRequest.SourceAddress", size, 20) + return + } + hh.PutBytes(c.SourceAddress) + + // Field (1) 'SourcePubkey' + if size := len(c.SourcePubkey); size != 48 { + err = ssz.ErrBytesLengthFn("ConsolidationRequest.SourcePubkey", size, 48) + return + } + hh.PutBytes(c.SourcePubkey) + + // Field (2) 'TargetPubkey' + if size := len(c.TargetPubkey); size != 48 { + err = ssz.ErrBytesLengthFn("ConsolidationRequest.TargetPubkey", size, 48) + return + } + hh.PutBytes(c.TargetPubkey) + + hh.Merkleize(indx) + return +} + +// GetTree ssz hashes the ConsolidationRequest object +func (c *ConsolidationRequest) GetTree() (*ssz.Node, error) { + return ssz.ProofTree(c) +} + +// MarshalSSZ ssz marshals the AttesterSlashingElectra object +func (a *AttesterSlashing) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(a) +} + +// MarshalSSZTo ssz marshals the AttesterSlashingElectra object to a target array +func (a *AttesterSlashing) MarshalSSZTo(buf []byte) (dst []byte, err error) { + dst = buf + offset := int(8) + + // Offset (0) 'Attestation1' + dst = ssz.WriteOffset(dst, offset) + if a.Attestation1 == nil { + a.Attestation1 = new(generic.IndexedAttestation) + } + offset += a.Attestation1.SizeSSZ() + + // Offset (1) 'Attestation2' + dst = ssz.WriteOffset(dst, offset) + + // Field (0) 'Attestation1' + if dst, err = a.Attestation1.MarshalSSZTo(dst); err != nil { + return + } + + // Field (1) 'Attestation2' + if dst, err = a.Attestation2.MarshalSSZTo(dst); err != nil { + return + } + + return +} + +// UnmarshalSSZ ssz unmarshals the AttesterSlashingElectra object +func (a *AttesterSlashing) UnmarshalSSZ(buf []byte) error { + var err error + size := uint64(len(buf)) + if size < 8 { + return ssz.ErrSize + } + + tail := buf + var o0, o1 uint64 + + // Offset (0) 'Attestation1' + if o0 = ssz.ReadOffset(buf[0:4]); o0 > size { + return ssz.ErrOffset + } + + if o0 != 8 { + return ssz.ErrInvalidVariableOffset + } + + // Offset (1) 'Attestation2' + if o1 = ssz.ReadOffset(buf[4:8]); o1 > size || o0 > o1 { + return ssz.ErrOffset + } + + // Field (0) 'Attestation1' + { + buf = tail[o0:o1] + if a.Attestation1 == nil { + a.Attestation1 = new(generic.IndexedAttestation) + } + if err = a.Attestation1.UnmarshalSSZ(buf); err != nil { + return err + } + } + + // Field (1) 'Attestation2' + { + buf = tail[o1:] + if a.Attestation2 == nil { + a.Attestation2 = new(generic.IndexedAttestation) + } + if err = a.Attestation2.UnmarshalSSZ(buf); err != nil { + return err + } + } + return err +} + +// SizeSSZ returns the ssz encoded size in bytes for the AttesterSlashingElectra object +func (a *AttesterSlashing) SizeSSZ() (size int) { + size = 8 + + // Field (0) 'Attestation1' + if a.Attestation1 == nil { + a.Attestation1 = new(generic.IndexedAttestation) + } + size += a.Attestation1.SizeSSZ() + + // Field (1) 'Attestation2' + if a.Attestation2 == nil { + a.Attestation2 = new(generic.IndexedAttestation) + } + size += a.Attestation2.SizeSSZ() + + return +} + +// HashTreeRoot ssz hashes the AttesterSlashingElectra object +func (a *AttesterSlashing) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(a) +} + +// HashTreeRootWith ssz hashes the AttesterSlashingElectra object with a hasher +func (a *AttesterSlashing) HashTreeRootWith(hh ssz.HashWalker) (err error) { + indx := hh.Index() + + // Field (0) 'Attestation1' + if err = a.Attestation1.HashTreeRootWith(hh); err != nil { + return + } + + // Field (1) 'Attestation2' + if err = a.Attestation2.HashTreeRootWith(hh); err != nil { + return + } + + hh.Merkleize(indx) + return +} + +// GetTree ssz hashes the AttesterSlashingElectra object +func (a *AttesterSlashing) GetTree() (*ssz.Node, error) { + return ssz.ProofTree(a) +} diff --git a/shared/types/eth2/fork/electra/state_electra.go b/shared/types/eth2/fork/electra/state_electra.go new file mode 100644 index 000000000..e30232222 --- /dev/null +++ b/shared/types/eth2/fork/electra/state_electra.go @@ -0,0 +1,293 @@ +package electra + +import ( + "bytes" + "errors" + "fmt" + "reflect" + "sync/atomic" + + "github.com/rocket-pool/smartnode/shared/types/eth2/generic" + "github.com/rocket-pool/smartnode/shared/utils/math" +) + +const beaconStateChunkCeil uint64 = 64 + +// Taken from https://github.com/OffchainLabs/prysm/blob/a0071826c5daf7dc3a6e76874fdaa76481a3c665/proto/prysm/v1alpha1/beacon_state.pb.go#L1955 +// Unexported fields stripped, as well as proto-related field tags. JSON and ssz-size tags are preserved, and nested types are replaced with local copies as well. +type BeaconState struct { + GenesisTime uint64 `json:"genesis_time"` + GenesisValidatorsRoot []byte `json:"genesis_validators_root" ssz-size:"32"` + Slot uint64 `json:"slot"` + Fork *generic.Fork `json:"fork"` + LatestBlockHeader *generic.BeaconBlockHeader `json:"latest_block_header"` + BlockRoots [8192][32]byte `json:"block_roots" ssz-size:"8192,32"` + StateRoots [8192][32]byte `json:"state_roots" ssz-size:"8192,32"` + HistoricalRoots [][]byte `json:"historical_roots" ssz-max:"16777216" ssz-size:"?,32"` + Eth1Data *generic.Eth1Data `json:"eth1_data"` + Eth1DataVotes []*generic.Eth1Data `json:"eth1_data_votes" ssz-max:"2048"` + Eth1DepositIndex uint64 `json:"eth1_deposit_index"` + Validators []*generic.Validator `json:"validators" ssz-max:"1099511627776"` + Balances []uint64 `json:"balances" ssz-max:"1099511627776"` + RandaoMixes [][]byte `json:"randao_mixes" ssz-size:"65536,32"` + Slashings []uint64 `json:"slashings" ssz-size:"8192"` + PreviousEpochParticipation []byte `json:"previous_epoch_participation" ssz-max:"1099511627776"` + CurrentEpochParticipation []byte `json:"current_epoch_participation" ssz-max:"1099511627776"` + JustificationBits [1]byte `json:"justification_bits" ssz-size:"1"` + PreviousJustifiedCheckpoint *generic.Checkpoint `json:"previous_justified_checkpoint"` + CurrentJustifiedCheckpoint *generic.Checkpoint `json:"current_justified_checkpoint"` + FinalizedCheckpoint *generic.Checkpoint `json:"finalized_checkpoint"` + InactivityScores []uint64 `json:"inactivity_scores" ssz-max:"1099511627776"` + CurrentSyncCommittee *generic.SyncCommittee `json:"current_sync_committee"` + NextSyncCommittee *generic.SyncCommittee `json:"next_sync_committee"` + LatestExecutionPayloadHeader *generic.ExecutionPayloadHeader `json:"latest_execution_payload_header"` + NextWithdrawalIndex uint64 `json:"next_withdrawal_index"` + NextWithdrawalValidatorIndex uint64 `json:"next_withdrawal_validator_index"` + HistoricalSummaries []*generic.HistoricalSummary `json:"historical_summaries" ssz-max:"16777216"` + + // New in Electra + DepositRequestsStartIndex uint64 `json:"deposit_requests_start_index"` + DepositBalanceToConsume uint64 `json:"deposit_balance_to_consume"` + ExitBalanceToConsume uint64 `json:"exit_balance_to_consume"` + EarliestExitEpoch uint64 `json:"earliest_exit_epoch"` + ConsolidationBalanceToConsume uint64 `json:"consolidation_balance_to_consume"` + EarliestConsolidationEpoch uint64 `json:"earliest_consolidation_epoch"` + PendingDeposits []*generic.PendingDeposit `json:"pending_deposits,omitempty" ssz-max:"134217728"` + PendingPartialWithdrawals []*generic.PendingPartialWithdrawal `json:"pending_partial_withdrawals,omitempty" ssz-max:"134217728"` + PendingConsolidations []*generic.PendingConsolidation `json:"pending_consolidations,omitempty" ssz-max:"262144"` +} + +var beaconStateChunkSize atomic.Uint64 + +func getStateChunkSize() uint64 { + // Use a static value to avoid multiple reflection calls + storedChunkSize := beaconStateChunkSize.Load() + if storedChunkSize == 0 { + s := reflect.TypeOf(BeaconState{}).NumField() + beaconStateChunkSize.Store(uint64(s)) + storedChunkSize = uint64(s) + } + return storedChunkSize +} + +func GetGeneralizedIndexForValidators() uint64 { + // There's 28 fields, so rounding up to the next power of two is 32, a left-aligned node + // BeaconStateValidatorsIndex is the 11th field, so its generalized index is 32 + 11 = 43 + return math.GetPowerOfTwoCeil(getStateChunkSize()) + generic.BeaconStateValidatorsIndex +} + +func (state *BeaconState) validatorStateProof(index uint64) ([][]byte, error) { + + // Convert the state to a proof tree + root, err := state.GetTree() + if err != nil { + return nil, fmt.Errorf("could not get state tree: %w", err) + } + + // Find the validator's generalized index + generalizedIndex := generic.GetGeneralizedIndexForValidator(index, GetGeneralizedIndexForValidators()) + + // Grab the proof for that index + proof, err := root.Prove(int(generalizedIndex)) + if err != nil { + return nil, fmt.Errorf("could not get proof for validator: %w", err) + } + + // Sanity check that the proof leaf matches the expected validator + validatorHashTreeRoot, err := state.Validators[index].HashTreeRoot() + if err != nil { + return nil, fmt.Errorf("could not get hash tree root for validator: %w", err) + } + if !bytes.Equal(proof.Leaf, validatorHashTreeRoot[:]) { + return nil, fmt.Errorf("proof leaf does not match expected validator") + } + + return proof.Hashes, nil + +} + +// ValidatorWithdrawableEpochProof computes the merkle proof for a validator's withdrawable epoch +// at a specific index in the validator registry. +func (state *BeaconState) ValidatorWithdrawableEpochProof(index uint64) ([][]byte, error) { + + if index >= uint64(len(state.Validators)) { + return nil, errors.New("validator index out of bounds") + } + + // Get the validator's withdrawable epoch proof + withdrawableEpochProof, err := state.Validators[index].ValidatorWithdrawableEpochProof() + if err != nil { + return nil, fmt.Errorf("could not get validator withdrawable epoch proof: %w", err) + } + + stateProof, err := state.validatorStateProof(index) + if err != nil { + return nil, fmt.Errorf("could not get validator state proof: %w", err) + } + + // The EL proves against BeaconBlockHeader root, so we need to merge the state proof with that. + generalizedIndex := generic.BeaconBlockHeaderStateRootGeneralizedIndex + root, err := state.LatestBlockHeader.GetTree() + if err != nil { + return nil, fmt.Errorf("could not get block header tree: %w", err) + } + blockHeaderProof, err := root.Prove(int(generalizedIndex)) + if err != nil { + return nil, fmt.Errorf("could not get proof for block header: %w", err) + } + + out := append(withdrawableEpochProof, stateProof...) + out = append(out, blockHeaderProof.Hashes...) + + return out, nil +} + +func (state *BeaconState) blockHeaderToStateProof(blockHeader *generic.BeaconBlockHeader) ([][]byte, error) { + generalizedIndex := generic.BeaconBlockHeaderStateRootGeneralizedIndex + root, err := blockHeader.GetTree() + if err != nil { + return nil, fmt.Errorf("could not get block header tree: %w", err) + } + blockHeaderProof, err := root.Prove(int(generalizedIndex)) + if err != nil { + return nil, fmt.Errorf("could not get proof for block header: %w", err) + } + return blockHeaderProof.Hashes, nil +} + +// ValidatorCredentialsProof computes the merkle proof for a validator's credentials +// at a specific index in the validator registry. +func (state *BeaconState) ValidatorCredentialsProof(index uint64) ([][]byte, error) { + + if index >= uint64(len(state.Validators)) { + return nil, errors.New("validator index out of bounds") + } + + // Get the validator's credentials proof + credentialsProof, err := state.Validators[index].ValidatorCredentialsPubkeyProof() + if err != nil { + return nil, fmt.Errorf("could not get validator credentials proof: %w", err) + } + + stateProof, err := state.validatorStateProof(index) + if err != nil { + return nil, fmt.Errorf("could not get validator state proof: %w", err) + } + + // The EL proves against BeaconBlockHeader root, so we need to merge the state proof with that. + blockHeaderProof, err := state.blockHeaderToStateProof(state.LatestBlockHeader) + if err != nil { + return nil, fmt.Errorf("could not get block header proof: %w", err) + } + + out := append(credentialsProof, stateProof...) + out = append(out, blockHeaderProof...) + + return out, nil +} + +func (state *BeaconState) HistoricalSummaryProof(slot uint64) ([][]byte, error) { + isHistorical := slot+generic.SlotsPerHistoricalRoot <= state.Slot + if !isHistorical { + return nil, fmt.Errorf("slot %d is less than %d slots in the past from the state at slot %d, you must build a proof from the block_roots field instead", slot, generic.SlotsPerHistoricalRoot, state.Slot) + } + tree, err := state.GetTree() + if err != nil { + return nil, fmt.Errorf("could not get state tree: %w", err) + } + + // Navigate to the historical_summaries + gid := uint64(1) + gid = gid*beaconStateChunkCeil + generic.BeaconStateHistoricalSummariesFieldIndex + // Navigate into the historical summaries vector. + arrayIndex := (slot / generic.SlotsPerHistoricalRoot) + gid = gid*2*generic.BeaconStateHistoricalSummariesMaxLength + arrayIndex + + proof, err := tree.Prove(int(gid)) + if err != nil { + return nil, fmt.Errorf("could not get proof for historical block root: %w", err) + } + + // The EL proves against BeaconBlockHeader root, so we need to merge the state proof with that. + blockHeaderProof, err := state.blockHeaderToStateProof(state.LatestBlockHeader) + if err != nil { + return nil, fmt.Errorf("could not get block header proof: %w", err) + } + return append(proof.Hashes, blockHeaderProof...), nil +} + +func (state *BeaconState) HistoricalSummaryBlockRootProof(slot int) ([][]byte, error) { + // If the state isn't aligned at the end of an 8192 slot era, throw an error + if state.Slot%generic.SlotsPerHistoricalRoot != generic.SlotsPerHistoricalRoot-1 { + return nil, fmt.Errorf("state is not aligned at the end of an 8192 slot era") + } + + if slot < int(state.Slot)-int(generic.SlotsPerHistoricalRoot)-1 || slot+int(generic.SlotsPerHistoricalRoot)-1 >= int(state.Slot) { + return nil, fmt.Errorf("slot %d is out of range for historical summary proof", slot) + } + + hsls := generic.HistoricalSummaryLists{ + BlockRoots: state.BlockRoots, + StateRoots: state.StateRoots, + } + + idx := slot % int(generic.SlotsPerHistoricalRoot) + tree, err := hsls.GetTree() + if err != nil { + return nil, fmt.Errorf("could not get historical summary lists tree: %w", err) + } + + gid := uint64(1) + gid = gid * 2 // Now at block_roots + gid = gid * generic.SlotsPerHistoricalRoot // Now at the first block_root + gid = gid + uint64(idx) // Now at the correct block_root + + proof, err := tree.Prove(int(gid)) + if err != nil { + return nil, fmt.Errorf("could not get proof for historical summary: %w", err) + } + + return proof.Hashes, nil +} + +func (state *BeaconState) BlockRootProof(slot uint64) ([][]byte, error) { + isHistorical := slot+generic.SlotsPerHistoricalRoot <= state.Slot + if isHistorical { + return nil, fmt.Errorf("slot %d is more than %d slots in the past from the state at slot %d, you must build a proof from the historical_summaries instead", slot, generic.SlotsPerHistoricalRoot, state.Slot) + } + + tree, err := state.GetTree() + if err != nil { + return nil, fmt.Errorf("could not get state tree: %w", err) + } + + gid := uint64(1) + + // Navigate to the block_roots + gid = gid*beaconStateChunkCeil + generic.BeaconStateBlockRootsFieldIndex + + // We're now at the block_roots vector, which is the root of a slotsPerHistoricalRoot slots vector. + // The index we care about is given by slot % slotsPerHistoricalRoot. + gid = gid*generic.BeaconStateBlockRootsMaxLength + (slot % generic.SlotsPerHistoricalRoot) + + proof, err := tree.Prove(int(gid)) + if err != nil { + return nil, fmt.Errorf("could not get proof for block root: %w", err) + } + + // Finally, prove from the block header to the state root. + blockHeaderProof, err := state.blockHeaderToStateProof(state.LatestBlockHeader) + if err != nil { + return nil, fmt.Errorf("could not get block header proof: %w", err) + } + + return append(proof.Hashes, blockHeaderProof...), nil +} + +func (state *BeaconState) GetValidators() []*generic.Validator { + return state.Validators +} + +func (state *BeaconState) GetSlot() uint64 { + return state.Slot +} diff --git a/shared/types/eth2/fork/electra/state_electra_encoding.go b/shared/types/eth2/fork/electra/state_electra_encoding.go new file mode 100644 index 000000000..3d8adf111 --- /dev/null +++ b/shared/types/eth2/fork/electra/state_electra_encoding.go @@ -0,0 +1,1114 @@ +// Code generated by fastssz. DO NOT EDIT. +// Hash: 2e0f36372f8ac8d6eedeefe79607ce66518d0694ccd5c39d16be04be1dbc1e65 +// Version: 0.1.3 +package electra + +import ( + ssz "github.com/ferranbt/fastssz" + "github.com/rocket-pool/smartnode/shared/types/eth2/generic" +) + +// MarshalSSZ ssz marshals the BeaconStateElectra object +func (b *BeaconState) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(b) +} + +// MarshalSSZTo ssz marshals the BeaconStateElectra object to a target array +func (b *BeaconState) MarshalSSZTo(buf []byte) (dst []byte, err error) { + dst = buf + offset := int(2736713) + + // Field (0) 'GenesisTime' + dst = ssz.MarshalUint64(dst, b.GenesisTime) + + // Field (1) 'GenesisValidatorsRoot' + if size := len(b.GenesisValidatorsRoot); size != 32 { + err = ssz.ErrBytesLengthFn("BeaconStateElectra.GenesisValidatorsRoot", size, 32) + return + } + dst = append(dst, b.GenesisValidatorsRoot...) + + // Field (2) 'Slot' + dst = ssz.MarshalUint64(dst, b.Slot) + + // Field (3) 'Fork' + if b.Fork == nil { + b.Fork = new(generic.Fork) + } + if dst, err = b.Fork.MarshalSSZTo(dst); err != nil { + return + } + + // Field (4) 'LatestBlockHeader' + if b.LatestBlockHeader == nil { + b.LatestBlockHeader = new(generic.BeaconBlockHeader) + } + if dst, err = b.LatestBlockHeader.MarshalSSZTo(dst); err != nil { + return + } + + // Field (5) 'BlockRoots' + for ii := 0; ii < 8192; ii++ { + dst = append(dst, b.BlockRoots[ii][:]...) + } + + // Field (6) 'StateRoots' + for ii := 0; ii < 8192; ii++ { + dst = append(dst, b.StateRoots[ii][:]...) + } + + // Offset (7) 'HistoricalRoots' + dst = ssz.WriteOffset(dst, offset) + offset += len(b.HistoricalRoots) * 32 + + // Field (8) 'Eth1Data' + if b.Eth1Data == nil { + b.Eth1Data = new(generic.Eth1Data) + } + if dst, err = b.Eth1Data.MarshalSSZTo(dst); err != nil { + return + } + + // Offset (9) 'Eth1DataVotes' + dst = ssz.WriteOffset(dst, offset) + offset += len(b.Eth1DataVotes) * 72 + + // Field (10) 'Eth1DepositIndex' + dst = ssz.MarshalUint64(dst, b.Eth1DepositIndex) + + // Offset (11) 'Validators' + dst = ssz.WriteOffset(dst, offset) + offset += len(b.Validators) * 121 + + // Offset (12) 'Balances' + dst = ssz.WriteOffset(dst, offset) + offset += len(b.Balances) * 8 + + // Field (13) 'RandaoMixes' + if size := len(b.RandaoMixes); size != 65536 { + err = ssz.ErrVectorLengthFn("BeaconStateElectra.RandaoMixes", size, 65536) + return + } + for ii := 0; ii < 65536; ii++ { + if size := len(b.RandaoMixes[ii]); size != 32 { + err = ssz.ErrBytesLengthFn("BeaconStateElectra.RandaoMixes[ii]", size, 32) + return + } + dst = append(dst, b.RandaoMixes[ii]...) + } + + // Field (14) 'Slashings' + if size := len(b.Slashings); size != 8192 { + err = ssz.ErrVectorLengthFn("BeaconStateElectra.Slashings", size, 8192) + return + } + for ii := 0; ii < 8192; ii++ { + dst = ssz.MarshalUint64(dst, b.Slashings[ii]) + } + + // Offset (15) 'PreviousEpochParticipation' + dst = ssz.WriteOffset(dst, offset) + offset += len(b.PreviousEpochParticipation) + + // Offset (16) 'CurrentEpochParticipation' + dst = ssz.WriteOffset(dst, offset) + offset += len(b.CurrentEpochParticipation) + + // Field (17) 'JustificationBits' + dst = append(dst, b.JustificationBits[:]...) + + // Field (18) 'PreviousJustifiedCheckpoint' + if b.PreviousJustifiedCheckpoint == nil { + b.PreviousJustifiedCheckpoint = new(generic.Checkpoint) + } + if dst, err = b.PreviousJustifiedCheckpoint.MarshalSSZTo(dst); err != nil { + return + } + + // Field (19) 'CurrentJustifiedCheckpoint' + if b.CurrentJustifiedCheckpoint == nil { + b.CurrentJustifiedCheckpoint = new(generic.Checkpoint) + } + if dst, err = b.CurrentJustifiedCheckpoint.MarshalSSZTo(dst); err != nil { + return + } + + // Field (20) 'FinalizedCheckpoint' + if b.FinalizedCheckpoint == nil { + b.FinalizedCheckpoint = new(generic.Checkpoint) + } + if dst, err = b.FinalizedCheckpoint.MarshalSSZTo(dst); err != nil { + return + } + + // Offset (21) 'InactivityScores' + dst = ssz.WriteOffset(dst, offset) + offset += len(b.InactivityScores) * 8 + + // Field (22) 'CurrentSyncCommittee' + if b.CurrentSyncCommittee == nil { + b.CurrentSyncCommittee = new(generic.SyncCommittee) + } + if dst, err = b.CurrentSyncCommittee.MarshalSSZTo(dst); err != nil { + return + } + + // Field (23) 'NextSyncCommittee' + if b.NextSyncCommittee == nil { + b.NextSyncCommittee = new(generic.SyncCommittee) + } + if dst, err = b.NextSyncCommittee.MarshalSSZTo(dst); err != nil { + return + } + + // Offset (24) 'LatestExecutionPayloadHeader' + dst = ssz.WriteOffset(dst, offset) + if b.LatestExecutionPayloadHeader == nil { + b.LatestExecutionPayloadHeader = new(generic.ExecutionPayloadHeader) + } + offset += b.LatestExecutionPayloadHeader.SizeSSZ() + + // Field (25) 'NextWithdrawalIndex' + dst = ssz.MarshalUint64(dst, b.NextWithdrawalIndex) + + // Field (26) 'NextWithdrawalValidatorIndex' + dst = ssz.MarshalUint64(dst, b.NextWithdrawalValidatorIndex) + + // Offset (27) 'HistoricalSummaries' + dst = ssz.WriteOffset(dst, offset) + offset += len(b.HistoricalSummaries) * 64 + + // Field (28) 'DepositRequestsStartIndex' + dst = ssz.MarshalUint64(dst, b.DepositRequestsStartIndex) + + // Field (29) 'DepositBalanceToConsume' + dst = ssz.MarshalUint64(dst, b.DepositBalanceToConsume) + + // Field (30) 'ExitBalanceToConsume' + dst = ssz.MarshalUint64(dst, b.ExitBalanceToConsume) + + // Field (31) 'EarliestExitEpoch' + dst = ssz.MarshalUint64(dst, b.EarliestExitEpoch) + + // Field (32) 'ConsolidationBalanceToConsume' + dst = ssz.MarshalUint64(dst, b.ConsolidationBalanceToConsume) + + // Field (33) 'EarliestConsolidationEpoch' + dst = ssz.MarshalUint64(dst, b.EarliestConsolidationEpoch) + + // Offset (34) 'PendingDeposits' + dst = ssz.WriteOffset(dst, offset) + offset += len(b.PendingDeposits) * 192 + + // Offset (35) 'PendingPartialWithdrawals' + dst = ssz.WriteOffset(dst, offset) + offset += len(b.PendingPartialWithdrawals) * 24 + + // Offset (36) 'PendingConsolidations' + dst = ssz.WriteOffset(dst, offset) + + // Field (7) 'HistoricalRoots' + if size := len(b.HistoricalRoots); size > 16777216 { + err = ssz.ErrListTooBigFn("BeaconStateElectra.HistoricalRoots", size, 16777216) + return + } + for ii := 0; ii < len(b.HistoricalRoots); ii++ { + if size := len(b.HistoricalRoots[ii]); size != 32 { + err = ssz.ErrBytesLengthFn("BeaconStateElectra.HistoricalRoots[ii]", size, 32) + return + } + dst = append(dst, b.HistoricalRoots[ii]...) + } + + // Field (9) 'Eth1DataVotes' + if size := len(b.Eth1DataVotes); size > 2048 { + err = ssz.ErrListTooBigFn("BeaconStateElectra.Eth1DataVotes", size, 2048) + return + } + for ii := 0; ii < len(b.Eth1DataVotes); ii++ { + if dst, err = b.Eth1DataVotes[ii].MarshalSSZTo(dst); err != nil { + return + } + } + + // Field (11) 'Validators' + if size := len(b.Validators); size > 1099511627776 { + err = ssz.ErrListTooBigFn("BeaconStateElectra.Validators", size, 1099511627776) + return + } + for ii := 0; ii < len(b.Validators); ii++ { + if dst, err = b.Validators[ii].MarshalSSZTo(dst); err != nil { + return + } + } + + // Field (12) 'Balances' + if size := len(b.Balances); size > 1099511627776 { + err = ssz.ErrListTooBigFn("BeaconStateElectra.Balances", size, 1099511627776) + return + } + for ii := 0; ii < len(b.Balances); ii++ { + dst = ssz.MarshalUint64(dst, b.Balances[ii]) + } + + // Field (15) 'PreviousEpochParticipation' + if size := len(b.PreviousEpochParticipation); size > 1099511627776 { + err = ssz.ErrBytesLengthFn("BeaconStateElectra.PreviousEpochParticipation", size, 1099511627776) + return + } + dst = append(dst, b.PreviousEpochParticipation...) + + // Field (16) 'CurrentEpochParticipation' + if size := len(b.CurrentEpochParticipation); size > 1099511627776 { + err = ssz.ErrBytesLengthFn("BeaconStateElectra.CurrentEpochParticipation", size, 1099511627776) + return + } + dst = append(dst, b.CurrentEpochParticipation...) + + // Field (21) 'InactivityScores' + if size := len(b.InactivityScores); size > 1099511627776 { + err = ssz.ErrListTooBigFn("BeaconStateElectra.InactivityScores", size, 1099511627776) + return + } + for ii := 0; ii < len(b.InactivityScores); ii++ { + dst = ssz.MarshalUint64(dst, b.InactivityScores[ii]) + } + + // Field (24) 'LatestExecutionPayloadHeader' + if dst, err = b.LatestExecutionPayloadHeader.MarshalSSZTo(dst); err != nil { + return + } + + // Field (27) 'HistoricalSummaries' + if size := len(b.HistoricalSummaries); size > 16777216 { + err = ssz.ErrListTooBigFn("BeaconStateElectra.HistoricalSummaries", size, 16777216) + return + } + for ii := 0; ii < len(b.HistoricalSummaries); ii++ { + if dst, err = b.HistoricalSummaries[ii].MarshalSSZTo(dst); err != nil { + return + } + } + + // Field (34) 'PendingDeposits' + if size := len(b.PendingDeposits); size > 134217728 { + err = ssz.ErrListTooBigFn("BeaconStateElectra.PendingDeposits", size, 134217728) + return + } + for ii := 0; ii < len(b.PendingDeposits); ii++ { + if dst, err = b.PendingDeposits[ii].MarshalSSZTo(dst); err != nil { + return + } + } + + // Field (35) 'PendingPartialWithdrawals' + if size := len(b.PendingPartialWithdrawals); size > 134217728 { + err = ssz.ErrListTooBigFn("BeaconStateElectra.PendingPartialWithdrawals", size, 134217728) + return + } + for ii := 0; ii < len(b.PendingPartialWithdrawals); ii++ { + if dst, err = b.PendingPartialWithdrawals[ii].MarshalSSZTo(dst); err != nil { + return + } + } + + // Field (36) 'PendingConsolidations' + if size := len(b.PendingConsolidations); size > 262144 { + err = ssz.ErrListTooBigFn("BeaconStateElectra.PendingConsolidations", size, 262144) + return + } + for ii := 0; ii < len(b.PendingConsolidations); ii++ { + if dst, err = b.PendingConsolidations[ii].MarshalSSZTo(dst); err != nil { + return + } + } + + return +} + +// UnmarshalSSZ ssz unmarshals the BeaconStateElectra object +func (b *BeaconState) UnmarshalSSZ(buf []byte) error { + var err error + size := uint64(len(buf)) + if size < 2736713 { + return ssz.ErrSize + } + + tail := buf + var o7, o9, o11, o12, o15, o16, o21, o24, o27, o34, o35, o36 uint64 + + // Field (0) 'GenesisTime' + b.GenesisTime = ssz.UnmarshallUint64(buf[0:8]) + + // Field (1) 'GenesisValidatorsRoot' + if cap(b.GenesisValidatorsRoot) == 0 { + b.GenesisValidatorsRoot = make([]byte, 0, len(buf[8:40])) + } + b.GenesisValidatorsRoot = append(b.GenesisValidatorsRoot, buf[8:40]...) + + // Field (2) 'Slot' + b.Slot = ssz.UnmarshallUint64(buf[40:48]) + + // Field (3) 'Fork' + if b.Fork == nil { + b.Fork = new(generic.Fork) + } + if err = b.Fork.UnmarshalSSZ(buf[48:64]); err != nil { + return err + } + + // Field (4) 'LatestBlockHeader' + if b.LatestBlockHeader == nil { + b.LatestBlockHeader = new(generic.BeaconBlockHeader) + } + if err = b.LatestBlockHeader.UnmarshalSSZ(buf[64:176]); err != nil { + return err + } + + // Field (5) 'BlockRoots' + + for ii := 0; ii < 8192; ii++ { + copy(b.BlockRoots[ii][:], buf[176:262320][ii*32:(ii+1)*32]) + } + + // Field (6) 'StateRoots' + + for ii := 0; ii < 8192; ii++ { + copy(b.StateRoots[ii][:], buf[262320:524464][ii*32:(ii+1)*32]) + } + + // Offset (7) 'HistoricalRoots' + if o7 = ssz.ReadOffset(buf[524464:524468]); o7 > size { + return ssz.ErrOffset + } + + if o7 != 2736713 { + return ssz.ErrInvalidVariableOffset + } + + // Field (8) 'Eth1Data' + if b.Eth1Data == nil { + b.Eth1Data = new(generic.Eth1Data) + } + if err = b.Eth1Data.UnmarshalSSZ(buf[524468:524540]); err != nil { + return err + } + + // Offset (9) 'Eth1DataVotes' + if o9 = ssz.ReadOffset(buf[524540:524544]); o9 > size || o7 > o9 { + return ssz.ErrOffset + } + + // Field (10) 'Eth1DepositIndex' + b.Eth1DepositIndex = ssz.UnmarshallUint64(buf[524544:524552]) + + // Offset (11) 'Validators' + if o11 = ssz.ReadOffset(buf[524552:524556]); o11 > size || o9 > o11 { + return ssz.ErrOffset + } + + // Offset (12) 'Balances' + if o12 = ssz.ReadOffset(buf[524556:524560]); o12 > size || o11 > o12 { + return ssz.ErrOffset + } + + // Field (13) 'RandaoMixes' + b.RandaoMixes = make([][]byte, 65536) + for ii := 0; ii < 65536; ii++ { + if cap(b.RandaoMixes[ii]) == 0 { + b.RandaoMixes[ii] = make([]byte, 0, len(buf[524560:2621712][ii*32:(ii+1)*32])) + } + b.RandaoMixes[ii] = append(b.RandaoMixes[ii], buf[524560:2621712][ii*32:(ii+1)*32]...) + } + + // Field (14) 'Slashings' + b.Slashings = ssz.ExtendUint64(b.Slashings, 8192) + for ii := 0; ii < 8192; ii++ { + b.Slashings[ii] = ssz.UnmarshallUint64(buf[2621712:2687248][ii*8 : (ii+1)*8]) + } + + // Offset (15) 'PreviousEpochParticipation' + if o15 = ssz.ReadOffset(buf[2687248:2687252]); o15 > size || o12 > o15 { + return ssz.ErrOffset + } + + // Offset (16) 'CurrentEpochParticipation' + if o16 = ssz.ReadOffset(buf[2687252:2687256]); o16 > size || o15 > o16 { + return ssz.ErrOffset + } + + // Field (17) 'JustificationBits' + copy(b.JustificationBits[:], buf[2687256:2687257]) + + // Field (18) 'PreviousJustifiedCheckpoint' + if b.PreviousJustifiedCheckpoint == nil { + b.PreviousJustifiedCheckpoint = new(generic.Checkpoint) + } + if err = b.PreviousJustifiedCheckpoint.UnmarshalSSZ(buf[2687257:2687297]); err != nil { + return err + } + + // Field (19) 'CurrentJustifiedCheckpoint' + if b.CurrentJustifiedCheckpoint == nil { + b.CurrentJustifiedCheckpoint = new(generic.Checkpoint) + } + if err = b.CurrentJustifiedCheckpoint.UnmarshalSSZ(buf[2687297:2687337]); err != nil { + return err + } + + // Field (20) 'FinalizedCheckpoint' + if b.FinalizedCheckpoint == nil { + b.FinalizedCheckpoint = new(generic.Checkpoint) + } + if err = b.FinalizedCheckpoint.UnmarshalSSZ(buf[2687337:2687377]); err != nil { + return err + } + + // Offset (21) 'InactivityScores' + if o21 = ssz.ReadOffset(buf[2687377:2687381]); o21 > size || o16 > o21 { + return ssz.ErrOffset + } + + // Field (22) 'CurrentSyncCommittee' + if b.CurrentSyncCommittee == nil { + b.CurrentSyncCommittee = new(generic.SyncCommittee) + } + if err = b.CurrentSyncCommittee.UnmarshalSSZ(buf[2687381:2712005]); err != nil { + return err + } + + // Field (23) 'NextSyncCommittee' + if b.NextSyncCommittee == nil { + b.NextSyncCommittee = new(generic.SyncCommittee) + } + if err = b.NextSyncCommittee.UnmarshalSSZ(buf[2712005:2736629]); err != nil { + return err + } + + // Offset (24) 'LatestExecutionPayloadHeader' + if o24 = ssz.ReadOffset(buf[2736629:2736633]); o24 > size || o21 > o24 { + return ssz.ErrOffset + } + + // Field (25) 'NextWithdrawalIndex' + b.NextWithdrawalIndex = ssz.UnmarshallUint64(buf[2736633:2736641]) + + // Field (26) 'NextWithdrawalValidatorIndex' + b.NextWithdrawalValidatorIndex = ssz.UnmarshallUint64(buf[2736641:2736649]) + + // Offset (27) 'HistoricalSummaries' + if o27 = ssz.ReadOffset(buf[2736649:2736653]); o27 > size || o24 > o27 { + return ssz.ErrOffset + } + + // Field (28) 'DepositRequestsStartIndex' + b.DepositRequestsStartIndex = ssz.UnmarshallUint64(buf[2736653:2736661]) + + // Field (29) 'DepositBalanceToConsume' + b.DepositBalanceToConsume = ssz.UnmarshallUint64(buf[2736661:2736669]) + + // Field (30) 'ExitBalanceToConsume' + b.ExitBalanceToConsume = ssz.UnmarshallUint64(buf[2736669:2736677]) + + // Field (31) 'EarliestExitEpoch' + b.EarliestExitEpoch = ssz.UnmarshallUint64(buf[2736677:2736685]) + + // Field (32) 'ConsolidationBalanceToConsume' + b.ConsolidationBalanceToConsume = ssz.UnmarshallUint64(buf[2736685:2736693]) + + // Field (33) 'EarliestConsolidationEpoch' + b.EarliestConsolidationEpoch = ssz.UnmarshallUint64(buf[2736693:2736701]) + + // Offset (34) 'PendingDeposits' + if o34 = ssz.ReadOffset(buf[2736701:2736705]); o34 > size || o27 > o34 { + return ssz.ErrOffset + } + + // Offset (35) 'PendingPartialWithdrawals' + if o35 = ssz.ReadOffset(buf[2736705:2736709]); o35 > size || o34 > o35 { + return ssz.ErrOffset + } + + // Offset (36) 'PendingConsolidations' + if o36 = ssz.ReadOffset(buf[2736709:2736713]); o36 > size || o35 > o36 { + return ssz.ErrOffset + } + + // Field (7) 'HistoricalRoots' + { + buf = tail[o7:o9] + num, err := ssz.DivideInt2(len(buf), 32, 16777216) + if err != nil { + return err + } + b.HistoricalRoots = make([][]byte, num) + for ii := 0; ii < num; ii++ { + if cap(b.HistoricalRoots[ii]) == 0 { + b.HistoricalRoots[ii] = make([]byte, 0, len(buf[ii*32:(ii+1)*32])) + } + b.HistoricalRoots[ii] = append(b.HistoricalRoots[ii], buf[ii*32:(ii+1)*32]...) + } + } + + // Field (9) 'Eth1DataVotes' + { + buf = tail[o9:o11] + num, err := ssz.DivideInt2(len(buf), 72, 2048) + if err != nil { + return err + } + b.Eth1DataVotes = make([]*generic.Eth1Data, num) + for ii := 0; ii < num; ii++ { + if b.Eth1DataVotes[ii] == nil { + b.Eth1DataVotes[ii] = new(generic.Eth1Data) + } + if err = b.Eth1DataVotes[ii].UnmarshalSSZ(buf[ii*72 : (ii+1)*72]); err != nil { + return err + } + } + } + + // Field (11) 'Validators' + { + buf = tail[o11:o12] + num, err := ssz.DivideInt2(len(buf), 121, 1099511627776) + if err != nil { + return err + } + b.Validators = make([]*generic.Validator, num) + for ii := 0; ii < num; ii++ { + if b.Validators[ii] == nil { + b.Validators[ii] = new(generic.Validator) + } + if err = b.Validators[ii].UnmarshalSSZ(buf[ii*121 : (ii+1)*121]); err != nil { + return err + } + } + } + + // Field (12) 'Balances' + { + buf = tail[o12:o15] + num, err := ssz.DivideInt2(len(buf), 8, 1099511627776) + if err != nil { + return err + } + b.Balances = ssz.ExtendUint64(b.Balances, num) + for ii := 0; ii < num; ii++ { + b.Balances[ii] = ssz.UnmarshallUint64(buf[ii*8 : (ii+1)*8]) + } + } + + // Field (15) 'PreviousEpochParticipation' + { + buf = tail[o15:o16] + if len(buf) > 1099511627776 { + return ssz.ErrBytesLength + } + if cap(b.PreviousEpochParticipation) == 0 { + b.PreviousEpochParticipation = make([]byte, 0, len(buf)) + } + b.PreviousEpochParticipation = append(b.PreviousEpochParticipation, buf...) + } + + // Field (16) 'CurrentEpochParticipation' + { + buf = tail[o16:o21] + if len(buf) > 1099511627776 { + return ssz.ErrBytesLength + } + if cap(b.CurrentEpochParticipation) == 0 { + b.CurrentEpochParticipation = make([]byte, 0, len(buf)) + } + b.CurrentEpochParticipation = append(b.CurrentEpochParticipation, buf...) + } + + // Field (21) 'InactivityScores' + { + buf = tail[o21:o24] + num, err := ssz.DivideInt2(len(buf), 8, 1099511627776) + if err != nil { + return err + } + b.InactivityScores = ssz.ExtendUint64(b.InactivityScores, num) + for ii := 0; ii < num; ii++ { + b.InactivityScores[ii] = ssz.UnmarshallUint64(buf[ii*8 : (ii+1)*8]) + } + } + + // Field (24) 'LatestExecutionPayloadHeader' + { + buf = tail[o24:o27] + if b.LatestExecutionPayloadHeader == nil { + b.LatestExecutionPayloadHeader = new(generic.ExecutionPayloadHeader) + } + if err = b.LatestExecutionPayloadHeader.UnmarshalSSZ(buf); err != nil { + return err + } + } + + // Field (27) 'HistoricalSummaries' + { + buf = tail[o27:o34] + num, err := ssz.DivideInt2(len(buf), 64, 16777216) + if err != nil { + return err + } + b.HistoricalSummaries = make([]*generic.HistoricalSummary, num) + for ii := 0; ii < num; ii++ { + if b.HistoricalSummaries[ii] == nil { + b.HistoricalSummaries[ii] = new(generic.HistoricalSummary) + } + if err = b.HistoricalSummaries[ii].UnmarshalSSZ(buf[ii*64 : (ii+1)*64]); err != nil { + return err + } + } + } + + // Field (34) 'PendingDeposits' + { + buf = tail[o34:o35] + num, err := ssz.DivideInt2(len(buf), 192, 134217728) + if err != nil { + return err + } + b.PendingDeposits = make([]*generic.PendingDeposit, num) + for ii := 0; ii < num; ii++ { + if b.PendingDeposits[ii] == nil { + b.PendingDeposits[ii] = new(generic.PendingDeposit) + } + if err = b.PendingDeposits[ii].UnmarshalSSZ(buf[ii*192 : (ii+1)*192]); err != nil { + return err + } + } + } + + // Field (35) 'PendingPartialWithdrawals' + { + buf = tail[o35:o36] + num, err := ssz.DivideInt2(len(buf), 24, 134217728) + if err != nil { + return err + } + b.PendingPartialWithdrawals = make([]*generic.PendingPartialWithdrawal, num) + for ii := 0; ii < num; ii++ { + if b.PendingPartialWithdrawals[ii] == nil { + b.PendingPartialWithdrawals[ii] = new(generic.PendingPartialWithdrawal) + } + if err = b.PendingPartialWithdrawals[ii].UnmarshalSSZ(buf[ii*24 : (ii+1)*24]); err != nil { + return err + } + } + } + + // Field (36) 'PendingConsolidations' + { + buf = tail[o36:] + num, err := ssz.DivideInt2(len(buf), 16, 262144) + if err != nil { + return err + } + b.PendingConsolidations = make([]*generic.PendingConsolidation, num) + for ii := 0; ii < num; ii++ { + if b.PendingConsolidations[ii] == nil { + b.PendingConsolidations[ii] = new(generic.PendingConsolidation) + } + if err = b.PendingConsolidations[ii].UnmarshalSSZ(buf[ii*16 : (ii+1)*16]); err != nil { + return err + } + } + } + return err +} + +// SizeSSZ returns the ssz encoded size in bytes for the BeaconStateElectra object +func (b *BeaconState) SizeSSZ() (size int) { + size = 2736713 + + // Field (7) 'HistoricalRoots' + size += len(b.HistoricalRoots) * 32 + + // Field (9) 'Eth1DataVotes' + size += len(b.Eth1DataVotes) * 72 + + // Field (11) 'Validators' + size += len(b.Validators) * 121 + + // Field (12) 'Balances' + size += len(b.Balances) * 8 + + // Field (15) 'PreviousEpochParticipation' + size += len(b.PreviousEpochParticipation) + + // Field (16) 'CurrentEpochParticipation' + size += len(b.CurrentEpochParticipation) + + // Field (21) 'InactivityScores' + size += len(b.InactivityScores) * 8 + + // Field (24) 'LatestExecutionPayloadHeader' + if b.LatestExecutionPayloadHeader == nil { + b.LatestExecutionPayloadHeader = new(generic.ExecutionPayloadHeader) + } + size += b.LatestExecutionPayloadHeader.SizeSSZ() + + // Field (27) 'HistoricalSummaries' + size += len(b.HistoricalSummaries) * 64 + + // Field (34) 'PendingDeposits' + size += len(b.PendingDeposits) * 192 + + // Field (35) 'PendingPartialWithdrawals' + size += len(b.PendingPartialWithdrawals) * 24 + + // Field (36) 'PendingConsolidations' + size += len(b.PendingConsolidations) * 16 + + return +} + +// HashTreeRoot ssz hashes the BeaconStateElectra object +func (b *BeaconState) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(b) +} + +// HashTreeRootWith ssz hashes the BeaconStateElectra object with a hasher +func (b *BeaconState) HashTreeRootWith(hh ssz.HashWalker) (err error) { + indx := hh.Index() + + // Field (0) 'GenesisTime' + hh.PutUint64(b.GenesisTime) + + // Field (1) 'GenesisValidatorsRoot' + if size := len(b.GenesisValidatorsRoot); size != 32 { + err = ssz.ErrBytesLengthFn("BeaconStateElectra.GenesisValidatorsRoot", size, 32) + return + } + hh.PutBytes(b.GenesisValidatorsRoot) + + // Field (2) 'Slot' + hh.PutUint64(b.Slot) + + // Field (3) 'Fork' + if b.Fork == nil { + b.Fork = new(generic.Fork) + } + if err = b.Fork.HashTreeRootWith(hh); err != nil { + return + } + + // Field (4) 'LatestBlockHeader' + if b.LatestBlockHeader == nil { + b.LatestBlockHeader = new(generic.BeaconBlockHeader) + } + if err = b.LatestBlockHeader.HashTreeRootWith(hh); err != nil { + return + } + + // Field (5) 'BlockRoots' + { + subIndx := hh.Index() + for _, i := range b.BlockRoots { + hh.Append(i[:]) + } + hh.Merkleize(subIndx) + } + + // Field (6) 'StateRoots' + { + subIndx := hh.Index() + for _, i := range b.StateRoots { + hh.Append(i[:]) + } + hh.Merkleize(subIndx) + } + + // Field (7) 'HistoricalRoots' + { + if size := len(b.HistoricalRoots); size > 16777216 { + err = ssz.ErrListTooBigFn("BeaconStateElectra.HistoricalRoots", size, 16777216) + return + } + subIndx := hh.Index() + for _, i := range b.HistoricalRoots { + if len(i) != 32 { + err = ssz.ErrBytesLength + return + } + hh.Append(i) + } + numItems := uint64(len(b.HistoricalRoots)) + hh.MerkleizeWithMixin(subIndx, numItems, 16777216) + } + + // Field (8) 'Eth1Data' + if b.Eth1Data == nil { + b.Eth1Data = new(generic.Eth1Data) + } + if err = b.Eth1Data.HashTreeRootWith(hh); err != nil { + return + } + + // Field (9) 'Eth1DataVotes' + { + subIndx := hh.Index() + num := uint64(len(b.Eth1DataVotes)) + if num > 2048 { + err = ssz.ErrIncorrectListSize + return + } + for _, elem := range b.Eth1DataVotes { + if err = elem.HashTreeRootWith(hh); err != nil { + return + } + } + hh.MerkleizeWithMixin(subIndx, num, 2048) + } + + // Field (10) 'Eth1DepositIndex' + hh.PutUint64(b.Eth1DepositIndex) + + // Field (11) 'Validators' + { + subIndx := hh.Index() + num := uint64(len(b.Validators)) + if num > 1099511627776 { + err = ssz.ErrIncorrectListSize + return + } + for _, elem := range b.Validators { + if err = elem.HashTreeRootWith(hh); err != nil { + return + } + } + hh.MerkleizeWithMixin(subIndx, num, 1099511627776) + } + + // Field (12) 'Balances' + { + if size := len(b.Balances); size > 1099511627776 { + err = ssz.ErrListTooBigFn("BeaconStateElectra.Balances", size, 1099511627776) + return + } + subIndx := hh.Index() + for _, i := range b.Balances { + hh.AppendUint64(i) + } + hh.FillUpTo32() + numItems := uint64(len(b.Balances)) + hh.MerkleizeWithMixin(subIndx, numItems, ssz.CalculateLimit(1099511627776, numItems, 8)) + } + + // Field (13) 'RandaoMixes' + { + if size := len(b.RandaoMixes); size != 65536 { + err = ssz.ErrVectorLengthFn("BeaconStateElectra.RandaoMixes", size, 65536) + return + } + subIndx := hh.Index() + for _, i := range b.RandaoMixes { + if len(i) != 32 { + err = ssz.ErrBytesLength + return + } + hh.Append(i) + } + hh.Merkleize(subIndx) + } + + // Field (14) 'Slashings' + { + if size := len(b.Slashings); size != 8192 { + err = ssz.ErrVectorLengthFn("BeaconStateElectra.Slashings", size, 8192) + return + } + subIndx := hh.Index() + for _, i := range b.Slashings { + hh.AppendUint64(i) + } + hh.Merkleize(subIndx) + } + + // Field (15) 'PreviousEpochParticipation' + { + elemIndx := hh.Index() + byteLen := uint64(len(b.PreviousEpochParticipation)) + if byteLen > 1099511627776 { + err = ssz.ErrIncorrectListSize + return + } + hh.Append(b.PreviousEpochParticipation) + hh.MerkleizeWithMixin(elemIndx, byteLen, (1099511627776+31)/32) + } + + // Field (16) 'CurrentEpochParticipation' + { + elemIndx := hh.Index() + byteLen := uint64(len(b.CurrentEpochParticipation)) + if byteLen > 1099511627776 { + err = ssz.ErrIncorrectListSize + return + } + hh.Append(b.CurrentEpochParticipation) + hh.MerkleizeWithMixin(elemIndx, byteLen, (1099511627776+31)/32) + } + + // Field (17) 'JustificationBits' + hh.PutBytes(b.JustificationBits[:]) + + // Field (18) 'PreviousJustifiedCheckpoint' + if b.PreviousJustifiedCheckpoint == nil { + b.PreviousJustifiedCheckpoint = new(generic.Checkpoint) + } + if err = b.PreviousJustifiedCheckpoint.HashTreeRootWith(hh); err != nil { + return + } + + // Field (19) 'CurrentJustifiedCheckpoint' + if b.CurrentJustifiedCheckpoint == nil { + b.CurrentJustifiedCheckpoint = new(generic.Checkpoint) + } + if err = b.CurrentJustifiedCheckpoint.HashTreeRootWith(hh); err != nil { + return + } + + // Field (20) 'FinalizedCheckpoint' + if b.FinalizedCheckpoint == nil { + b.FinalizedCheckpoint = new(generic.Checkpoint) + } + if err = b.FinalizedCheckpoint.HashTreeRootWith(hh); err != nil { + return + } + + // Field (21) 'InactivityScores' + { + if size := len(b.InactivityScores); size > 1099511627776 { + err = ssz.ErrListTooBigFn("BeaconStateElectra.InactivityScores", size, 1099511627776) + return + } + subIndx := hh.Index() + for _, i := range b.InactivityScores { + hh.AppendUint64(i) + } + hh.FillUpTo32() + numItems := uint64(len(b.InactivityScores)) + hh.MerkleizeWithMixin(subIndx, numItems, ssz.CalculateLimit(1099511627776, numItems, 8)) + } + + // Field (22) 'CurrentSyncCommittee' + if b.CurrentSyncCommittee == nil { + b.CurrentSyncCommittee = new(generic.SyncCommittee) + } + if err = b.CurrentSyncCommittee.HashTreeRootWith(hh); err != nil { + return + } + + // Field (23) 'NextSyncCommittee' + if b.NextSyncCommittee == nil { + b.NextSyncCommittee = new(generic.SyncCommittee) + } + if err = b.NextSyncCommittee.HashTreeRootWith(hh); err != nil { + return + } + + // Field (24) 'LatestExecutionPayloadHeader' + if err = b.LatestExecutionPayloadHeader.HashTreeRootWith(hh); err != nil { + return + } + + // Field (25) 'NextWithdrawalIndex' + hh.PutUint64(b.NextWithdrawalIndex) + + // Field (26) 'NextWithdrawalValidatorIndex' + hh.PutUint64(b.NextWithdrawalValidatorIndex) + + // Field (27) 'HistoricalSummaries' + { + subIndx := hh.Index() + num := uint64(len(b.HistoricalSummaries)) + if num > 16777216 { + err = ssz.ErrIncorrectListSize + return + } + for _, elem := range b.HistoricalSummaries { + if err = elem.HashTreeRootWith(hh); err != nil { + return + } + } + hh.MerkleizeWithMixin(subIndx, num, 16777216) + } + + // Field (28) 'DepositRequestsStartIndex' + hh.PutUint64(b.DepositRequestsStartIndex) + + // Field (29) 'DepositBalanceToConsume' + hh.PutUint64(b.DepositBalanceToConsume) + + // Field (30) 'ExitBalanceToConsume' + hh.PutUint64(b.ExitBalanceToConsume) + + // Field (31) 'EarliestExitEpoch' + hh.PutUint64(b.EarliestExitEpoch) + + // Field (32) 'ConsolidationBalanceToConsume' + hh.PutUint64(b.ConsolidationBalanceToConsume) + + // Field (33) 'EarliestConsolidationEpoch' + hh.PutUint64(b.EarliestConsolidationEpoch) + + // Field (34) 'PendingDeposits' + { + subIndx := hh.Index() + num := uint64(len(b.PendingDeposits)) + if num > 134217728 { + err = ssz.ErrIncorrectListSize + return + } + for _, elem := range b.PendingDeposits { + if err = elem.HashTreeRootWith(hh); err != nil { + return + } + } + hh.MerkleizeWithMixin(subIndx, num, 134217728) + } + + // Field (35) 'PendingPartialWithdrawals' + { + subIndx := hh.Index() + num := uint64(len(b.PendingPartialWithdrawals)) + if num > 134217728 { + err = ssz.ErrIncorrectListSize + return + } + for _, elem := range b.PendingPartialWithdrawals { + if err = elem.HashTreeRootWith(hh); err != nil { + return + } + } + hh.MerkleizeWithMixin(subIndx, num, 134217728) + } + + // Field (36) 'PendingConsolidations' + { + subIndx := hh.Index() + num := uint64(len(b.PendingConsolidations)) + if num > 262144 { + err = ssz.ErrIncorrectListSize + return + } + for _, elem := range b.PendingConsolidations { + if err = elem.HashTreeRootWith(hh); err != nil { + return + } + } + hh.MerkleizeWithMixin(subIndx, num, 262144) + } + + hh.Merkleize(indx) + return +} + +// GetTree ssz hashes the BeaconStateElectra object +func (b *BeaconState) GetTree() (*ssz.Node, error) { + return ssz.ProofTree(b) +} diff --git a/shared/types/eth2/generic/block.go b/shared/types/eth2/generic/block.go new file mode 100644 index 000000000..ff9b0a94e --- /dev/null +++ b/shared/types/eth2/generic/block.go @@ -0,0 +1,96 @@ +package generic + +const BeaconBlockChunksCeil uint64 = 8 +const BeaconBlockBodyIndex uint64 = 4 +const BeaconBlockBodyExecutionPayloadIndex uint64 = 9 +const BeaconBlockBodyExecutionPayloadChunksCeil uint64 = 32 +const BeaconBlockBodyExecutionPayloadWithdrawalsIndex uint64 = 14 +const BeaconBlockWithdrawalsArrayMax uint64 = 16 + +type ProposerSlashing struct { + Header1 *SignedBeaconBlockHeader `json:"signed_header_1"` + Header2 *SignedBeaconBlockHeader `json:"signed_header_2"` +} + +type SignedBeaconBlockHeader struct { + Header *BeaconBlockHeader `json:"message"` + Signature []byte `json:"signature" ssz-size:"96"` +} + +type AttesterSlashing struct { + Attestation1 *IndexedAttestation `json:"attestation_1"` + Attestation2 *IndexedAttestation `json:"attestation_2"` +} + +type IndexedAttestation struct { + AttestationIndices []uint64 `json:"attesting_indices" ssz-max:"2048"` + Data *AttestationData `json:"data"` + Signature []byte `json:"signature" ssz-size:"96"` +} + +type AttestationData struct { + Slot uint64 `json:"slot"` + Index uint64 `json:"index"` + BeaconBlockHash [32]byte `json:"beacon_block_root" ssz-size:"32"` + Source *Checkpoint `json:"source"` + Target *Checkpoint `json:"target"` +} + +type Attestation struct { + AggregationBits []byte `json:"aggregation_bits" ssz:"bitlist" ssz-max:"2048"` + Data *AttestationData `json:"data"` + Signature [96]byte `json:"signature" ssz-size:"96"` +} + +type Deposit struct { + Proof [][]byte `ssz-size:"33,32"` + Data *DepositData +} + +type SignedVoluntaryExit struct { + Exit *VoluntaryExit `json:"message"` + Signature [96]byte `json:"signature" ssz-size:"96"` +} + +type SyncAggregate struct { + SyncCommiteeBits []byte `json:"sync_committee_bits" ssz-size:"64"` + SyncCommiteeSignature [96]byte `json:"sync_committee_signature" ssz-size:"96"` +} + +type ExecutionPayload struct { + ParentHash [32]byte `ssz-size:"32" json:"parent_hash"` + FeeRecipient [20]byte `ssz-size:"20" json:"fee_recipient"` + StateRoot [32]byte `ssz-size:"32" json:"state_root"` + ReceiptsRoot [32]byte `ssz-size:"32" json:"receipts_root"` + LogsBloom [256]byte `ssz-size:"256" json:"logs_bloom"` + PrevRandao [32]byte `ssz-size:"32" json:"prev_randao"` + BlockNumber uint64 `json:"block_number"` + GasLimit uint64 `json:"gas_limit"` + GasUsed uint64 `json:"gas_used"` + Timestamp uint64 `json:"timestamp"` + ExtraData []byte `ssz-max:"32" json:"extra_data"` + BaseFeePerGas Uint256 `ssz-size:"32" json:"base_fee_per_gas"` + BlockHash [32]byte `ssz-size:"32" json:"block_hash"` + Transactions [][]byte `ssz-max:"1048576,1073741824" ssz-size:"?,?" json:"transactions"` + Withdrawals []*Withdrawal `json:"withdrawals" ssz-max:"16"` + BlobGasUsed uint64 `json:"blob_gas_used"` + ExcessBlobGas uint64 `json:"excess_blob_gas"` +} + +type Withdrawal struct { + Index uint64 `json:"index"` + ValidatorIndex uint64 `json:"validator_index"` + Address [20]byte `json:"address" ssz-size:"20"` + Amount uint64 `json:"amount"` +} + +type BLSToExecutionChange struct { + ValidatorIndex uint64 `json:"validator_index"` + FromBLSPubKey [48]byte `json:"from_bls_pubkey" ssz-size:"48"` + ToExecutionAddress [20]byte `json:"to_execution_address" ssz-size:"20"` +} + +type SignedBLSToExecutionChange struct { + Message *BLSToExecutionChange `json:"message"` + Signature [96]byte `json:"signature" ssz-size:"96"` +} diff --git a/shared/types/eth2/generic/block_encoding.go b/shared/types/eth2/generic/block_encoding.go new file mode 100644 index 000000000..944481e27 --- /dev/null +++ b/shared/types/eth2/generic/block_encoding.go @@ -0,0 +1,1584 @@ +// Code generated by fastssz. DO NOT EDIT. +// Hash: 3fba9f7b919dd4fa010d06a5c0b339f6a3643424cec6377e930e2b3569a71338 +// Version: 0.1.3 +package generic + +import ( + ssz "github.com/ferranbt/fastssz" +) + +// MarshalSSZ ssz marshals the ProposerSlashing object +func (p *ProposerSlashing) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(p) +} + +// MarshalSSZTo ssz marshals the ProposerSlashing object to a target array +func (p *ProposerSlashing) MarshalSSZTo(buf []byte) (dst []byte, err error) { + dst = buf + + // Field (0) 'Header1' + if p.Header1 == nil { + p.Header1 = new(SignedBeaconBlockHeader) + } + if dst, err = p.Header1.MarshalSSZTo(dst); err != nil { + return + } + + // Field (1) 'Header2' + if p.Header2 == nil { + p.Header2 = new(SignedBeaconBlockHeader) + } + if dst, err = p.Header2.MarshalSSZTo(dst); err != nil { + return + } + + return +} + +// UnmarshalSSZ ssz unmarshals the ProposerSlashing object +func (p *ProposerSlashing) UnmarshalSSZ(buf []byte) error { + var err error + size := uint64(len(buf)) + if size != 416 { + return ssz.ErrSize + } + + // Field (0) 'Header1' + if p.Header1 == nil { + p.Header1 = new(SignedBeaconBlockHeader) + } + if err = p.Header1.UnmarshalSSZ(buf[0:208]); err != nil { + return err + } + + // Field (1) 'Header2' + if p.Header2 == nil { + p.Header2 = new(SignedBeaconBlockHeader) + } + if err = p.Header2.UnmarshalSSZ(buf[208:416]); err != nil { + return err + } + + return err +} + +// SizeSSZ returns the ssz encoded size in bytes for the ProposerSlashing object +func (p *ProposerSlashing) SizeSSZ() (size int) { + size = 416 + return +} + +// HashTreeRoot ssz hashes the ProposerSlashing object +func (p *ProposerSlashing) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(p) +} + +// HashTreeRootWith ssz hashes the ProposerSlashing object with a hasher +func (p *ProposerSlashing) HashTreeRootWith(hh ssz.HashWalker) (err error) { + indx := hh.Index() + + // Field (0) 'Header1' + if p.Header1 == nil { + p.Header1 = new(SignedBeaconBlockHeader) + } + if err = p.Header1.HashTreeRootWith(hh); err != nil { + return + } + + // Field (1) 'Header2' + if p.Header2 == nil { + p.Header2 = new(SignedBeaconBlockHeader) + } + if err = p.Header2.HashTreeRootWith(hh); err != nil { + return + } + + hh.Merkleize(indx) + return +} + +// GetTree ssz hashes the ProposerSlashing object +func (p *ProposerSlashing) GetTree() (*ssz.Node, error) { + return ssz.ProofTree(p) +} + +// MarshalSSZ ssz marshals the SignedBeaconBlockHeader object +func (s *SignedBeaconBlockHeader) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(s) +} + +// MarshalSSZTo ssz marshals the SignedBeaconBlockHeader object to a target array +func (s *SignedBeaconBlockHeader) MarshalSSZTo(buf []byte) (dst []byte, err error) { + dst = buf + + // Field (0) 'Header' + if s.Header == nil { + s.Header = new(BeaconBlockHeader) + } + if dst, err = s.Header.MarshalSSZTo(dst); err != nil { + return + } + + // Field (1) 'Signature' + if size := len(s.Signature); size != 96 { + err = ssz.ErrBytesLengthFn("SignedBeaconBlockHeader.Signature", size, 96) + return + } + dst = append(dst, s.Signature...) + + return +} + +// UnmarshalSSZ ssz unmarshals the SignedBeaconBlockHeader object +func (s *SignedBeaconBlockHeader) UnmarshalSSZ(buf []byte) error { + var err error + size := uint64(len(buf)) + if size != 208 { + return ssz.ErrSize + } + + // Field (0) 'Header' + if s.Header == nil { + s.Header = new(BeaconBlockHeader) + } + if err = s.Header.UnmarshalSSZ(buf[0:112]); err != nil { + return err + } + + // Field (1) 'Signature' + if cap(s.Signature) == 0 { + s.Signature = make([]byte, 0, len(buf[112:208])) + } + s.Signature = append(s.Signature, buf[112:208]...) + + return err +} + +// SizeSSZ returns the ssz encoded size in bytes for the SignedBeaconBlockHeader object +func (s *SignedBeaconBlockHeader) SizeSSZ() (size int) { + size = 208 + return +} + +// HashTreeRoot ssz hashes the SignedBeaconBlockHeader object +func (s *SignedBeaconBlockHeader) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(s) +} + +// HashTreeRootWith ssz hashes the SignedBeaconBlockHeader object with a hasher +func (s *SignedBeaconBlockHeader) HashTreeRootWith(hh ssz.HashWalker) (err error) { + indx := hh.Index() + + // Field (0) 'Header' + if s.Header == nil { + s.Header = new(BeaconBlockHeader) + } + if err = s.Header.HashTreeRootWith(hh); err != nil { + return + } + + // Field (1) 'Signature' + if size := len(s.Signature); size != 96 { + err = ssz.ErrBytesLengthFn("SignedBeaconBlockHeader.Signature", size, 96) + return + } + hh.PutBytes(s.Signature) + + hh.Merkleize(indx) + return +} + +// GetTree ssz hashes the SignedBeaconBlockHeader object +func (s *SignedBeaconBlockHeader) GetTree() (*ssz.Node, error) { + return ssz.ProofTree(s) +} + +// MarshalSSZ ssz marshals the AttesterSlashing object +func (a *AttesterSlashing) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(a) +} + +// MarshalSSZTo ssz marshals the AttesterSlashing object to a target array +func (a *AttesterSlashing) MarshalSSZTo(buf []byte) (dst []byte, err error) { + dst = buf + offset := int(8) + + // Offset (0) 'Attestation1' + dst = ssz.WriteOffset(dst, offset) + if a.Attestation1 == nil { + a.Attestation1 = new(IndexedAttestation) + } + offset += a.Attestation1.SizeSSZ() + + // Offset (1) 'Attestation2' + dst = ssz.WriteOffset(dst, offset) + + // Field (0) 'Attestation1' + if dst, err = a.Attestation1.MarshalSSZTo(dst); err != nil { + return + } + + // Field (1) 'Attestation2' + if dst, err = a.Attestation2.MarshalSSZTo(dst); err != nil { + return + } + + return +} + +// UnmarshalSSZ ssz unmarshals the AttesterSlashing object +func (a *AttesterSlashing) UnmarshalSSZ(buf []byte) error { + var err error + size := uint64(len(buf)) + if size < 8 { + return ssz.ErrSize + } + + tail := buf + var o0, o1 uint64 + + // Offset (0) 'Attestation1' + if o0 = ssz.ReadOffset(buf[0:4]); o0 > size { + return ssz.ErrOffset + } + + if o0 != 8 { + return ssz.ErrInvalidVariableOffset + } + + // Offset (1) 'Attestation2' + if o1 = ssz.ReadOffset(buf[4:8]); o1 > size || o0 > o1 { + return ssz.ErrOffset + } + + // Field (0) 'Attestation1' + { + buf = tail[o0:o1] + if a.Attestation1 == nil { + a.Attestation1 = new(IndexedAttestation) + } + if err = a.Attestation1.UnmarshalSSZ(buf); err != nil { + return err + } + } + + // Field (1) 'Attestation2' + { + buf = tail[o1:] + if a.Attestation2 == nil { + a.Attestation2 = new(IndexedAttestation) + } + if err = a.Attestation2.UnmarshalSSZ(buf); err != nil { + return err + } + } + return err +} + +// SizeSSZ returns the ssz encoded size in bytes for the AttesterSlashing object +func (a *AttesterSlashing) SizeSSZ() (size int) { + size = 8 + + // Field (0) 'Attestation1' + if a.Attestation1 == nil { + a.Attestation1 = new(IndexedAttestation) + } + size += a.Attestation1.SizeSSZ() + + // Field (1) 'Attestation2' + if a.Attestation2 == nil { + a.Attestation2 = new(IndexedAttestation) + } + size += a.Attestation2.SizeSSZ() + + return +} + +// HashTreeRoot ssz hashes the AttesterSlashing object +func (a *AttesterSlashing) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(a) +} + +// HashTreeRootWith ssz hashes the AttesterSlashing object with a hasher +func (a *AttesterSlashing) HashTreeRootWith(hh ssz.HashWalker) (err error) { + indx := hh.Index() + + // Field (0) 'Attestation1' + if err = a.Attestation1.HashTreeRootWith(hh); err != nil { + return + } + + // Field (1) 'Attestation2' + if err = a.Attestation2.HashTreeRootWith(hh); err != nil { + return + } + + hh.Merkleize(indx) + return +} + +// GetTree ssz hashes the AttesterSlashing object +func (a *AttesterSlashing) GetTree() (*ssz.Node, error) { + return ssz.ProofTree(a) +} + +// MarshalSSZ ssz marshals the IndexedAttestation object +func (i *IndexedAttestation) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(i) +} + +// MarshalSSZTo ssz marshals the IndexedAttestation object to a target array +func (i *IndexedAttestation) MarshalSSZTo(buf []byte) (dst []byte, err error) { + dst = buf + offset := int(228) + + // Offset (0) 'AttestationIndices' + dst = ssz.WriteOffset(dst, offset) + + // Field (1) 'Data' + if i.Data == nil { + i.Data = new(AttestationData) + } + if dst, err = i.Data.MarshalSSZTo(dst); err != nil { + return + } + + // Field (2) 'Signature' + if size := len(i.Signature); size != 96 { + err = ssz.ErrBytesLengthFn("IndexedAttestation.Signature", size, 96) + return + } + dst = append(dst, i.Signature...) + + // Field (0) 'AttestationIndices' + if size := len(i.AttestationIndices); size > 2048 { + err = ssz.ErrListTooBigFn("IndexedAttestation.AttestationIndices", size, 2048) + return + } + for ii := 0; ii < len(i.AttestationIndices); ii++ { + dst = ssz.MarshalUint64(dst, i.AttestationIndices[ii]) + } + + return +} + +// UnmarshalSSZ ssz unmarshals the IndexedAttestation object +func (i *IndexedAttestation) UnmarshalSSZ(buf []byte) error { + var err error + size := uint64(len(buf)) + if size < 228 { + return ssz.ErrSize + } + + tail := buf + var o0 uint64 + + // Offset (0) 'AttestationIndices' + if o0 = ssz.ReadOffset(buf[0:4]); o0 > size { + return ssz.ErrOffset + } + + if o0 != 228 { + return ssz.ErrInvalidVariableOffset + } + + // Field (1) 'Data' + if i.Data == nil { + i.Data = new(AttestationData) + } + if err = i.Data.UnmarshalSSZ(buf[4:132]); err != nil { + return err + } + + // Field (2) 'Signature' + if cap(i.Signature) == 0 { + i.Signature = make([]byte, 0, len(buf[132:228])) + } + i.Signature = append(i.Signature, buf[132:228]...) + + // Field (0) 'AttestationIndices' + { + buf = tail[o0:] + num, err := ssz.DivideInt2(len(buf), 8, 2048) + if err != nil { + return err + } + i.AttestationIndices = ssz.ExtendUint64(i.AttestationIndices, num) + for ii := 0; ii < num; ii++ { + i.AttestationIndices[ii] = ssz.UnmarshallUint64(buf[ii*8 : (ii+1)*8]) + } + } + return err +} + +// SizeSSZ returns the ssz encoded size in bytes for the IndexedAttestation object +func (i *IndexedAttestation) SizeSSZ() (size int) { + size = 228 + + // Field (0) 'AttestationIndices' + size += len(i.AttestationIndices) * 8 + + return +} + +// HashTreeRoot ssz hashes the IndexedAttestation object +func (i *IndexedAttestation) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(i) +} + +// HashTreeRootWith ssz hashes the IndexedAttestation object with a hasher +func (i *IndexedAttestation) HashTreeRootWith(hh ssz.HashWalker) (err error) { + indx := hh.Index() + + // Field (0) 'AttestationIndices' + { + if size := len(i.AttestationIndices); size > 2048 { + err = ssz.ErrListTooBigFn("IndexedAttestation.AttestationIndices", size, 2048) + return + } + subIndx := hh.Index() + for _, i := range i.AttestationIndices { + hh.AppendUint64(i) + } + hh.FillUpTo32() + numItems := uint64(len(i.AttestationIndices)) + hh.MerkleizeWithMixin(subIndx, numItems, ssz.CalculateLimit(2048, numItems, 8)) + } + + // Field (1) 'Data' + if i.Data == nil { + i.Data = new(AttestationData) + } + if err = i.Data.HashTreeRootWith(hh); err != nil { + return + } + + // Field (2) 'Signature' + if size := len(i.Signature); size != 96 { + err = ssz.ErrBytesLengthFn("IndexedAttestation.Signature", size, 96) + return + } + hh.PutBytes(i.Signature) + + hh.Merkleize(indx) + return +} + +// GetTree ssz hashes the IndexedAttestation object +func (i *IndexedAttestation) GetTree() (*ssz.Node, error) { + return ssz.ProofTree(i) +} + +// MarshalSSZ ssz marshals the AttestationData object +func (a *AttestationData) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(a) +} + +// MarshalSSZTo ssz marshals the AttestationData object to a target array +func (a *AttestationData) MarshalSSZTo(buf []byte) (dst []byte, err error) { + dst = buf + + // Field (0) 'Slot' + dst = ssz.MarshalUint64(dst, a.Slot) + + // Field (1) 'Index' + dst = ssz.MarshalUint64(dst, a.Index) + + // Field (2) 'BeaconBlockHash' + dst = append(dst, a.BeaconBlockHash[:]...) + + // Field (3) 'Source' + if a.Source == nil { + a.Source = new(Checkpoint) + } + if dst, err = a.Source.MarshalSSZTo(dst); err != nil { + return + } + + // Field (4) 'Target' + if a.Target == nil { + a.Target = new(Checkpoint) + } + if dst, err = a.Target.MarshalSSZTo(dst); err != nil { + return + } + + return +} + +// UnmarshalSSZ ssz unmarshals the AttestationData object +func (a *AttestationData) UnmarshalSSZ(buf []byte) error { + var err error + size := uint64(len(buf)) + if size != 128 { + return ssz.ErrSize + } + + // Field (0) 'Slot' + a.Slot = ssz.UnmarshallUint64(buf[0:8]) + + // Field (1) 'Index' + a.Index = ssz.UnmarshallUint64(buf[8:16]) + + // Field (2) 'BeaconBlockHash' + copy(a.BeaconBlockHash[:], buf[16:48]) + + // Field (3) 'Source' + if a.Source == nil { + a.Source = new(Checkpoint) + } + if err = a.Source.UnmarshalSSZ(buf[48:88]); err != nil { + return err + } + + // Field (4) 'Target' + if a.Target == nil { + a.Target = new(Checkpoint) + } + if err = a.Target.UnmarshalSSZ(buf[88:128]); err != nil { + return err + } + + return err +} + +// SizeSSZ returns the ssz encoded size in bytes for the AttestationData object +func (a *AttestationData) SizeSSZ() (size int) { + size = 128 + return +} + +// HashTreeRoot ssz hashes the AttestationData object +func (a *AttestationData) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(a) +} + +// HashTreeRootWith ssz hashes the AttestationData object with a hasher +func (a *AttestationData) HashTreeRootWith(hh ssz.HashWalker) (err error) { + indx := hh.Index() + + // Field (0) 'Slot' + hh.PutUint64(a.Slot) + + // Field (1) 'Index' + hh.PutUint64(a.Index) + + // Field (2) 'BeaconBlockHash' + hh.PutBytes(a.BeaconBlockHash[:]) + + // Field (3) 'Source' + if a.Source == nil { + a.Source = new(Checkpoint) + } + if err = a.Source.HashTreeRootWith(hh); err != nil { + return + } + + // Field (4) 'Target' + if a.Target == nil { + a.Target = new(Checkpoint) + } + if err = a.Target.HashTreeRootWith(hh); err != nil { + return + } + + hh.Merkleize(indx) + return +} + +// GetTree ssz hashes the AttestationData object +func (a *AttestationData) GetTree() (*ssz.Node, error) { + return ssz.ProofTree(a) +} + +// MarshalSSZ ssz marshals the Attestation object +func (a *Attestation) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(a) +} + +// MarshalSSZTo ssz marshals the Attestation object to a target array +func (a *Attestation) MarshalSSZTo(buf []byte) (dst []byte, err error) { + dst = buf + offset := int(228) + + // Offset (0) 'AggregationBits' + dst = ssz.WriteOffset(dst, offset) + + // Field (1) 'Data' + if a.Data == nil { + a.Data = new(AttestationData) + } + if dst, err = a.Data.MarshalSSZTo(dst); err != nil { + return + } + + // Field (2) 'Signature' + dst = append(dst, a.Signature[:]...) + + // Field (0) 'AggregationBits' + if size := len(a.AggregationBits); size > 2048 { + err = ssz.ErrBytesLengthFn("Attestation.AggregationBits", size, 2048) + return + } + dst = append(dst, a.AggregationBits...) + + return +} + +// UnmarshalSSZ ssz unmarshals the Attestation object +func (a *Attestation) UnmarshalSSZ(buf []byte) error { + var err error + size := uint64(len(buf)) + if size < 228 { + return ssz.ErrSize + } + + tail := buf + var o0 uint64 + + // Offset (0) 'AggregationBits' + if o0 = ssz.ReadOffset(buf[0:4]); o0 > size { + return ssz.ErrOffset + } + + if o0 != 228 { + return ssz.ErrInvalidVariableOffset + } + + // Field (1) 'Data' + if a.Data == nil { + a.Data = new(AttestationData) + } + if err = a.Data.UnmarshalSSZ(buf[4:132]); err != nil { + return err + } + + // Field (2) 'Signature' + copy(a.Signature[:], buf[132:228]) + + // Field (0) 'AggregationBits' + { + buf = tail[o0:] + if err = ssz.ValidateBitlist(buf, 2048); err != nil { + return err + } + if cap(a.AggregationBits) == 0 { + a.AggregationBits = make([]byte, 0, len(buf)) + } + a.AggregationBits = append(a.AggregationBits, buf...) + } + return err +} + +// SizeSSZ returns the ssz encoded size in bytes for the Attestation object +func (a *Attestation) SizeSSZ() (size int) { + size = 228 + + // Field (0) 'AggregationBits' + size += len(a.AggregationBits) + + return +} + +// HashTreeRoot ssz hashes the Attestation object +func (a *Attestation) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(a) +} + +// HashTreeRootWith ssz hashes the Attestation object with a hasher +func (a *Attestation) HashTreeRootWith(hh ssz.HashWalker) (err error) { + indx := hh.Index() + + // Field (0) 'AggregationBits' + if len(a.AggregationBits) == 0 { + err = ssz.ErrEmptyBitlist + return + } + hh.PutBitlist(a.AggregationBits, 2048) + + // Field (1) 'Data' + if a.Data == nil { + a.Data = new(AttestationData) + } + if err = a.Data.HashTreeRootWith(hh); err != nil { + return + } + + // Field (2) 'Signature' + hh.PutBytes(a.Signature[:]) + + hh.Merkleize(indx) + return +} + +// GetTree ssz hashes the Attestation object +func (a *Attestation) GetTree() (*ssz.Node, error) { + return ssz.ProofTree(a) +} + +// MarshalSSZ ssz marshals the Deposit object +func (d *Deposit) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(d) +} + +// MarshalSSZTo ssz marshals the Deposit object to a target array +func (d *Deposit) MarshalSSZTo(buf []byte) (dst []byte, err error) { + dst = buf + + // Field (0) 'Proof' + if size := len(d.Proof); size != 33 { + err = ssz.ErrVectorLengthFn("Deposit.Proof", size, 33) + return + } + for ii := 0; ii < 33; ii++ { + if size := len(d.Proof[ii]); size != 32 { + err = ssz.ErrBytesLengthFn("Deposit.Proof[ii]", size, 32) + return + } + dst = append(dst, d.Proof[ii]...) + } + + // Field (1) 'Data' + if d.Data == nil { + d.Data = new(DepositData) + } + if dst, err = d.Data.MarshalSSZTo(dst); err != nil { + return + } + + return +} + +// UnmarshalSSZ ssz unmarshals the Deposit object +func (d *Deposit) UnmarshalSSZ(buf []byte) error { + var err error + size := uint64(len(buf)) + if size != 1240 { + return ssz.ErrSize + } + + // Field (0) 'Proof' + d.Proof = make([][]byte, 33) + for ii := 0; ii < 33; ii++ { + if cap(d.Proof[ii]) == 0 { + d.Proof[ii] = make([]byte, 0, len(buf[0:1056][ii*32:(ii+1)*32])) + } + d.Proof[ii] = append(d.Proof[ii], buf[0:1056][ii*32:(ii+1)*32]...) + } + + // Field (1) 'Data' + if d.Data == nil { + d.Data = new(DepositData) + } + if err = d.Data.UnmarshalSSZ(buf[1056:1240]); err != nil { + return err + } + + return err +} + +// SizeSSZ returns the ssz encoded size in bytes for the Deposit object +func (d *Deposit) SizeSSZ() (size int) { + size = 1240 + return +} + +// HashTreeRoot ssz hashes the Deposit object +func (d *Deposit) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(d) +} + +// HashTreeRootWith ssz hashes the Deposit object with a hasher +func (d *Deposit) HashTreeRootWith(hh ssz.HashWalker) (err error) { + indx := hh.Index() + + // Field (0) 'Proof' + { + if size := len(d.Proof); size != 33 { + err = ssz.ErrVectorLengthFn("Deposit.Proof", size, 33) + return + } + subIndx := hh.Index() + for _, i := range d.Proof { + if len(i) != 32 { + err = ssz.ErrBytesLength + return + } + hh.Append(i) + } + hh.Merkleize(subIndx) + } + + // Field (1) 'Data' + if d.Data == nil { + d.Data = new(DepositData) + } + if err = d.Data.HashTreeRootWith(hh); err != nil { + return + } + + hh.Merkleize(indx) + return +} + +// GetTree ssz hashes the Deposit object +func (d *Deposit) GetTree() (*ssz.Node, error) { + return ssz.ProofTree(d) +} + +// MarshalSSZ ssz marshals the SignedVoluntaryExit object +func (s *SignedVoluntaryExit) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(s) +} + +// MarshalSSZTo ssz marshals the SignedVoluntaryExit object to a target array +func (s *SignedVoluntaryExit) MarshalSSZTo(buf []byte) (dst []byte, err error) { + dst = buf + + // Field (0) 'Exit' + if s.Exit == nil { + s.Exit = new(VoluntaryExit) + } + if dst, err = s.Exit.MarshalSSZTo(dst); err != nil { + return + } + + // Field (1) 'Signature' + dst = append(dst, s.Signature[:]...) + + return +} + +// UnmarshalSSZ ssz unmarshals the SignedVoluntaryExit object +func (s *SignedVoluntaryExit) UnmarshalSSZ(buf []byte) error { + var err error + size := uint64(len(buf)) + if size != 112 { + return ssz.ErrSize + } + + // Field (0) 'Exit' + if s.Exit == nil { + s.Exit = new(VoluntaryExit) + } + if err = s.Exit.UnmarshalSSZ(buf[0:16]); err != nil { + return err + } + + // Field (1) 'Signature' + copy(s.Signature[:], buf[16:112]) + + return err +} + +// SizeSSZ returns the ssz encoded size in bytes for the SignedVoluntaryExit object +func (s *SignedVoluntaryExit) SizeSSZ() (size int) { + size = 112 + return +} + +// HashTreeRoot ssz hashes the SignedVoluntaryExit object +func (s *SignedVoluntaryExit) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(s) +} + +// HashTreeRootWith ssz hashes the SignedVoluntaryExit object with a hasher +func (s *SignedVoluntaryExit) HashTreeRootWith(hh ssz.HashWalker) (err error) { + indx := hh.Index() + + // Field (0) 'Exit' + if s.Exit == nil { + s.Exit = new(VoluntaryExit) + } + if err = s.Exit.HashTreeRootWith(hh); err != nil { + return + } + + // Field (1) 'Signature' + hh.PutBytes(s.Signature[:]) + + hh.Merkleize(indx) + return +} + +// GetTree ssz hashes the SignedVoluntaryExit object +func (s *SignedVoluntaryExit) GetTree() (*ssz.Node, error) { + return ssz.ProofTree(s) +} + +// MarshalSSZ ssz marshals the SyncAggregate object +func (s *SyncAggregate) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(s) +} + +// MarshalSSZTo ssz marshals the SyncAggregate object to a target array +func (s *SyncAggregate) MarshalSSZTo(buf []byte) (dst []byte, err error) { + dst = buf + + // Field (0) 'SyncCommiteeBits' + if size := len(s.SyncCommiteeBits); size != 64 { + err = ssz.ErrBytesLengthFn("SyncAggregate.SyncCommiteeBits", size, 64) + return + } + dst = append(dst, s.SyncCommiteeBits...) + + // Field (1) 'SyncCommiteeSignature' + dst = append(dst, s.SyncCommiteeSignature[:]...) + + return +} + +// UnmarshalSSZ ssz unmarshals the SyncAggregate object +func (s *SyncAggregate) UnmarshalSSZ(buf []byte) error { + var err error + size := uint64(len(buf)) + if size != 160 { + return ssz.ErrSize + } + + // Field (0) 'SyncCommiteeBits' + if cap(s.SyncCommiteeBits) == 0 { + s.SyncCommiteeBits = make([]byte, 0, len(buf[0:64])) + } + s.SyncCommiteeBits = append(s.SyncCommiteeBits, buf[0:64]...) + + // Field (1) 'SyncCommiteeSignature' + copy(s.SyncCommiteeSignature[:], buf[64:160]) + + return err +} + +// SizeSSZ returns the ssz encoded size in bytes for the SyncAggregate object +func (s *SyncAggregate) SizeSSZ() (size int) { + size = 160 + return +} + +// HashTreeRoot ssz hashes the SyncAggregate object +func (s *SyncAggregate) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(s) +} + +// HashTreeRootWith ssz hashes the SyncAggregate object with a hasher +func (s *SyncAggregate) HashTreeRootWith(hh ssz.HashWalker) (err error) { + indx := hh.Index() + + // Field (0) 'SyncCommiteeBits' + if size := len(s.SyncCommiteeBits); size != 64 { + err = ssz.ErrBytesLengthFn("SyncAggregate.SyncCommiteeBits", size, 64) + return + } + hh.PutBytes(s.SyncCommiteeBits) + + // Field (1) 'SyncCommiteeSignature' + hh.PutBytes(s.SyncCommiteeSignature[:]) + + hh.Merkleize(indx) + return +} + +// GetTree ssz hashes the SyncAggregate object +func (s *SyncAggregate) GetTree() (*ssz.Node, error) { + return ssz.ProofTree(s) +} + +// MarshalSSZ ssz marshals the ExecutionPayload object +func (e *ExecutionPayload) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(e) +} + +// MarshalSSZTo ssz marshals the ExecutionPayload object to a target array +func (e *ExecutionPayload) MarshalSSZTo(buf []byte) (dst []byte, err error) { + dst = buf + offset := int(528) + + // Field (0) 'ParentHash' + dst = append(dst, e.ParentHash[:]...) + + // Field (1) 'FeeRecipient' + dst = append(dst, e.FeeRecipient[:]...) + + // Field (2) 'StateRoot' + dst = append(dst, e.StateRoot[:]...) + + // Field (3) 'ReceiptsRoot' + dst = append(dst, e.ReceiptsRoot[:]...) + + // Field (4) 'LogsBloom' + dst = append(dst, e.LogsBloom[:]...) + + // Field (5) 'PrevRandao' + dst = append(dst, e.PrevRandao[:]...) + + // Field (6) 'BlockNumber' + dst = ssz.MarshalUint64(dst, e.BlockNumber) + + // Field (7) 'GasLimit' + dst = ssz.MarshalUint64(dst, e.GasLimit) + + // Field (8) 'GasUsed' + dst = ssz.MarshalUint64(dst, e.GasUsed) + + // Field (9) 'Timestamp' + dst = ssz.MarshalUint64(dst, e.Timestamp) + + // Offset (10) 'ExtraData' + dst = ssz.WriteOffset(dst, offset) + offset += len(e.ExtraData) + + // Field (11) 'BaseFeePerGas' + dst = append(dst, e.BaseFeePerGas[:]...) + + // Field (12) 'BlockHash' + dst = append(dst, e.BlockHash[:]...) + + // Offset (13) 'Transactions' + dst = ssz.WriteOffset(dst, offset) + for ii := 0; ii < len(e.Transactions); ii++ { + offset += 4 + offset += len(e.Transactions[ii]) + } + + // Offset (14) 'Withdrawals' + dst = ssz.WriteOffset(dst, offset) + + // Field (15) 'BlobGasUsed' + dst = ssz.MarshalUint64(dst, e.BlobGasUsed) + + // Field (16) 'ExcessBlobGas' + dst = ssz.MarshalUint64(dst, e.ExcessBlobGas) + + // Field (10) 'ExtraData' + if size := len(e.ExtraData); size > 32 { + err = ssz.ErrBytesLengthFn("ExecutionPayload.ExtraData", size, 32) + return + } + dst = append(dst, e.ExtraData...) + + // Field (13) 'Transactions' + if size := len(e.Transactions); size > 1048576 { + err = ssz.ErrListTooBigFn("ExecutionPayload.Transactions", size, 1048576) + return + } + { + offset = 4 * len(e.Transactions) + for ii := 0; ii < len(e.Transactions); ii++ { + dst = ssz.WriteOffset(dst, offset) + offset += len(e.Transactions[ii]) + } + } + for ii := 0; ii < len(e.Transactions); ii++ { + if size := len(e.Transactions[ii]); size > 1073741824 { + err = ssz.ErrBytesLengthFn("ExecutionPayload.Transactions[ii]", size, 1073741824) + return + } + dst = append(dst, e.Transactions[ii]...) + } + + // Field (14) 'Withdrawals' + if size := len(e.Withdrawals); size > 16 { + err = ssz.ErrListTooBigFn("ExecutionPayload.Withdrawals", size, 16) + return + } + for ii := 0; ii < len(e.Withdrawals); ii++ { + if dst, err = e.Withdrawals[ii].MarshalSSZTo(dst); err != nil { + return + } + } + + return +} + +// UnmarshalSSZ ssz unmarshals the ExecutionPayload object +func (e *ExecutionPayload) UnmarshalSSZ(buf []byte) error { + var err error + size := uint64(len(buf)) + if size < 528 { + return ssz.ErrSize + } + + tail := buf + var o10, o13, o14 uint64 + + // Field (0) 'ParentHash' + copy(e.ParentHash[:], buf[0:32]) + + // Field (1) 'FeeRecipient' + copy(e.FeeRecipient[:], buf[32:52]) + + // Field (2) 'StateRoot' + copy(e.StateRoot[:], buf[52:84]) + + // Field (3) 'ReceiptsRoot' + copy(e.ReceiptsRoot[:], buf[84:116]) + + // Field (4) 'LogsBloom' + copy(e.LogsBloom[:], buf[116:372]) + + // Field (5) 'PrevRandao' + copy(e.PrevRandao[:], buf[372:404]) + + // Field (6) 'BlockNumber' + e.BlockNumber = ssz.UnmarshallUint64(buf[404:412]) + + // Field (7) 'GasLimit' + e.GasLimit = ssz.UnmarshallUint64(buf[412:420]) + + // Field (8) 'GasUsed' + e.GasUsed = ssz.UnmarshallUint64(buf[420:428]) + + // Field (9) 'Timestamp' + e.Timestamp = ssz.UnmarshallUint64(buf[428:436]) + + // Offset (10) 'ExtraData' + if o10 = ssz.ReadOffset(buf[436:440]); o10 > size { + return ssz.ErrOffset + } + + if o10 != 528 { + return ssz.ErrInvalidVariableOffset + } + + // Field (11) 'BaseFeePerGas' + copy(e.BaseFeePerGas[:], buf[440:472]) + + // Field (12) 'BlockHash' + copy(e.BlockHash[:], buf[472:504]) + + // Offset (13) 'Transactions' + if o13 = ssz.ReadOffset(buf[504:508]); o13 > size || o10 > o13 { + return ssz.ErrOffset + } + + // Offset (14) 'Withdrawals' + if o14 = ssz.ReadOffset(buf[508:512]); o14 > size || o13 > o14 { + return ssz.ErrOffset + } + + // Field (15) 'BlobGasUsed' + e.BlobGasUsed = ssz.UnmarshallUint64(buf[512:520]) + + // Field (16) 'ExcessBlobGas' + e.ExcessBlobGas = ssz.UnmarshallUint64(buf[520:528]) + + // Field (10) 'ExtraData' + { + buf = tail[o10:o13] + if len(buf) > 32 { + return ssz.ErrBytesLength + } + if cap(e.ExtraData) == 0 { + e.ExtraData = make([]byte, 0, len(buf)) + } + e.ExtraData = append(e.ExtraData, buf...) + } + + // Field (13) 'Transactions' + { + buf = tail[o13:o14] + num, err := ssz.DecodeDynamicLength(buf, 1048576) + if err != nil { + return err + } + e.Transactions = make([][]byte, num) + err = ssz.UnmarshalDynamic(buf, num, func(indx int, buf []byte) (err error) { + if len(buf) > 1073741824 { + return ssz.ErrBytesLength + } + if cap(e.Transactions[indx]) == 0 { + e.Transactions[indx] = make([]byte, 0, len(buf)) + } + e.Transactions[indx] = append(e.Transactions[indx], buf...) + return nil + }) + if err != nil { + return err + } + } + + // Field (14) 'Withdrawals' + { + buf = tail[o14:] + num, err := ssz.DivideInt2(len(buf), 44, 16) + if err != nil { + return err + } + e.Withdrawals = make([]*Withdrawal, num) + for ii := 0; ii < num; ii++ { + if e.Withdrawals[ii] == nil { + e.Withdrawals[ii] = new(Withdrawal) + } + if err = e.Withdrawals[ii].UnmarshalSSZ(buf[ii*44 : (ii+1)*44]); err != nil { + return err + } + } + } + return err +} + +// SizeSSZ returns the ssz encoded size in bytes for the ExecutionPayload object +func (e *ExecutionPayload) SizeSSZ() (size int) { + size = 528 + + // Field (10) 'ExtraData' + size += len(e.ExtraData) + + // Field (13) 'Transactions' + for ii := 0; ii < len(e.Transactions); ii++ { + size += 4 + size += len(e.Transactions[ii]) + } + + // Field (14) 'Withdrawals' + size += len(e.Withdrawals) * 44 + + return +} + +// HashTreeRoot ssz hashes the ExecutionPayload object +func (e *ExecutionPayload) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(e) +} + +// HashTreeRootWith ssz hashes the ExecutionPayload object with a hasher +func (e *ExecutionPayload) HashTreeRootWith(hh ssz.HashWalker) (err error) { + indx := hh.Index() + + // Field (0) 'ParentHash' + hh.PutBytes(e.ParentHash[:]) + + // Field (1) 'FeeRecipient' + hh.PutBytes(e.FeeRecipient[:]) + + // Field (2) 'StateRoot' + hh.PutBytes(e.StateRoot[:]) + + // Field (3) 'ReceiptsRoot' + hh.PutBytes(e.ReceiptsRoot[:]) + + // Field (4) 'LogsBloom' + hh.PutBytes(e.LogsBloom[:]) + + // Field (5) 'PrevRandao' + hh.PutBytes(e.PrevRandao[:]) + + // Field (6) 'BlockNumber' + hh.PutUint64(e.BlockNumber) + + // Field (7) 'GasLimit' + hh.PutUint64(e.GasLimit) + + // Field (8) 'GasUsed' + hh.PutUint64(e.GasUsed) + + // Field (9) 'Timestamp' + hh.PutUint64(e.Timestamp) + + // Field (10) 'ExtraData' + { + elemIndx := hh.Index() + byteLen := uint64(len(e.ExtraData)) + if byteLen > 32 { + err = ssz.ErrIncorrectListSize + return + } + hh.Append(e.ExtraData) + hh.MerkleizeWithMixin(elemIndx, byteLen, (32+31)/32) + } + + // Field (11) 'BaseFeePerGas' + hh.PutBytes(e.BaseFeePerGas[:]) + + // Field (12) 'BlockHash' + hh.PutBytes(e.BlockHash[:]) + + // Field (13) 'Transactions' + { + subIndx := hh.Index() + num := uint64(len(e.Transactions)) + if num > 1048576 { + err = ssz.ErrIncorrectListSize + return + } + for _, elem := range e.Transactions { + { + elemIndx := hh.Index() + byteLen := uint64(len(elem)) + if byteLen > 1073741824 { + err = ssz.ErrIncorrectListSize + return + } + hh.AppendBytes32(elem) + hh.MerkleizeWithMixin(elemIndx, byteLen, (1073741824+31)/32) + } + } + hh.MerkleizeWithMixin(subIndx, num, 1048576) + } + + // Field (14) 'Withdrawals' + { + subIndx := hh.Index() + num := uint64(len(e.Withdrawals)) + if num > 16 { + err = ssz.ErrIncorrectListSize + return + } + for _, elem := range e.Withdrawals { + if err = elem.HashTreeRootWith(hh); err != nil { + return + } + } + hh.MerkleizeWithMixin(subIndx, num, 16) + } + + // Field (15) 'BlobGasUsed' + hh.PutUint64(e.BlobGasUsed) + + // Field (16) 'ExcessBlobGas' + hh.PutUint64(e.ExcessBlobGas) + + hh.Merkleize(indx) + return +} + +// GetTree ssz hashes the ExecutionPayload object +func (e *ExecutionPayload) GetTree() (*ssz.Node, error) { + return ssz.ProofTree(e) +} + +// MarshalSSZ ssz marshals the Withdrawal object +func (w *Withdrawal) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(w) +} + +// MarshalSSZTo ssz marshals the Withdrawal object to a target array +func (w *Withdrawal) MarshalSSZTo(buf []byte) (dst []byte, err error) { + dst = buf + + // Field (0) 'Index' + dst = ssz.MarshalUint64(dst, w.Index) + + // Field (1) 'ValidatorIndex' + dst = ssz.MarshalUint64(dst, w.ValidatorIndex) + + // Field (2) 'Address' + dst = append(dst, w.Address[:]...) + + // Field (3) 'Amount' + dst = ssz.MarshalUint64(dst, w.Amount) + + return +} + +// UnmarshalSSZ ssz unmarshals the Withdrawal object +func (w *Withdrawal) UnmarshalSSZ(buf []byte) error { + var err error + size := uint64(len(buf)) + if size != 44 { + return ssz.ErrSize + } + + // Field (0) 'Index' + w.Index = ssz.UnmarshallUint64(buf[0:8]) + + // Field (1) 'ValidatorIndex' + w.ValidatorIndex = ssz.UnmarshallUint64(buf[8:16]) + + // Field (2) 'Address' + copy(w.Address[:], buf[16:36]) + + // Field (3) 'Amount' + w.Amount = ssz.UnmarshallUint64(buf[36:44]) + + return err +} + +// SizeSSZ returns the ssz encoded size in bytes for the Withdrawal object +func (w *Withdrawal) SizeSSZ() (size int) { + size = 44 + return +} + +// HashTreeRoot ssz hashes the Withdrawal object +func (w *Withdrawal) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(w) +} + +// HashTreeRootWith ssz hashes the Withdrawal object with a hasher +func (w *Withdrawal) HashTreeRootWith(hh ssz.HashWalker) (err error) { + indx := hh.Index() + + // Field (0) 'Index' + hh.PutUint64(w.Index) + + // Field (1) 'ValidatorIndex' + hh.PutUint64(w.ValidatorIndex) + + // Field (2) 'Address' + hh.PutBytes(w.Address[:]) + + // Field (3) 'Amount' + hh.PutUint64(w.Amount) + + hh.Merkleize(indx) + return +} + +// GetTree ssz hashes the Withdrawal object +func (w *Withdrawal) GetTree() (*ssz.Node, error) { + return ssz.ProofTree(w) +} + +// MarshalSSZ ssz marshals the BLSToExecutionChange object +func (b *BLSToExecutionChange) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(b) +} + +// MarshalSSZTo ssz marshals the BLSToExecutionChange object to a target array +func (b *BLSToExecutionChange) MarshalSSZTo(buf []byte) (dst []byte, err error) { + dst = buf + + // Field (0) 'ValidatorIndex' + dst = ssz.MarshalUint64(dst, b.ValidatorIndex) + + // Field (1) 'FromBLSPubKey' + dst = append(dst, b.FromBLSPubKey[:]...) + + // Field (2) 'ToExecutionAddress' + dst = append(dst, b.ToExecutionAddress[:]...) + + return +} + +// UnmarshalSSZ ssz unmarshals the BLSToExecutionChange object +func (b *BLSToExecutionChange) UnmarshalSSZ(buf []byte) error { + var err error + size := uint64(len(buf)) + if size != 76 { + return ssz.ErrSize + } + + // Field (0) 'ValidatorIndex' + b.ValidatorIndex = ssz.UnmarshallUint64(buf[0:8]) + + // Field (1) 'FromBLSPubKey' + copy(b.FromBLSPubKey[:], buf[8:56]) + + // Field (2) 'ToExecutionAddress' + copy(b.ToExecutionAddress[:], buf[56:76]) + + return err +} + +// SizeSSZ returns the ssz encoded size in bytes for the BLSToExecutionChange object +func (b *BLSToExecutionChange) SizeSSZ() (size int) { + size = 76 + return +} + +// HashTreeRoot ssz hashes the BLSToExecutionChange object +func (b *BLSToExecutionChange) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(b) +} + +// HashTreeRootWith ssz hashes the BLSToExecutionChange object with a hasher +func (b *BLSToExecutionChange) HashTreeRootWith(hh ssz.HashWalker) (err error) { + indx := hh.Index() + + // Field (0) 'ValidatorIndex' + hh.PutUint64(b.ValidatorIndex) + + // Field (1) 'FromBLSPubKey' + hh.PutBytes(b.FromBLSPubKey[:]) + + // Field (2) 'ToExecutionAddress' + hh.PutBytes(b.ToExecutionAddress[:]) + + hh.Merkleize(indx) + return +} + +// GetTree ssz hashes the BLSToExecutionChange object +func (b *BLSToExecutionChange) GetTree() (*ssz.Node, error) { + return ssz.ProofTree(b) +} + +// MarshalSSZ ssz marshals the SignedBLSToExecutionChange object +func (s *SignedBLSToExecutionChange) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(s) +} + +// MarshalSSZTo ssz marshals the SignedBLSToExecutionChange object to a target array +func (s *SignedBLSToExecutionChange) MarshalSSZTo(buf []byte) (dst []byte, err error) { + dst = buf + + // Field (0) 'Message' + if s.Message == nil { + s.Message = new(BLSToExecutionChange) + } + if dst, err = s.Message.MarshalSSZTo(dst); err != nil { + return + } + + // Field (1) 'Signature' + dst = append(dst, s.Signature[:]...) + + return +} + +// UnmarshalSSZ ssz unmarshals the SignedBLSToExecutionChange object +func (s *SignedBLSToExecutionChange) UnmarshalSSZ(buf []byte) error { + var err error + size := uint64(len(buf)) + if size != 172 { + return ssz.ErrSize + } + + // Field (0) 'Message' + if s.Message == nil { + s.Message = new(BLSToExecutionChange) + } + if err = s.Message.UnmarshalSSZ(buf[0:76]); err != nil { + return err + } + + // Field (1) 'Signature' + copy(s.Signature[:], buf[76:172]) + + return err +} + +// SizeSSZ returns the ssz encoded size in bytes for the SignedBLSToExecutionChange object +func (s *SignedBLSToExecutionChange) SizeSSZ() (size int) { + size = 172 + return +} + +// HashTreeRoot ssz hashes the SignedBLSToExecutionChange object +func (s *SignedBLSToExecutionChange) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(s) +} + +// HashTreeRootWith ssz hashes the SignedBLSToExecutionChange object with a hasher +func (s *SignedBLSToExecutionChange) HashTreeRootWith(hh ssz.HashWalker) (err error) { + indx := hh.Index() + + // Field (0) 'Message' + if s.Message == nil { + s.Message = new(BLSToExecutionChange) + } + if err = s.Message.HashTreeRootWith(hh); err != nil { + return + } + + // Field (1) 'Signature' + hh.PutBytes(s.Signature[:]) + + hh.Merkleize(indx) + return +} + +// GetTree ssz hashes the SignedBLSToExecutionChange object +func (s *SignedBLSToExecutionChange) GetTree() (*ssz.Node, error) { + return ssz.ProofTree(s) +} diff --git a/shared/types/eth2/generic/block_header.go b/shared/types/eth2/generic/block_header.go new file mode 100644 index 000000000..231515075 --- /dev/null +++ b/shared/types/eth2/generic/block_header.go @@ -0,0 +1,3 @@ +package generic + +const BeaconBlockHeaderStateRootGeneralizedIndex uint64 = 11 // Container with 5 fields, so gid 8 is the first field. We want the 4th field, so gid 8 + 3 = 11 diff --git a/shared/types/eth2/generic/state.go b/shared/types/eth2/generic/state.go new file mode 100644 index 000000000..693be87cc --- /dev/null +++ b/shared/types/eth2/generic/state.go @@ -0,0 +1,53 @@ +package generic + +// BeaconStateValidatorsIndex is the field offset of the Validators field in the BeaconState struct +const BeaconStateValidatorsIndex uint64 = 11 + +// If this ever isn't a power of two, we need to round up to the next power of two +const beaconStateValidatorsMaxLength uint64 = 1 << 40 + +const BeaconStateHistoricalSummariesFieldIndex uint64 = 27 +const BeaconStateHistoricalSummariesMaxLength uint64 = 1 << 24 +const beaconStateHistoricalSummaryChunkCeil uint64 = 2 +const beaconStateHistoricalSummaryBlockSummaryRootIndex uint64 = 0 +const BeaconStateBlockRootsMaxLength uint64 = 1 << 13 +const BeaconStateBlockRootsFieldIndex uint64 = 5 + +type PendingDeposit struct { + Pubkey []byte `json:"pubkey" ssz-size:"48"` + WithdrawalCredentials []byte `json:"withdrawal_credentials" ssz-size:"32"` + Amount uint64 `json:"amount"` + Signature []byte `json:"signature" ssz-size:"96"` + Slot uint64 `json:"slot"` +} + +type PendingPartialWithdrawal struct { + ValidatorIndex uint64 `json:"validator_index"` + Amount uint64 `json:"amount"` + WithdrawableEpoch uint64 `json:"withdrawable_epoch"` +} + +type PendingConsolidation struct { + SourceIndex uint64 `json:"source_index"` + TargetIndex uint64 `json:"target_index"` +} + +type ExecutionPayloadHeader struct { + ParentHash [32]byte `json:"parent_hash" ssz-size:"32"` + FeeRecipient [20]byte `json:"fee_recipient" ssz-size:"20"` + StateRoot [32]byte `json:"state_root" ssz-size:"32"` + ReceiptsRoot [32]byte `json:"receipts_root" ssz-size:"32"` + LogsBloom [256]byte `json:"logs_bloom" ssz-size:"256"` + PrevRandao [32]byte `json:"prev_randao" ssz-size:"32"` + BlockNumber uint64 `json:"block_number"` + GasLimit uint64 `json:"gas_limit"` + GasUsed uint64 `json:"gas_used"` + Timestamp uint64 `json:"timestamp"` + ExtraData []byte `json:"extra_data" ssz-max:"32"` + BaseFeePerGas Uint256 `json:"base_fee_per_gas" ssz-size:"32"` + BlockHash [32]byte `json:"block_hash" ssz-size:"32"` + TransactionsRoot [32]byte `json:"transactions_root" ssz-size:"32"` + WithdrawalRoot [32]byte `json:"withdrawals_root" ssz-size:"32"` + BlobGasUsed uint64 `json:"blob_gas_used"` + ExcessBlobGas uint64 `json:"excess_blob_gas"` +} diff --git a/shared/types/eth2/generic/state_encoding.go b/shared/types/eth2/generic/state_encoding.go new file mode 100644 index 000000000..35a770b1d --- /dev/null +++ b/shared/types/eth2/generic/state_encoding.go @@ -0,0 +1,513 @@ +// Code generated by fastssz. DO NOT EDIT. +// Hash: 3fba9f7b919dd4fa010d06a5c0b339f6a3643424cec6377e930e2b3569a71338 +// Version: 0.1.3 +package generic + +import ( + ssz "github.com/ferranbt/fastssz" +) + +// MarshalSSZ ssz marshals the PendingDeposit object +func (p *PendingDeposit) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(p) +} + +// MarshalSSZTo ssz marshals the PendingDeposit object to a target array +func (p *PendingDeposit) MarshalSSZTo(buf []byte) (dst []byte, err error) { + dst = buf + + // Field (0) 'Pubkey' + if size := len(p.Pubkey); size != 48 { + err = ssz.ErrBytesLengthFn("PendingDeposit.Pubkey", size, 48) + return + } + dst = append(dst, p.Pubkey...) + + // Field (1) 'WithdrawalCredentials' + if size := len(p.WithdrawalCredentials); size != 32 { + err = ssz.ErrBytesLengthFn("PendingDeposit.WithdrawalCredentials", size, 32) + return + } + dst = append(dst, p.WithdrawalCredentials...) + + // Field (2) 'Amount' + dst = ssz.MarshalUint64(dst, p.Amount) + + // Field (3) 'Signature' + if size := len(p.Signature); size != 96 { + err = ssz.ErrBytesLengthFn("PendingDeposit.Signature", size, 96) + return + } + dst = append(dst, p.Signature...) + + // Field (4) 'Slot' + dst = ssz.MarshalUint64(dst, p.Slot) + + return +} + +// UnmarshalSSZ ssz unmarshals the PendingDeposit object +func (p *PendingDeposit) UnmarshalSSZ(buf []byte) error { + var err error + size := uint64(len(buf)) + if size != 192 { + return ssz.ErrSize + } + + // Field (0) 'Pubkey' + if cap(p.Pubkey) == 0 { + p.Pubkey = make([]byte, 0, len(buf[0:48])) + } + p.Pubkey = append(p.Pubkey, buf[0:48]...) + + // Field (1) 'WithdrawalCredentials' + if cap(p.WithdrawalCredentials) == 0 { + p.WithdrawalCredentials = make([]byte, 0, len(buf[48:80])) + } + p.WithdrawalCredentials = append(p.WithdrawalCredentials, buf[48:80]...) + + // Field (2) 'Amount' + p.Amount = ssz.UnmarshallUint64(buf[80:88]) + + // Field (3) 'Signature' + if cap(p.Signature) == 0 { + p.Signature = make([]byte, 0, len(buf[88:184])) + } + p.Signature = append(p.Signature, buf[88:184]...) + + // Field (4) 'Slot' + p.Slot = ssz.UnmarshallUint64(buf[184:192]) + + return err +} + +// SizeSSZ returns the ssz encoded size in bytes for the PendingDeposit object +func (p *PendingDeposit) SizeSSZ() (size int) { + size = 192 + return +} + +// HashTreeRoot ssz hashes the PendingDeposit object +func (p *PendingDeposit) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(p) +} + +// HashTreeRootWith ssz hashes the PendingDeposit object with a hasher +func (p *PendingDeposit) HashTreeRootWith(hh ssz.HashWalker) (err error) { + indx := hh.Index() + + // Field (0) 'Pubkey' + if size := len(p.Pubkey); size != 48 { + err = ssz.ErrBytesLengthFn("PendingDeposit.Pubkey", size, 48) + return + } + hh.PutBytes(p.Pubkey) + + // Field (1) 'WithdrawalCredentials' + if size := len(p.WithdrawalCredentials); size != 32 { + err = ssz.ErrBytesLengthFn("PendingDeposit.WithdrawalCredentials", size, 32) + return + } + hh.PutBytes(p.WithdrawalCredentials) + + // Field (2) 'Amount' + hh.PutUint64(p.Amount) + + // Field (3) 'Signature' + if size := len(p.Signature); size != 96 { + err = ssz.ErrBytesLengthFn("PendingDeposit.Signature", size, 96) + return + } + hh.PutBytes(p.Signature) + + // Field (4) 'Slot' + hh.PutUint64(p.Slot) + + hh.Merkleize(indx) + return +} + +// GetTree ssz hashes the PendingDeposit object +func (p *PendingDeposit) GetTree() (*ssz.Node, error) { + return ssz.ProofTree(p) +} + +// MarshalSSZ ssz marshals the PendingPartialWithdrawal object +func (p *PendingPartialWithdrawal) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(p) +} + +// MarshalSSZTo ssz marshals the PendingPartialWithdrawal object to a target array +func (p *PendingPartialWithdrawal) MarshalSSZTo(buf []byte) (dst []byte, err error) { + dst = buf + + // Field (0) 'ValidatorIndex' + dst = ssz.MarshalUint64(dst, p.ValidatorIndex) + + // Field (1) 'Amount' + dst = ssz.MarshalUint64(dst, p.Amount) + + // Field (2) 'WithdrawableEpoch' + dst = ssz.MarshalUint64(dst, p.WithdrawableEpoch) + + return +} + +// UnmarshalSSZ ssz unmarshals the PendingPartialWithdrawal object +func (p *PendingPartialWithdrawal) UnmarshalSSZ(buf []byte) error { + var err error + size := uint64(len(buf)) + if size != 24 { + return ssz.ErrSize + } + + // Field (0) 'ValidatorIndex' + p.ValidatorIndex = ssz.UnmarshallUint64(buf[0:8]) + + // Field (1) 'Amount' + p.Amount = ssz.UnmarshallUint64(buf[8:16]) + + // Field (2) 'WithdrawableEpoch' + p.WithdrawableEpoch = ssz.UnmarshallUint64(buf[16:24]) + + return err +} + +// SizeSSZ returns the ssz encoded size in bytes for the PendingPartialWithdrawal object +func (p *PendingPartialWithdrawal) SizeSSZ() (size int) { + size = 24 + return +} + +// HashTreeRoot ssz hashes the PendingPartialWithdrawal object +func (p *PendingPartialWithdrawal) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(p) +} + +// HashTreeRootWith ssz hashes the PendingPartialWithdrawal object with a hasher +func (p *PendingPartialWithdrawal) HashTreeRootWith(hh ssz.HashWalker) (err error) { + indx := hh.Index() + + // Field (0) 'ValidatorIndex' + hh.PutUint64(p.ValidatorIndex) + + // Field (1) 'Amount' + hh.PutUint64(p.Amount) + + // Field (2) 'WithdrawableEpoch' + hh.PutUint64(p.WithdrawableEpoch) + + hh.Merkleize(indx) + return +} + +// GetTree ssz hashes the PendingPartialWithdrawal object +func (p *PendingPartialWithdrawal) GetTree() (*ssz.Node, error) { + return ssz.ProofTree(p) +} + +// MarshalSSZ ssz marshals the PendingConsolidation object +func (p *PendingConsolidation) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(p) +} + +// MarshalSSZTo ssz marshals the PendingConsolidation object to a target array +func (p *PendingConsolidation) MarshalSSZTo(buf []byte) (dst []byte, err error) { + dst = buf + + // Field (0) 'SourceIndex' + dst = ssz.MarshalUint64(dst, p.SourceIndex) + + // Field (1) 'TargetIndex' + dst = ssz.MarshalUint64(dst, p.TargetIndex) + + return +} + +// UnmarshalSSZ ssz unmarshals the PendingConsolidation object +func (p *PendingConsolidation) UnmarshalSSZ(buf []byte) error { + var err error + size := uint64(len(buf)) + if size != 16 { + return ssz.ErrSize + } + + // Field (0) 'SourceIndex' + p.SourceIndex = ssz.UnmarshallUint64(buf[0:8]) + + // Field (1) 'TargetIndex' + p.TargetIndex = ssz.UnmarshallUint64(buf[8:16]) + + return err +} + +// SizeSSZ returns the ssz encoded size in bytes for the PendingConsolidation object +func (p *PendingConsolidation) SizeSSZ() (size int) { + size = 16 + return +} + +// HashTreeRoot ssz hashes the PendingConsolidation object +func (p *PendingConsolidation) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(p) +} + +// HashTreeRootWith ssz hashes the PendingConsolidation object with a hasher +func (p *PendingConsolidation) HashTreeRootWith(hh ssz.HashWalker) (err error) { + indx := hh.Index() + + // Field (0) 'SourceIndex' + hh.PutUint64(p.SourceIndex) + + // Field (1) 'TargetIndex' + hh.PutUint64(p.TargetIndex) + + hh.Merkleize(indx) + return +} + +// GetTree ssz hashes the PendingConsolidation object +func (p *PendingConsolidation) GetTree() (*ssz.Node, error) { + return ssz.ProofTree(p) +} + +// MarshalSSZ ssz marshals the ExecutionPayloadHeader object +func (e *ExecutionPayloadHeader) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(e) +} + +// MarshalSSZTo ssz marshals the ExecutionPayloadHeader object to a target array +func (e *ExecutionPayloadHeader) MarshalSSZTo(buf []byte) (dst []byte, err error) { + dst = buf + offset := int(584) + + // Field (0) 'ParentHash' + dst = append(dst, e.ParentHash[:]...) + + // Field (1) 'FeeRecipient' + dst = append(dst, e.FeeRecipient[:]...) + + // Field (2) 'StateRoot' + dst = append(dst, e.StateRoot[:]...) + + // Field (3) 'ReceiptsRoot' + dst = append(dst, e.ReceiptsRoot[:]...) + + // Field (4) 'LogsBloom' + dst = append(dst, e.LogsBloom[:]...) + + // Field (5) 'PrevRandao' + dst = append(dst, e.PrevRandao[:]...) + + // Field (6) 'BlockNumber' + dst = ssz.MarshalUint64(dst, e.BlockNumber) + + // Field (7) 'GasLimit' + dst = ssz.MarshalUint64(dst, e.GasLimit) + + // Field (8) 'GasUsed' + dst = ssz.MarshalUint64(dst, e.GasUsed) + + // Field (9) 'Timestamp' + dst = ssz.MarshalUint64(dst, e.Timestamp) + + // Offset (10) 'ExtraData' + dst = ssz.WriteOffset(dst, offset) + + // Field (11) 'BaseFeePerGas' + dst = append(dst, e.BaseFeePerGas[:]...) + + // Field (12) 'BlockHash' + dst = append(dst, e.BlockHash[:]...) + + // Field (13) 'TransactionsRoot' + dst = append(dst, e.TransactionsRoot[:]...) + + // Field (14) 'WithdrawalRoot' + dst = append(dst, e.WithdrawalRoot[:]...) + + // Field (15) 'BlobGasUsed' + dst = ssz.MarshalUint64(dst, e.BlobGasUsed) + + // Field (16) 'ExcessBlobGas' + dst = ssz.MarshalUint64(dst, e.ExcessBlobGas) + + // Field (10) 'ExtraData' + if size := len(e.ExtraData); size > 32 { + err = ssz.ErrBytesLengthFn("ExecutionPayloadHeader.ExtraData", size, 32) + return + } + dst = append(dst, e.ExtraData...) + + return +} + +// UnmarshalSSZ ssz unmarshals the ExecutionPayloadHeader object +func (e *ExecutionPayloadHeader) UnmarshalSSZ(buf []byte) error { + var err error + size := uint64(len(buf)) + if size < 584 { + return ssz.ErrSize + } + + tail := buf + var o10 uint64 + + // Field (0) 'ParentHash' + copy(e.ParentHash[:], buf[0:32]) + + // Field (1) 'FeeRecipient' + copy(e.FeeRecipient[:], buf[32:52]) + + // Field (2) 'StateRoot' + copy(e.StateRoot[:], buf[52:84]) + + // Field (3) 'ReceiptsRoot' + copy(e.ReceiptsRoot[:], buf[84:116]) + + // Field (4) 'LogsBloom' + copy(e.LogsBloom[:], buf[116:372]) + + // Field (5) 'PrevRandao' + copy(e.PrevRandao[:], buf[372:404]) + + // Field (6) 'BlockNumber' + e.BlockNumber = ssz.UnmarshallUint64(buf[404:412]) + + // Field (7) 'GasLimit' + e.GasLimit = ssz.UnmarshallUint64(buf[412:420]) + + // Field (8) 'GasUsed' + e.GasUsed = ssz.UnmarshallUint64(buf[420:428]) + + // Field (9) 'Timestamp' + e.Timestamp = ssz.UnmarshallUint64(buf[428:436]) + + // Offset (10) 'ExtraData' + if o10 = ssz.ReadOffset(buf[436:440]); o10 > size { + return ssz.ErrOffset + } + + if o10 != 584 { + return ssz.ErrInvalidVariableOffset + } + + // Field (11) 'BaseFeePerGas' + copy(e.BaseFeePerGas[:], buf[440:472]) + + // Field (12) 'BlockHash' + copy(e.BlockHash[:], buf[472:504]) + + // Field (13) 'TransactionsRoot' + copy(e.TransactionsRoot[:], buf[504:536]) + + // Field (14) 'WithdrawalRoot' + copy(e.WithdrawalRoot[:], buf[536:568]) + + // Field (15) 'BlobGasUsed' + e.BlobGasUsed = ssz.UnmarshallUint64(buf[568:576]) + + // Field (16) 'ExcessBlobGas' + e.ExcessBlobGas = ssz.UnmarshallUint64(buf[576:584]) + + // Field (10) 'ExtraData' + { + buf = tail[o10:] + if len(buf) > 32 { + return ssz.ErrBytesLength + } + if cap(e.ExtraData) == 0 { + e.ExtraData = make([]byte, 0, len(buf)) + } + e.ExtraData = append(e.ExtraData, buf...) + } + return err +} + +// SizeSSZ returns the ssz encoded size in bytes for the ExecutionPayloadHeader object +func (e *ExecutionPayloadHeader) SizeSSZ() (size int) { + size = 584 + + // Field (10) 'ExtraData' + size += len(e.ExtraData) + + return +} + +// HashTreeRoot ssz hashes the ExecutionPayloadHeader object +func (e *ExecutionPayloadHeader) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(e) +} + +// HashTreeRootWith ssz hashes the ExecutionPayloadHeader object with a hasher +func (e *ExecutionPayloadHeader) HashTreeRootWith(hh ssz.HashWalker) (err error) { + indx := hh.Index() + + // Field (0) 'ParentHash' + hh.PutBytes(e.ParentHash[:]) + + // Field (1) 'FeeRecipient' + hh.PutBytes(e.FeeRecipient[:]) + + // Field (2) 'StateRoot' + hh.PutBytes(e.StateRoot[:]) + + // Field (3) 'ReceiptsRoot' + hh.PutBytes(e.ReceiptsRoot[:]) + + // Field (4) 'LogsBloom' + hh.PutBytes(e.LogsBloom[:]) + + // Field (5) 'PrevRandao' + hh.PutBytes(e.PrevRandao[:]) + + // Field (6) 'BlockNumber' + hh.PutUint64(e.BlockNumber) + + // Field (7) 'GasLimit' + hh.PutUint64(e.GasLimit) + + // Field (8) 'GasUsed' + hh.PutUint64(e.GasUsed) + + // Field (9) 'Timestamp' + hh.PutUint64(e.Timestamp) + + // Field (10) 'ExtraData' + { + elemIndx := hh.Index() + byteLen := uint64(len(e.ExtraData)) + if byteLen > 32 { + err = ssz.ErrIncorrectListSize + return + } + hh.Append(e.ExtraData) + hh.MerkleizeWithMixin(elemIndx, byteLen, (32+31)/32) + } + + // Field (11) 'BaseFeePerGas' + hh.PutBytes(e.BaseFeePerGas[:]) + + // Field (12) 'BlockHash' + hh.PutBytes(e.BlockHash[:]) + + // Field (13) 'TransactionsRoot' + hh.PutBytes(e.TransactionsRoot[:]) + + // Field (14) 'WithdrawalRoot' + hh.PutBytes(e.WithdrawalRoot[:]) + + // Field (15) 'BlobGasUsed' + hh.PutUint64(e.BlobGasUsed) + + // Field (16) 'ExcessBlobGas' + hh.PutUint64(e.ExcessBlobGas) + + hh.Merkleize(indx) + return +} + +// GetTree ssz hashes the ExecutionPayloadHeader object +func (e *ExecutionPayloadHeader) GetTree() (*ssz.Node, error) { + return ssz.ProofTree(e) +} diff --git a/shared/types/eth2/generic/types.go b/shared/types/eth2/generic/types.go new file mode 100644 index 000000000..101993f10 --- /dev/null +++ b/shared/types/eth2/generic/types.go @@ -0,0 +1,92 @@ +package generic + +const SlotsPerHistoricalRoot uint64 = 8192 + +// Deposit data (with no signature field) +type DepositDataNoSignature struct { + PublicKey []byte `json:"pubkey" ssz-size:"48"` + WithdrawalCredentials []byte `json:"withdrawal_credentials" ssz-size:"32"` + Amount uint64 `json:"amount"` +} + +// Deposit data (including signature) +type DepositData struct { + PublicKey []byte `json:"pubkey" ssz-size:"48"` + WithdrawalCredentials []byte `json:"withdrawal_credentials" ssz-size:"32"` + Amount uint64 `json:"amount"` + Signature []byte `json:"signature" ssz-size:"96"` +} + +// BLS signing root with domain +type SigningRoot struct { + ObjectRoot []byte `json:"object_root" ssz-size:"32"` + Domain []byte `json:"domain" ssz-size:"32"` +} + +// Voluntary exit transaction +type VoluntaryExit struct { + Epoch uint64 `json:"epoch"` + ValidatorIndex uint64 `json:"validator_index"` +} + +// Withdrawal creds change message +type WithdrawalCredentialsChange struct { + ValidatorIndex uint64 `json:"validator_index"` + FromBLSPubkey [48]byte `json:"from_bls_pubkey" ssz-size:"48"` + ToExecutionAddress [20]byte `json:"to_execution_address" ssz-size:"20"` +} + +// Generic types + +type Uint256 [32]byte + +type Fork struct { + PreviousVersion []byte `json:"previous_version" ssz-size:"4"` + CurrentVersion []byte `json:"current_version" ssz-size:"4"` + Epoch uint64 `json:"epoch"` +} + +type BeaconBlockHeader struct { + Slot uint64 `json:"slot"` + ProposerIndex uint64 `json:"proposer_index"` + ParentRoot []byte `json:"parent_root" ssz-size:"32"` + StateRoot []byte `json:"state_root" ssz-size:"32"` + BodyRoot []byte `json:"body_root" ssz-size:"32"` +} + +type Eth1Data struct { + DepositRoot []byte `json:"deposit_root" ssz-size:"32"` + DepositCount uint64 `json:"deposit_count"` + BlockHash []byte `json:"block_hash" ssz-size:"32"` +} + +type Validator struct { + Pubkey []byte `json:"pubkey" ssz-size:"48"` + WithdrawalCredentials []byte `json:"withdrawal_credentials" ssz-size:"32"` + EffectiveBalance uint64 `json:"effective_balance"` + Slashed bool `json:"slashed"` + ActivationEligibilityEpoch uint64 `json:"activation_eligibility_epoch"` + ActivationEpoch uint64 `json:"activation_epoch"` + ExitEpoch uint64 `json:"exit_epoch"` + WithdrawableEpoch uint64 `json:"withdrawable_epoch"` +} + +type Checkpoint struct { + Epoch uint64 `json:"epoch"` + Root []byte `json:"root" ssz-size:"32"` +} + +type SyncCommittee struct { + PubKeys [][]byte `json:"pubkeys" ssz-size:"512,48"` + AggregatePubKey [48]byte `json:"aggregate_pubkey" ssz-size:"48"` +} + +type HistoricalSummary struct { + BlockSummaryRoot [32]byte `json:"block_summary_root" ssz-size:"32"` + StateSummaryRoot [32]byte `json:"state_summary_root" ssz-size:"32"` +} + +type HistoricalSummaryLists struct { + BlockRoots [8192][32]byte `json:"block_roots" ssz-size:"8192,32"` + StateRoots [8192][32]byte `json:"state_roots" ssz-size:"8192,32"` +} diff --git a/shared/types/eth2/generic/types_encoding.go b/shared/types/eth2/generic/types_encoding.go new file mode 100644 index 000000000..596f34378 --- /dev/null +++ b/shared/types/eth2/generic/types_encoding.go @@ -0,0 +1,1227 @@ +// Code generated by fastssz. DO NOT EDIT. +// Hash: 3fba9f7b919dd4fa010d06a5c0b339f6a3643424cec6377e930e2b3569a71338 +// Version: 0.1.3 +package generic + +import ( + ssz "github.com/ferranbt/fastssz" +) + +// MarshalSSZ ssz marshals the DepositDataNoSignature object +func (d *DepositDataNoSignature) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(d) +} + +// MarshalSSZTo ssz marshals the DepositDataNoSignature object to a target array +func (d *DepositDataNoSignature) MarshalSSZTo(buf []byte) (dst []byte, err error) { + dst = buf + + // Field (0) 'PublicKey' + if size := len(d.PublicKey); size != 48 { + err = ssz.ErrBytesLengthFn("DepositDataNoSignature.PublicKey", size, 48) + return + } + dst = append(dst, d.PublicKey...) + + // Field (1) 'WithdrawalCredentials' + if size := len(d.WithdrawalCredentials); size != 32 { + err = ssz.ErrBytesLengthFn("DepositDataNoSignature.WithdrawalCredentials", size, 32) + return + } + dst = append(dst, d.WithdrawalCredentials...) + + // Field (2) 'Amount' + dst = ssz.MarshalUint64(dst, d.Amount) + + return +} + +// UnmarshalSSZ ssz unmarshals the DepositDataNoSignature object +func (d *DepositDataNoSignature) UnmarshalSSZ(buf []byte) error { + var err error + size := uint64(len(buf)) + if size != 88 { + return ssz.ErrSize + } + + // Field (0) 'PublicKey' + if cap(d.PublicKey) == 0 { + d.PublicKey = make([]byte, 0, len(buf[0:48])) + } + d.PublicKey = append(d.PublicKey, buf[0:48]...) + + // Field (1) 'WithdrawalCredentials' + if cap(d.WithdrawalCredentials) == 0 { + d.WithdrawalCredentials = make([]byte, 0, len(buf[48:80])) + } + d.WithdrawalCredentials = append(d.WithdrawalCredentials, buf[48:80]...) + + // Field (2) 'Amount' + d.Amount = ssz.UnmarshallUint64(buf[80:88]) + + return err +} + +// SizeSSZ returns the ssz encoded size in bytes for the DepositDataNoSignature object +func (d *DepositDataNoSignature) SizeSSZ() (size int) { + size = 88 + return +} + +// HashTreeRoot ssz hashes the DepositDataNoSignature object +func (d *DepositDataNoSignature) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(d) +} + +// HashTreeRootWith ssz hashes the DepositDataNoSignature object with a hasher +func (d *DepositDataNoSignature) HashTreeRootWith(hh ssz.HashWalker) (err error) { + indx := hh.Index() + + // Field (0) 'PublicKey' + if size := len(d.PublicKey); size != 48 { + err = ssz.ErrBytesLengthFn("DepositDataNoSignature.PublicKey", size, 48) + return + } + hh.PutBytes(d.PublicKey) + + // Field (1) 'WithdrawalCredentials' + if size := len(d.WithdrawalCredentials); size != 32 { + err = ssz.ErrBytesLengthFn("DepositDataNoSignature.WithdrawalCredentials", size, 32) + return + } + hh.PutBytes(d.WithdrawalCredentials) + + // Field (2) 'Amount' + hh.PutUint64(d.Amount) + + hh.Merkleize(indx) + return +} + +// GetTree ssz hashes the DepositDataNoSignature object +func (d *DepositDataNoSignature) GetTree() (*ssz.Node, error) { + return ssz.ProofTree(d) +} + +// MarshalSSZ ssz marshals the DepositData object +func (d *DepositData) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(d) +} + +// MarshalSSZTo ssz marshals the DepositData object to a target array +func (d *DepositData) MarshalSSZTo(buf []byte) (dst []byte, err error) { + dst = buf + + // Field (0) 'PublicKey' + if size := len(d.PublicKey); size != 48 { + err = ssz.ErrBytesLengthFn("DepositData.PublicKey", size, 48) + return + } + dst = append(dst, d.PublicKey...) + + // Field (1) 'WithdrawalCredentials' + if size := len(d.WithdrawalCredentials); size != 32 { + err = ssz.ErrBytesLengthFn("DepositData.WithdrawalCredentials", size, 32) + return + } + dst = append(dst, d.WithdrawalCredentials...) + + // Field (2) 'Amount' + dst = ssz.MarshalUint64(dst, d.Amount) + + // Field (3) 'Signature' + if size := len(d.Signature); size != 96 { + err = ssz.ErrBytesLengthFn("DepositData.Signature", size, 96) + return + } + dst = append(dst, d.Signature...) + + return +} + +// UnmarshalSSZ ssz unmarshals the DepositData object +func (d *DepositData) UnmarshalSSZ(buf []byte) error { + var err error + size := uint64(len(buf)) + if size != 184 { + return ssz.ErrSize + } + + // Field (0) 'PublicKey' + if cap(d.PublicKey) == 0 { + d.PublicKey = make([]byte, 0, len(buf[0:48])) + } + d.PublicKey = append(d.PublicKey, buf[0:48]...) + + // Field (1) 'WithdrawalCredentials' + if cap(d.WithdrawalCredentials) == 0 { + d.WithdrawalCredentials = make([]byte, 0, len(buf[48:80])) + } + d.WithdrawalCredentials = append(d.WithdrawalCredentials, buf[48:80]...) + + // Field (2) 'Amount' + d.Amount = ssz.UnmarshallUint64(buf[80:88]) + + // Field (3) 'Signature' + if cap(d.Signature) == 0 { + d.Signature = make([]byte, 0, len(buf[88:184])) + } + d.Signature = append(d.Signature, buf[88:184]...) + + return err +} + +// SizeSSZ returns the ssz encoded size in bytes for the DepositData object +func (d *DepositData) SizeSSZ() (size int) { + size = 184 + return +} + +// HashTreeRoot ssz hashes the DepositData object +func (d *DepositData) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(d) +} + +// HashTreeRootWith ssz hashes the DepositData object with a hasher +func (d *DepositData) HashTreeRootWith(hh ssz.HashWalker) (err error) { + indx := hh.Index() + + // Field (0) 'PublicKey' + if size := len(d.PublicKey); size != 48 { + err = ssz.ErrBytesLengthFn("DepositData.PublicKey", size, 48) + return + } + hh.PutBytes(d.PublicKey) + + // Field (1) 'WithdrawalCredentials' + if size := len(d.WithdrawalCredentials); size != 32 { + err = ssz.ErrBytesLengthFn("DepositData.WithdrawalCredentials", size, 32) + return + } + hh.PutBytes(d.WithdrawalCredentials) + + // Field (2) 'Amount' + hh.PutUint64(d.Amount) + + // Field (3) 'Signature' + if size := len(d.Signature); size != 96 { + err = ssz.ErrBytesLengthFn("DepositData.Signature", size, 96) + return + } + hh.PutBytes(d.Signature) + + hh.Merkleize(indx) + return +} + +// GetTree ssz hashes the DepositData object +func (d *DepositData) GetTree() (*ssz.Node, error) { + return ssz.ProofTree(d) +} + +// MarshalSSZ ssz marshals the SigningRoot object +func (s *SigningRoot) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(s) +} + +// MarshalSSZTo ssz marshals the SigningRoot object to a target array +func (s *SigningRoot) MarshalSSZTo(buf []byte) (dst []byte, err error) { + dst = buf + + // Field (0) 'ObjectRoot' + if size := len(s.ObjectRoot); size != 32 { + err = ssz.ErrBytesLengthFn("SigningRoot.ObjectRoot", size, 32) + return + } + dst = append(dst, s.ObjectRoot...) + + // Field (1) 'Domain' + if size := len(s.Domain); size != 32 { + err = ssz.ErrBytesLengthFn("SigningRoot.Domain", size, 32) + return + } + dst = append(dst, s.Domain...) + + return +} + +// UnmarshalSSZ ssz unmarshals the SigningRoot object +func (s *SigningRoot) UnmarshalSSZ(buf []byte) error { + var err error + size := uint64(len(buf)) + if size != 64 { + return ssz.ErrSize + } + + // Field (0) 'ObjectRoot' + if cap(s.ObjectRoot) == 0 { + s.ObjectRoot = make([]byte, 0, len(buf[0:32])) + } + s.ObjectRoot = append(s.ObjectRoot, buf[0:32]...) + + // Field (1) 'Domain' + if cap(s.Domain) == 0 { + s.Domain = make([]byte, 0, len(buf[32:64])) + } + s.Domain = append(s.Domain, buf[32:64]...) + + return err +} + +// SizeSSZ returns the ssz encoded size in bytes for the SigningRoot object +func (s *SigningRoot) SizeSSZ() (size int) { + size = 64 + return +} + +// HashTreeRoot ssz hashes the SigningRoot object +func (s *SigningRoot) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(s) +} + +// HashTreeRootWith ssz hashes the SigningRoot object with a hasher +func (s *SigningRoot) HashTreeRootWith(hh ssz.HashWalker) (err error) { + indx := hh.Index() + + // Field (0) 'ObjectRoot' + if size := len(s.ObjectRoot); size != 32 { + err = ssz.ErrBytesLengthFn("SigningRoot.ObjectRoot", size, 32) + return + } + hh.PutBytes(s.ObjectRoot) + + // Field (1) 'Domain' + if size := len(s.Domain); size != 32 { + err = ssz.ErrBytesLengthFn("SigningRoot.Domain", size, 32) + return + } + hh.PutBytes(s.Domain) + + hh.Merkleize(indx) + return +} + +// GetTree ssz hashes the SigningRoot object +func (s *SigningRoot) GetTree() (*ssz.Node, error) { + return ssz.ProofTree(s) +} + +// MarshalSSZ ssz marshals the VoluntaryExit object +func (v *VoluntaryExit) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(v) +} + +// MarshalSSZTo ssz marshals the VoluntaryExit object to a target array +func (v *VoluntaryExit) MarshalSSZTo(buf []byte) (dst []byte, err error) { + dst = buf + + // Field (0) 'Epoch' + dst = ssz.MarshalUint64(dst, v.Epoch) + + // Field (1) 'ValidatorIndex' + dst = ssz.MarshalUint64(dst, v.ValidatorIndex) + + return +} + +// UnmarshalSSZ ssz unmarshals the VoluntaryExit object +func (v *VoluntaryExit) UnmarshalSSZ(buf []byte) error { + var err error + size := uint64(len(buf)) + if size != 16 { + return ssz.ErrSize + } + + // Field (0) 'Epoch' + v.Epoch = ssz.UnmarshallUint64(buf[0:8]) + + // Field (1) 'ValidatorIndex' + v.ValidatorIndex = ssz.UnmarshallUint64(buf[8:16]) + + return err +} + +// SizeSSZ returns the ssz encoded size in bytes for the VoluntaryExit object +func (v *VoluntaryExit) SizeSSZ() (size int) { + size = 16 + return +} + +// HashTreeRoot ssz hashes the VoluntaryExit object +func (v *VoluntaryExit) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(v) +} + +// HashTreeRootWith ssz hashes the VoluntaryExit object with a hasher +func (v *VoluntaryExit) HashTreeRootWith(hh ssz.HashWalker) (err error) { + indx := hh.Index() + + // Field (0) 'Epoch' + hh.PutUint64(v.Epoch) + + // Field (1) 'ValidatorIndex' + hh.PutUint64(v.ValidatorIndex) + + hh.Merkleize(indx) + return +} + +// GetTree ssz hashes the VoluntaryExit object +func (v *VoluntaryExit) GetTree() (*ssz.Node, error) { + return ssz.ProofTree(v) +} + +// MarshalSSZ ssz marshals the WithdrawalCredentialsChange object +func (w *WithdrawalCredentialsChange) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(w) +} + +// MarshalSSZTo ssz marshals the WithdrawalCredentialsChange object to a target array +func (w *WithdrawalCredentialsChange) MarshalSSZTo(buf []byte) (dst []byte, err error) { + dst = buf + + // Field (0) 'ValidatorIndex' + dst = ssz.MarshalUint64(dst, w.ValidatorIndex) + + // Field (1) 'FromBLSPubkey' + dst = append(dst, w.FromBLSPubkey[:]...) + + // Field (2) 'ToExecutionAddress' + dst = append(dst, w.ToExecutionAddress[:]...) + + return +} + +// UnmarshalSSZ ssz unmarshals the WithdrawalCredentialsChange object +func (w *WithdrawalCredentialsChange) UnmarshalSSZ(buf []byte) error { + var err error + size := uint64(len(buf)) + if size != 76 { + return ssz.ErrSize + } + + // Field (0) 'ValidatorIndex' + w.ValidatorIndex = ssz.UnmarshallUint64(buf[0:8]) + + // Field (1) 'FromBLSPubkey' + copy(w.FromBLSPubkey[:], buf[8:56]) + + // Field (2) 'ToExecutionAddress' + copy(w.ToExecutionAddress[:], buf[56:76]) + + return err +} + +// SizeSSZ returns the ssz encoded size in bytes for the WithdrawalCredentialsChange object +func (w *WithdrawalCredentialsChange) SizeSSZ() (size int) { + size = 76 + return +} + +// HashTreeRoot ssz hashes the WithdrawalCredentialsChange object +func (w *WithdrawalCredentialsChange) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(w) +} + +// HashTreeRootWith ssz hashes the WithdrawalCredentialsChange object with a hasher +func (w *WithdrawalCredentialsChange) HashTreeRootWith(hh ssz.HashWalker) (err error) { + indx := hh.Index() + + // Field (0) 'ValidatorIndex' + hh.PutUint64(w.ValidatorIndex) + + // Field (1) 'FromBLSPubkey' + hh.PutBytes(w.FromBLSPubkey[:]) + + // Field (2) 'ToExecutionAddress' + hh.PutBytes(w.ToExecutionAddress[:]) + + hh.Merkleize(indx) + return +} + +// GetTree ssz hashes the WithdrawalCredentialsChange object +func (w *WithdrawalCredentialsChange) GetTree() (*ssz.Node, error) { + return ssz.ProofTree(w) +} + +// MarshalSSZ ssz marshals the Fork object +func (f *Fork) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(f) +} + +// MarshalSSZTo ssz marshals the Fork object to a target array +func (f *Fork) MarshalSSZTo(buf []byte) (dst []byte, err error) { + dst = buf + + // Field (0) 'PreviousVersion' + if size := len(f.PreviousVersion); size != 4 { + err = ssz.ErrBytesLengthFn("Fork.PreviousVersion", size, 4) + return + } + dst = append(dst, f.PreviousVersion...) + + // Field (1) 'CurrentVersion' + if size := len(f.CurrentVersion); size != 4 { + err = ssz.ErrBytesLengthFn("Fork.CurrentVersion", size, 4) + return + } + dst = append(dst, f.CurrentVersion...) + + // Field (2) 'Epoch' + dst = ssz.MarshalUint64(dst, f.Epoch) + + return +} + +// UnmarshalSSZ ssz unmarshals the Fork object +func (f *Fork) UnmarshalSSZ(buf []byte) error { + var err error + size := uint64(len(buf)) + if size != 16 { + return ssz.ErrSize + } + + // Field (0) 'PreviousVersion' + if cap(f.PreviousVersion) == 0 { + f.PreviousVersion = make([]byte, 0, len(buf[0:4])) + } + f.PreviousVersion = append(f.PreviousVersion, buf[0:4]...) + + // Field (1) 'CurrentVersion' + if cap(f.CurrentVersion) == 0 { + f.CurrentVersion = make([]byte, 0, len(buf[4:8])) + } + f.CurrentVersion = append(f.CurrentVersion, buf[4:8]...) + + // Field (2) 'Epoch' + f.Epoch = ssz.UnmarshallUint64(buf[8:16]) + + return err +} + +// SizeSSZ returns the ssz encoded size in bytes for the Fork object +func (f *Fork) SizeSSZ() (size int) { + size = 16 + return +} + +// HashTreeRoot ssz hashes the Fork object +func (f *Fork) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(f) +} + +// HashTreeRootWith ssz hashes the Fork object with a hasher +func (f *Fork) HashTreeRootWith(hh ssz.HashWalker) (err error) { + indx := hh.Index() + + // Field (0) 'PreviousVersion' + if size := len(f.PreviousVersion); size != 4 { + err = ssz.ErrBytesLengthFn("Fork.PreviousVersion", size, 4) + return + } + hh.PutBytes(f.PreviousVersion) + + // Field (1) 'CurrentVersion' + if size := len(f.CurrentVersion); size != 4 { + err = ssz.ErrBytesLengthFn("Fork.CurrentVersion", size, 4) + return + } + hh.PutBytes(f.CurrentVersion) + + // Field (2) 'Epoch' + hh.PutUint64(f.Epoch) + + hh.Merkleize(indx) + return +} + +// GetTree ssz hashes the Fork object +func (f *Fork) GetTree() (*ssz.Node, error) { + return ssz.ProofTree(f) +} + +// MarshalSSZ ssz marshals the BeaconBlockHeader object +func (b *BeaconBlockHeader) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(b) +} + +// MarshalSSZTo ssz marshals the BeaconBlockHeader object to a target array +func (b *BeaconBlockHeader) MarshalSSZTo(buf []byte) (dst []byte, err error) { + dst = buf + + // Field (0) 'Slot' + dst = ssz.MarshalUint64(dst, b.Slot) + + // Field (1) 'ProposerIndex' + dst = ssz.MarshalUint64(dst, b.ProposerIndex) + + // Field (2) 'ParentRoot' + if size := len(b.ParentRoot); size != 32 { + err = ssz.ErrBytesLengthFn("BeaconBlockHeader.ParentRoot", size, 32) + return + } + dst = append(dst, b.ParentRoot...) + + // Field (3) 'StateRoot' + if size := len(b.StateRoot); size != 32 { + err = ssz.ErrBytesLengthFn("BeaconBlockHeader.StateRoot", size, 32) + return + } + dst = append(dst, b.StateRoot...) + + // Field (4) 'BodyRoot' + if size := len(b.BodyRoot); size != 32 { + err = ssz.ErrBytesLengthFn("BeaconBlockHeader.BodyRoot", size, 32) + return + } + dst = append(dst, b.BodyRoot...) + + return +} + +// UnmarshalSSZ ssz unmarshals the BeaconBlockHeader object +func (b *BeaconBlockHeader) UnmarshalSSZ(buf []byte) error { + var err error + size := uint64(len(buf)) + if size != 112 { + return ssz.ErrSize + } + + // Field (0) 'Slot' + b.Slot = ssz.UnmarshallUint64(buf[0:8]) + + // Field (1) 'ProposerIndex' + b.ProposerIndex = ssz.UnmarshallUint64(buf[8:16]) + + // Field (2) 'ParentRoot' + if cap(b.ParentRoot) == 0 { + b.ParentRoot = make([]byte, 0, len(buf[16:48])) + } + b.ParentRoot = append(b.ParentRoot, buf[16:48]...) + + // Field (3) 'StateRoot' + if cap(b.StateRoot) == 0 { + b.StateRoot = make([]byte, 0, len(buf[48:80])) + } + b.StateRoot = append(b.StateRoot, buf[48:80]...) + + // Field (4) 'BodyRoot' + if cap(b.BodyRoot) == 0 { + b.BodyRoot = make([]byte, 0, len(buf[80:112])) + } + b.BodyRoot = append(b.BodyRoot, buf[80:112]...) + + return err +} + +// SizeSSZ returns the ssz encoded size in bytes for the BeaconBlockHeader object +func (b *BeaconBlockHeader) SizeSSZ() (size int) { + size = 112 + return +} + +// HashTreeRoot ssz hashes the BeaconBlockHeader object +func (b *BeaconBlockHeader) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(b) +} + +// HashTreeRootWith ssz hashes the BeaconBlockHeader object with a hasher +func (b *BeaconBlockHeader) HashTreeRootWith(hh ssz.HashWalker) (err error) { + indx := hh.Index() + + // Field (0) 'Slot' + hh.PutUint64(b.Slot) + + // Field (1) 'ProposerIndex' + hh.PutUint64(b.ProposerIndex) + + // Field (2) 'ParentRoot' + if size := len(b.ParentRoot); size != 32 { + err = ssz.ErrBytesLengthFn("BeaconBlockHeader.ParentRoot", size, 32) + return + } + hh.PutBytes(b.ParentRoot) + + // Field (3) 'StateRoot' + if size := len(b.StateRoot); size != 32 { + err = ssz.ErrBytesLengthFn("BeaconBlockHeader.StateRoot", size, 32) + return + } + hh.PutBytes(b.StateRoot) + + // Field (4) 'BodyRoot' + if size := len(b.BodyRoot); size != 32 { + err = ssz.ErrBytesLengthFn("BeaconBlockHeader.BodyRoot", size, 32) + return + } + hh.PutBytes(b.BodyRoot) + + hh.Merkleize(indx) + return +} + +// GetTree ssz hashes the BeaconBlockHeader object +func (b *BeaconBlockHeader) GetTree() (*ssz.Node, error) { + return ssz.ProofTree(b) +} + +// MarshalSSZ ssz marshals the Eth1Data object +func (e *Eth1Data) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(e) +} + +// MarshalSSZTo ssz marshals the Eth1Data object to a target array +func (e *Eth1Data) MarshalSSZTo(buf []byte) (dst []byte, err error) { + dst = buf + + // Field (0) 'DepositRoot' + if size := len(e.DepositRoot); size != 32 { + err = ssz.ErrBytesLengthFn("Eth1Data.DepositRoot", size, 32) + return + } + dst = append(dst, e.DepositRoot...) + + // Field (1) 'DepositCount' + dst = ssz.MarshalUint64(dst, e.DepositCount) + + // Field (2) 'BlockHash' + if size := len(e.BlockHash); size != 32 { + err = ssz.ErrBytesLengthFn("Eth1Data.BlockHash", size, 32) + return + } + dst = append(dst, e.BlockHash...) + + return +} + +// UnmarshalSSZ ssz unmarshals the Eth1Data object +func (e *Eth1Data) UnmarshalSSZ(buf []byte) error { + var err error + size := uint64(len(buf)) + if size != 72 { + return ssz.ErrSize + } + + // Field (0) 'DepositRoot' + if cap(e.DepositRoot) == 0 { + e.DepositRoot = make([]byte, 0, len(buf[0:32])) + } + e.DepositRoot = append(e.DepositRoot, buf[0:32]...) + + // Field (1) 'DepositCount' + e.DepositCount = ssz.UnmarshallUint64(buf[32:40]) + + // Field (2) 'BlockHash' + if cap(e.BlockHash) == 0 { + e.BlockHash = make([]byte, 0, len(buf[40:72])) + } + e.BlockHash = append(e.BlockHash, buf[40:72]...) + + return err +} + +// SizeSSZ returns the ssz encoded size in bytes for the Eth1Data object +func (e *Eth1Data) SizeSSZ() (size int) { + size = 72 + return +} + +// HashTreeRoot ssz hashes the Eth1Data object +func (e *Eth1Data) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(e) +} + +// HashTreeRootWith ssz hashes the Eth1Data object with a hasher +func (e *Eth1Data) HashTreeRootWith(hh ssz.HashWalker) (err error) { + indx := hh.Index() + + // Field (0) 'DepositRoot' + if size := len(e.DepositRoot); size != 32 { + err = ssz.ErrBytesLengthFn("Eth1Data.DepositRoot", size, 32) + return + } + hh.PutBytes(e.DepositRoot) + + // Field (1) 'DepositCount' + hh.PutUint64(e.DepositCount) + + // Field (2) 'BlockHash' + if size := len(e.BlockHash); size != 32 { + err = ssz.ErrBytesLengthFn("Eth1Data.BlockHash", size, 32) + return + } + hh.PutBytes(e.BlockHash) + + hh.Merkleize(indx) + return +} + +// GetTree ssz hashes the Eth1Data object +func (e *Eth1Data) GetTree() (*ssz.Node, error) { + return ssz.ProofTree(e) +} + +// MarshalSSZ ssz marshals the Validator object +func (v *Validator) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(v) +} + +// MarshalSSZTo ssz marshals the Validator object to a target array +func (v *Validator) MarshalSSZTo(buf []byte) (dst []byte, err error) { + dst = buf + + // Field (0) 'Pubkey' + if size := len(v.Pubkey); size != 48 { + err = ssz.ErrBytesLengthFn("Validator.Pubkey", size, 48) + return + } + dst = append(dst, v.Pubkey...) + + // Field (1) 'WithdrawalCredentials' + if size := len(v.WithdrawalCredentials); size != 32 { + err = ssz.ErrBytesLengthFn("Validator.WithdrawalCredentials", size, 32) + return + } + dst = append(dst, v.WithdrawalCredentials...) + + // Field (2) 'EffectiveBalance' + dst = ssz.MarshalUint64(dst, v.EffectiveBalance) + + // Field (3) 'Slashed' + dst = ssz.MarshalBool(dst, v.Slashed) + + // Field (4) 'ActivationEligibilityEpoch' + dst = ssz.MarshalUint64(dst, v.ActivationEligibilityEpoch) + + // Field (5) 'ActivationEpoch' + dst = ssz.MarshalUint64(dst, v.ActivationEpoch) + + // Field (6) 'ExitEpoch' + dst = ssz.MarshalUint64(dst, v.ExitEpoch) + + // Field (7) 'WithdrawableEpoch' + dst = ssz.MarshalUint64(dst, v.WithdrawableEpoch) + + return +} + +// UnmarshalSSZ ssz unmarshals the Validator object +func (v *Validator) UnmarshalSSZ(buf []byte) error { + var err error + size := uint64(len(buf)) + if size != 121 { + return ssz.ErrSize + } + + // Field (0) 'Pubkey' + if cap(v.Pubkey) == 0 { + v.Pubkey = make([]byte, 0, len(buf[0:48])) + } + v.Pubkey = append(v.Pubkey, buf[0:48]...) + + // Field (1) 'WithdrawalCredentials' + if cap(v.WithdrawalCredentials) == 0 { + v.WithdrawalCredentials = make([]byte, 0, len(buf[48:80])) + } + v.WithdrawalCredentials = append(v.WithdrawalCredentials, buf[48:80]...) + + // Field (2) 'EffectiveBalance' + v.EffectiveBalance = ssz.UnmarshallUint64(buf[80:88]) + + // Field (3) 'Slashed' + v.Slashed = ssz.UnmarshalBool(buf[88:89]) + + // Field (4) 'ActivationEligibilityEpoch' + v.ActivationEligibilityEpoch = ssz.UnmarshallUint64(buf[89:97]) + + // Field (5) 'ActivationEpoch' + v.ActivationEpoch = ssz.UnmarshallUint64(buf[97:105]) + + // Field (6) 'ExitEpoch' + v.ExitEpoch = ssz.UnmarshallUint64(buf[105:113]) + + // Field (7) 'WithdrawableEpoch' + v.WithdrawableEpoch = ssz.UnmarshallUint64(buf[113:121]) + + return err +} + +// SizeSSZ returns the ssz encoded size in bytes for the Validator object +func (v *Validator) SizeSSZ() (size int) { + size = 121 + return +} + +// HashTreeRoot ssz hashes the Validator object +func (v *Validator) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(v) +} + +// HashTreeRootWith ssz hashes the Validator object with a hasher +func (v *Validator) HashTreeRootWith(hh ssz.HashWalker) (err error) { + indx := hh.Index() + + // Field (0) 'Pubkey' + if size := len(v.Pubkey); size != 48 { + err = ssz.ErrBytesLengthFn("Validator.Pubkey", size, 48) + return + } + hh.PutBytes(v.Pubkey) + + // Field (1) 'WithdrawalCredentials' + if size := len(v.WithdrawalCredentials); size != 32 { + err = ssz.ErrBytesLengthFn("Validator.WithdrawalCredentials", size, 32) + return + } + hh.PutBytes(v.WithdrawalCredentials) + + // Field (2) 'EffectiveBalance' + hh.PutUint64(v.EffectiveBalance) + + // Field (3) 'Slashed' + hh.PutBool(v.Slashed) + + // Field (4) 'ActivationEligibilityEpoch' + hh.PutUint64(v.ActivationEligibilityEpoch) + + // Field (5) 'ActivationEpoch' + hh.PutUint64(v.ActivationEpoch) + + // Field (6) 'ExitEpoch' + hh.PutUint64(v.ExitEpoch) + + // Field (7) 'WithdrawableEpoch' + hh.PutUint64(v.WithdrawableEpoch) + + hh.Merkleize(indx) + return +} + +// GetTree ssz hashes the Validator object +func (v *Validator) GetTree() (*ssz.Node, error) { + return ssz.ProofTree(v) +} + +// MarshalSSZ ssz marshals the Checkpoint object +func (c *Checkpoint) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(c) +} + +// MarshalSSZTo ssz marshals the Checkpoint object to a target array +func (c *Checkpoint) MarshalSSZTo(buf []byte) (dst []byte, err error) { + dst = buf + + // Field (0) 'Epoch' + dst = ssz.MarshalUint64(dst, c.Epoch) + + // Field (1) 'Root' + if size := len(c.Root); size != 32 { + err = ssz.ErrBytesLengthFn("Checkpoint.Root", size, 32) + return + } + dst = append(dst, c.Root...) + + return +} + +// UnmarshalSSZ ssz unmarshals the Checkpoint object +func (c *Checkpoint) UnmarshalSSZ(buf []byte) error { + var err error + size := uint64(len(buf)) + if size != 40 { + return ssz.ErrSize + } + + // Field (0) 'Epoch' + c.Epoch = ssz.UnmarshallUint64(buf[0:8]) + + // Field (1) 'Root' + if cap(c.Root) == 0 { + c.Root = make([]byte, 0, len(buf[8:40])) + } + c.Root = append(c.Root, buf[8:40]...) + + return err +} + +// SizeSSZ returns the ssz encoded size in bytes for the Checkpoint object +func (c *Checkpoint) SizeSSZ() (size int) { + size = 40 + return +} + +// HashTreeRoot ssz hashes the Checkpoint object +func (c *Checkpoint) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(c) +} + +// HashTreeRootWith ssz hashes the Checkpoint object with a hasher +func (c *Checkpoint) HashTreeRootWith(hh ssz.HashWalker) (err error) { + indx := hh.Index() + + // Field (0) 'Epoch' + hh.PutUint64(c.Epoch) + + // Field (1) 'Root' + if size := len(c.Root); size != 32 { + err = ssz.ErrBytesLengthFn("Checkpoint.Root", size, 32) + return + } + hh.PutBytes(c.Root) + + hh.Merkleize(indx) + return +} + +// GetTree ssz hashes the Checkpoint object +func (c *Checkpoint) GetTree() (*ssz.Node, error) { + return ssz.ProofTree(c) +} + +// MarshalSSZ ssz marshals the SyncCommittee object +func (s *SyncCommittee) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(s) +} + +// MarshalSSZTo ssz marshals the SyncCommittee object to a target array +func (s *SyncCommittee) MarshalSSZTo(buf []byte) (dst []byte, err error) { + dst = buf + + // Field (0) 'PubKeys' + if size := len(s.PubKeys); size != 512 { + err = ssz.ErrVectorLengthFn("SyncCommittee.PubKeys", size, 512) + return + } + for ii := 0; ii < 512; ii++ { + if size := len(s.PubKeys[ii]); size != 48 { + err = ssz.ErrBytesLengthFn("SyncCommittee.PubKeys[ii]", size, 48) + return + } + dst = append(dst, s.PubKeys[ii]...) + } + + // Field (1) 'AggregatePubKey' + dst = append(dst, s.AggregatePubKey[:]...) + + return +} + +// UnmarshalSSZ ssz unmarshals the SyncCommittee object +func (s *SyncCommittee) UnmarshalSSZ(buf []byte) error { + var err error + size := uint64(len(buf)) + if size != 24624 { + return ssz.ErrSize + } + + // Field (0) 'PubKeys' + s.PubKeys = make([][]byte, 512) + for ii := 0; ii < 512; ii++ { + if cap(s.PubKeys[ii]) == 0 { + s.PubKeys[ii] = make([]byte, 0, len(buf[0:24576][ii*48:(ii+1)*48])) + } + s.PubKeys[ii] = append(s.PubKeys[ii], buf[0:24576][ii*48:(ii+1)*48]...) + } + + // Field (1) 'AggregatePubKey' + copy(s.AggregatePubKey[:], buf[24576:24624]) + + return err +} + +// SizeSSZ returns the ssz encoded size in bytes for the SyncCommittee object +func (s *SyncCommittee) SizeSSZ() (size int) { + size = 24624 + return +} + +// HashTreeRoot ssz hashes the SyncCommittee object +func (s *SyncCommittee) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(s) +} + +// HashTreeRootWith ssz hashes the SyncCommittee object with a hasher +func (s *SyncCommittee) HashTreeRootWith(hh ssz.HashWalker) (err error) { + indx := hh.Index() + + // Field (0) 'PubKeys' + { + if size := len(s.PubKeys); size != 512 { + err = ssz.ErrVectorLengthFn("SyncCommittee.PubKeys", size, 512) + return + } + subIndx := hh.Index() + for _, i := range s.PubKeys { + if len(i) != 48 { + err = ssz.ErrBytesLength + return + } + hh.PutBytes(i) + } + hh.Merkleize(subIndx) + } + + // Field (1) 'AggregatePubKey' + hh.PutBytes(s.AggregatePubKey[:]) + + hh.Merkleize(indx) + return +} + +// GetTree ssz hashes the SyncCommittee object +func (s *SyncCommittee) GetTree() (*ssz.Node, error) { + return ssz.ProofTree(s) +} + +// MarshalSSZ ssz marshals the HistoricalSummary object +func (h *HistoricalSummary) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(h) +} + +// MarshalSSZTo ssz marshals the HistoricalSummary object to a target array +func (h *HistoricalSummary) MarshalSSZTo(buf []byte) (dst []byte, err error) { + dst = buf + + // Field (0) 'BlockSummaryRoot' + dst = append(dst, h.BlockSummaryRoot[:]...) + + // Field (1) 'StateSummaryRoot' + dst = append(dst, h.StateSummaryRoot[:]...) + + return +} + +// UnmarshalSSZ ssz unmarshals the HistoricalSummary object +func (h *HistoricalSummary) UnmarshalSSZ(buf []byte) error { + var err error + size := uint64(len(buf)) + if size != 64 { + return ssz.ErrSize + } + + // Field (0) 'BlockSummaryRoot' + copy(h.BlockSummaryRoot[:], buf[0:32]) + + // Field (1) 'StateSummaryRoot' + copy(h.StateSummaryRoot[:], buf[32:64]) + + return err +} + +// SizeSSZ returns the ssz encoded size in bytes for the HistoricalSummary object +func (h *HistoricalSummary) SizeSSZ() (size int) { + size = 64 + return +} + +// HashTreeRoot ssz hashes the HistoricalSummary object +func (h *HistoricalSummary) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(h) +} + +// HashTreeRootWith ssz hashes the HistoricalSummary object with a hasher +func (h *HistoricalSummary) HashTreeRootWith(hh ssz.HashWalker) (err error) { + indx := hh.Index() + + // Field (0) 'BlockSummaryRoot' + hh.PutBytes(h.BlockSummaryRoot[:]) + + // Field (1) 'StateSummaryRoot' + hh.PutBytes(h.StateSummaryRoot[:]) + + hh.Merkleize(indx) + return +} + +// GetTree ssz hashes the HistoricalSummary object +func (h *HistoricalSummary) GetTree() (*ssz.Node, error) { + return ssz.ProofTree(h) +} + +// MarshalSSZ ssz marshals the HistoricalSummaryLists object +func (h *HistoricalSummaryLists) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(h) +} + +// MarshalSSZTo ssz marshals the HistoricalSummaryLists object to a target array +func (h *HistoricalSummaryLists) MarshalSSZTo(buf []byte) (dst []byte, err error) { + dst = buf + + // Field (0) 'BlockRoots' + for ii := 0; ii < 8192; ii++ { + dst = append(dst, h.BlockRoots[ii][:]...) + } + + // Field (1) 'StateRoots' + for ii := 0; ii < 8192; ii++ { + dst = append(dst, h.StateRoots[ii][:]...) + } + + return +} + +// UnmarshalSSZ ssz unmarshals the HistoricalSummaryLists object +func (h *HistoricalSummaryLists) UnmarshalSSZ(buf []byte) error { + var err error + size := uint64(len(buf)) + if size != 524288 { + return ssz.ErrSize + } + + // Field (0) 'BlockRoots' + + for ii := 0; ii < 8192; ii++ { + copy(h.BlockRoots[ii][:], buf[0:262144][ii*32:(ii+1)*32]) + } + + // Field (1) 'StateRoots' + + for ii := 0; ii < 8192; ii++ { + copy(h.StateRoots[ii][:], buf[262144:524288][ii*32:(ii+1)*32]) + } + + return err +} + +// SizeSSZ returns the ssz encoded size in bytes for the HistoricalSummaryLists object +func (h *HistoricalSummaryLists) SizeSSZ() (size int) { + size = 524288 + return +} + +// HashTreeRoot ssz hashes the HistoricalSummaryLists object +func (h *HistoricalSummaryLists) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(h) +} + +// HashTreeRootWith ssz hashes the HistoricalSummaryLists object with a hasher +func (h *HistoricalSummaryLists) HashTreeRootWith(hh ssz.HashWalker) (err error) { + indx := hh.Index() + + // Field (0) 'BlockRoots' + { + subIndx := hh.Index() + for _, i := range h.BlockRoots { + hh.Append(i[:]) + } + hh.Merkleize(subIndx) + } + + // Field (1) 'StateRoots' + { + subIndx := hh.Index() + for _, i := range h.StateRoots { + hh.Append(i[:]) + } + hh.Merkleize(subIndx) + } + + hh.Merkleize(indx) + return +} + +// GetTree ssz hashes the HistoricalSummaryLists object +func (h *HistoricalSummaryLists) GetTree() (*ssz.Node, error) { + return ssz.ProofTree(h) +} diff --git a/shared/types/eth2/generic/validator.go b/shared/types/eth2/generic/validator.go new file mode 100644 index 000000000..d0392e06a --- /dev/null +++ b/shared/types/eth2/generic/validator.go @@ -0,0 +1,51 @@ +package generic + +import ( + "fmt" + + "github.com/rocket-pool/smartnode/shared/utils/math" +) + +const beaconStateValidatorWithdrawalCredentialsPubkeyGeneralizedIndex uint64 = 4 // Container with 8 fields, so gid 8 is the first field. We want the parent of 1st field, so gid 8 / 2 = 4 +const BeaconStateValidatorWithdrawableEpochGeneralizedIndex uint64 = 14 // Container with 8 fields, so gid 8 is the first field. We want the 8th field, so gid 8 + 7 = 15 + +func GetGeneralizedIndexForValidator(index uint64, validatorsArrayIndex uint64) uint64 { + root := validatorsArrayIndex + + // Now, grab the validator index within the list + // `start` is `index * 32` and `pos` is `start / 32` so pos is just `index` + pos := index + baseIndex := uint64(2) // Lists have a base index of 2 + root = root*baseIndex*math.GetPowerOfTwoCeil(beaconStateValidatorsMaxLength) + pos + + // root is now the generalized index for the validator + return root +} + +func (validator *Validator) ValidatorCredentialsPubkeyProof() ([][]byte, error) { + // Just get the portion of the proof for the validator's credentials. + generalizedIndex := beaconStateValidatorWithdrawalCredentialsPubkeyGeneralizedIndex + root, err := validator.GetTree() + if err != nil { + return nil, fmt.Errorf("could not get validator tree: %w", err) + } + proof, err := root.Prove(int(generalizedIndex)) + if err != nil { + return nil, fmt.Errorf("could not get proof for validator credentials: %w", err) + } + return proof.Hashes, nil +} + +func (validator *Validator) ValidatorWithdrawableEpochProof() ([][]byte, error) { + // Just get the portion of the proof for the validator ExitEpoch. + generalizedIndex := BeaconStateValidatorWithdrawableEpochGeneralizedIndex + root, err := validator.GetTree() + if err != nil { + return nil, fmt.Errorf("could not get validator tree: %w", err) + } + proof, err := root.Prove(int(generalizedIndex)) + if err != nil { + return nil, fmt.Errorf("could not get proof for validator withdrawable epoch: %w", err) + } + return proof.Hashes, nil +} diff --git a/shared/types/eth2/proofs_test.go b/shared/types/eth2/proofs_test.go new file mode 100644 index 000000000..ba2716ebf --- /dev/null +++ b/shared/types/eth2/proofs_test.go @@ -0,0 +1,434 @@ +package eth2 + +import ( + "bytes" + "compress/gzip" + "encoding/hex" + "encoding/json" + "fmt" + "io" + "math/bits" + + // Don't get tripped up by the sha256 import + // See https://github.com/ethereum/consensus-specs/pull/779 + "crypto/sha256" + _ "embed" + "testing" + + ssz "github.com/ferranbt/fastssz" + "github.com/rocket-pool/smartnode/shared/types/eth2/fork/deneb" + "github.com/rocket-pool/smartnode/shared/types/eth2/generic" + hexutils "github.com/rocket-pool/smartnode/shared/utils/hex" +) + +// Test state - deneb fork. Hoodi genesis state. +// +//go:embed testdata/hoodi_genesis.ssz.gz +var testState []byte + +//go:embed testdata/block_11544444.ssz +var testBlock []byte + +func init() { + decompressor, err := gzip.NewReader(bytes.NewReader(testState)) + if err != nil { + panic(err) + } + out, err := io.ReadAll(decompressor) + if err != nil { + panic(err) + } + testState = out +} + +func hash(a, b []byte, isReversed bool) []byte { + tmp := [64]byte{} + if isReversed { + copy(tmp[:32], b) + copy(tmp[32:], a) + } else { + copy(tmp[:32], a) + copy(tmp[32:], b) + } + out := sha256.Sum256(tmp[:]) + return out[:] +} + +func offsetGidRoot(gid uint64, newRoot uint64) uint64 { + mulp := gid + if bits.OnesCount64(gid) == 1 { + // gid is a power of 2, so to offset it we just multiply by the new root + return mulp * newRoot + } + add := uint64(0) + // Get the highest power of 2 that is less than, or equal to, gid + clz := bits.LeadingZeros64(gid) + mulp = uint64(0x8000000000000000) >> clz + add = gid - mulp + // mulp is the highest power of 2 that is less than, or equal to, gid + // add is the difference between gid and mulp + // Multiply mulp by the new root and add the remainder + return mulp*newRoot + add +} + +func TestOffsetGidRoot(t *testing.T) { + type c struct { + oldGid uint64 + newRoot uint64 + expectedGid uint64 + } + testCases := []c{ + {oldGid: 1, newRoot: 1, expectedGid: 1}, + {oldGid: 2, newRoot: 1, expectedGid: 2}, + {oldGid: 3, newRoot: 1, expectedGid: 3}, + {oldGid: 4, newRoot: 1, expectedGid: 4}, + {oldGid: 5, newRoot: 1, expectedGid: 5}, + + {oldGid: 1, newRoot: 2, expectedGid: 2}, + {oldGid: 2, newRoot: 2, expectedGid: 4}, + {oldGid: 3, newRoot: 2, expectedGid: 5}, + {oldGid: 4, newRoot: 2, expectedGid: 8}, + {oldGid: 5, newRoot: 2, expectedGid: 9}, + + {oldGid: 1, newRoot: 303, expectedGid: 303}, + {oldGid: 2, newRoot: 303, expectedGid: 606}, + {oldGid: 3, newRoot: 303, expectedGid: 607}, + {oldGid: 4, newRoot: 303, expectedGid: 1212}, + {oldGid: 5, newRoot: 303, expectedGid: 1213}, + } + for _, tc := range testCases { + t.Run(fmt.Sprintf("oldGid: %d, newRoot: %d, expectedGid: %d", tc.oldGid, tc.newRoot, tc.expectedGid), func(t *testing.T) { + result := offsetGidRoot(tc.oldGid, tc.newRoot) + if result != tc.expectedGid { + t.Fatalf("expected gid: %d, got: %d", tc.expectedGid, result) + } + }) + } +} + +func validateStateProof(t *testing.T, leaf []byte, proof [][]byte, gid uint64, state *deneb.BeaconState) ([]byte, []byte) { + // First, offset the gid to account for the fact that state proofs are actually beacon block header proofs + gid = offsetGidRoot(gid, generic.BeaconBlockHeaderStateRootGeneralizedIndex) + currentHash := leaf + + // The gid is now the index of the leaf + for i, proofRow := range proof { + // The last neighbor must have a gid of either 2 or 3 + if i == len(proof)-1 { + if gid != 2 && gid != 3 { + t.Fatalf("last node/neighbor gid must be 2 or 3, got: %d", gid) + } + } + // If the current gid is odd, the neighbor is on the left, otherwise it's on the right + t.Logf("iter: %d, gid: %d, currentHash: %x, proofRow: %x", i, gid, currentHash, proofRow) + neighborIsLeft := gid%2 == 1 + gid /= 2 + + // hash the neighbor and the current hash together + currentHash = hash(currentHash, proofRow, neighborIsLeft) + } + + // Shallow copy the LatestBlockHeader + lbh := *state.LatestBlockHeader + // Restore when done + defer func() { + state.LatestBlockHeader = &lbh + }() + // Set the state root in LatestBlockHeader before calculating the hash + stateRoot, err := state.HashTreeRoot() + if err != nil { + t.Fatalf("Failed to get state root: %v", err) + } + state.LatestBlockHeader.StateRoot = stateRoot[:] + + // The final hash must be the block root + finalHash, err := state.LatestBlockHeader.HashTreeRoot() + if err != nil { + t.Fatalf("Failed to get block root: %v", err) + } + if !bytes.Equal(currentHash, finalHash[:]) { + t.Logf("currentHash: %x", currentHash) + t.Logf("block root: %x", finalHash) + t.Fatalf("final hash does not match block root") + } + + return stateRoot[:], finalHash[:] +} + +func validateValidatorProof(t *testing.T, leaf []byte, proof [][]byte, gid uint64, state *deneb.BeaconState) ([]byte, []byte) { + gid *= 4 + return validateStateProof(t, leaf, proof, gid, state) +} + +func validateWithdrawableEpochProof(t *testing.T, leaf []byte, proof [][]byte, gid uint64, state *deneb.BeaconState) ([]byte, []byte) { + gid = offsetGidRoot(generic.BeaconStateValidatorWithdrawableEpochGeneralizedIndex, gid) + return validateStateProof(t, leaf, proof, gid, state) +} + +func getValidatorLeaf(t *testing.T, validator *generic.Validator) []byte { + // The leaf for a validator is the parent hash of its first two fields. + // this is at gid 4 + tree, err := validator.GetTree() + if err != nil { + t.Fatalf("Failed to get validator tree: %v", err) + } + + leafNode, err := tree.Get(4) + if err != nil { + t.Fatalf("Failed to get validator leaf node: %v", err) + } + + return leafNode.Hash() +} + +func TestWithdrawalCredentialsStateProof(t *testing.T) { + state := &deneb.BeaconState{} + err := state.UnmarshalSSZ(testState) + if err != nil { + t.Fatalf("Failed to unmarshal test state: %v", err) + } + + type tc struct { + validatorIndex uint64 + gid uint64 + } + + testCases := []tc{ + {validatorIndex: 0, gid: 94557999988736}, + {validatorIndex: 111111, gid: 94558000099847}, + {validatorIndex: 555555, gid: 94558000544291}, + } + + // check a few of the proofs using the ssz library + for _, tc := range testCases { + proof, err := state.ValidatorCredentialsProof(tc.validatorIndex) + if err != nil { + t.Fatalf("Failed to get validator credentials proof: %v", err) + } + gid := generic.GetGeneralizedIndexForValidator(tc.validatorIndex, deneb.GetGeneralizedIndexForValidators()) + t.Logf("gid: %v", gid) + if gid != tc.gid { + t.Fatalf("expected gid: %v, got: %v", tc.gid, gid) + } + + stateRoot, blockRoot := validateValidatorProof(t, getValidatorLeaf(t, state.Validators[tc.validatorIndex]), proof, tc.gid, state) + t.Logf("stateRoot: %x", stateRoot) + t.Logf("blockRoot: %x", blockRoot) + expectedStateRoot, err := hex.DecodeString("2683ebc120f91f740c7bed4c866672d01e1ba51b4cc360297138465ee5df40f0") + if err != nil { + panic(err) + } + expectedBlockRoot, err := hex.DecodeString("376450cd7fb9f05ade82a7f88565ac57af449ac696b1a6ac5cc7dac7d467b7d6") + if err != nil { + panic(err) + } + if !bytes.Equal(stateRoot, expectedStateRoot) { + t.Fatalf("expected state root: %x, got: %x", expectedStateRoot, stateRoot) + } + if !bytes.Equal(blockRoot, expectedBlockRoot) { + t.Fatalf("expected block root: %x, got: %x", expectedBlockRoot, blockRoot) + } + } +} + +func TestValidatorWithdrawableEpochProof(t *testing.T) { + state := &deneb.BeaconState{} + err := state.UnmarshalSSZ(testState) + if err != nil { + t.Fatalf("Failed to unmarshal test state: %v", err) + } + + type tc struct { + validatorIndex uint64 + gid uint64 + } + + testCases := []tc{ + {validatorIndex: 0, gid: 94557999988736}, + {validatorIndex: 111111, gid: 94558000099847}, + {validatorIndex: 555555, gid: 94558000544291}, + } + + for _, tc := range testCases { + proof, err := state.ValidatorWithdrawableEpochProof(tc.validatorIndex) + if err != nil { + t.Fatalf("Failed to get validator withdrawable epoch proof: %v", err) + } + gid := generic.GetGeneralizedIndexForValidator(tc.validatorIndex, deneb.GetGeneralizedIndexForValidators()) + t.Logf("gid: %v", gid) + if gid != tc.gid { + t.Fatalf("expected gid: %v, got: %v", tc.gid, gid) + } + + expectedWithdrawableEpoch := state.Validators[tc.validatorIndex].WithdrawableEpoch + leaf := ssz.LeafFromUint64(expectedWithdrawableEpoch) + + stateRoot, blockRoot := validateWithdrawableEpochProof(t, leaf.Hash(), proof, tc.gid, state) + t.Logf("stateRoot: %x", stateRoot) + t.Logf("blockRoot: %x", blockRoot) + expectedStateRoot, err := hex.DecodeString("2683ebc120f91f740c7bed4c866672d01e1ba51b4cc360297138465ee5df40f0") + if err != nil { + panic(err) + } + expectedBlockRoot, err := hex.DecodeString("376450cd7fb9f05ade82a7f88565ac57af449ac696b1a6ac5cc7dac7d467b7d6") + if err != nil { + panic(err) + } + if !bytes.Equal(stateRoot, expectedStateRoot) { + t.Fatalf("expected state root: %x, got: %x", expectedStateRoot, stateRoot) + } + if !bytes.Equal(blockRoot, expectedBlockRoot) { + t.Fatalf("expected block root: %x, got: %x", expectedBlockRoot, blockRoot) + } + } +} + +func validateBlockProof(t *testing.T, leaf [32]byte, proof [][]byte, gid uint64, block *deneb.BeaconBlock) []byte { + savedExepectedBlockRoot, err := hex.DecodeString("8442138d973483bfeaba9082f28217234e2879dedb5202e67ef68e2349db9a31") + if err != nil { + panic(err) + } + expectedBlockRoot, err := block.HashTreeRoot() + if err != nil { + t.Fatalf("Failed to get block root: %v", err) + } + + if !bytes.Equal(expectedBlockRoot[:], savedExepectedBlockRoot) { + t.Fatalf("expected block root: %x, got: %x", savedExepectedBlockRoot, expectedBlockRoot) + } + + currentHash := leaf[:] + for i, proofRow := range proof { + // The last neighbor must have a gid of either 2 or 3 + if i == len(proof)-1 { + if gid != 2 && gid != 3 { + t.Fatalf("last node/neighbor gid must be 2 or 3, got: %d", gid) + } + } + // If the current gid is odd, the neighbor is on the left, otherwise it's on the right + t.Logf("iter: %d, gid: %d, currentHash: %x, proofRow: %x", i, gid, currentHash, proofRow) + neighborIsLeft := gid%2 == 1 + gid /= 2 + + // hash the neighbor and the current hash together + currentHash = hash(currentHash, proofRow, neighborIsLeft) + } + + if !bytes.Equal(currentHash, expectedBlockRoot[:]) { + t.Fatalf("final hash does not match block root") + } else { + t.Logf("final hash %x matches expected block root %x", currentHash, expectedBlockRoot) + } + + return currentHash +} + +func TestWithdrawalProof(t *testing.T) { + block := &deneb.SignedBeaconBlock{} + err := block.UnmarshalSSZ(testBlock) + if err != nil { + t.Fatalf("Failed to unmarshal test block: %v", err) + } + + for idx, withdrawal := range block.Block.Body.ExecutionPayload.Withdrawals { + proof, err := block.Block.ProveWithdrawal(uint64(idx)) + if err != nil { + t.Fatalf("Failed to get withdrawal proof: %v", err) + } + + if testing.Verbose() { + for i, p := range proof { + t.Logf("proof[%d]: %x", i, p) + } + } + gid := uint64(1) + gid = gid*generic.BeaconBlockChunksCeil + generic.BeaconBlockBodyIndex + gid = gid*deneb.BeaconBlockBodyChunksCeil + generic.BeaconBlockBodyExecutionPayloadIndex + gid = gid*generic.BeaconBlockBodyExecutionPayloadChunksCeil + generic.BeaconBlockBodyExecutionPayloadWithdrawalsIndex + gid = gid * 2 + gid = gid*generic.BeaconBlockWithdrawalsArrayMax + uint64(idx) + leaf, err := withdrawal.HashTreeRoot() + if err != nil { + t.Fatalf("Failed to get withdrawal leaf: %v", err) + } + validateBlockProof(t, leaf, proof, gid, block.Block) + } +} + +//go:embed testdata/11567104_roots.json +var testRootsJSON []byte + +type testRoots struct { + BlockRoots []string `json:"block_roots"` + StateRoots []string `json:"state_roots"` +} + +func TestBlockRootProof(t *testing.T) { + // Unmarshal testRootsJSON + var testRoots testRoots + err := json.Unmarshal(testRootsJSON, &testRoots) + if err != nil { + t.Fatalf("Failed to unmarshal test roots: %v", err) + } + + // Convert it to a HistoricalSummaryLists + hsls := generic.HistoricalSummaryLists{} + + for i, blockRoot := range testRoots.BlockRoots { + blockRootBytes, err := hex.DecodeString(hexutils.RemovePrefix(blockRoot)) + if err != nil { + t.Fatalf("Failed to decode block root: %v", err) + } + copy(hsls.BlockRoots[i][:], blockRootBytes) + } + for i, stateRoot := range testRoots.StateRoots { + stateRootBytes, err := hex.DecodeString(hexutils.RemovePrefix(stateRoot)) + if err != nil { + t.Fatalf("Failed to decode state root: %v", err) + } + copy(hsls.StateRoots[i][:], stateRootBytes) + } + + hslsTree, err := hsls.GetTree() + if err != nil { + t.Fatalf("Failed to get historical summary lists tree: %v", err) + } + block_summary_node, err := hslsTree.Get(2) + if err != nil { + t.Fatalf("Failed to get block summary node: %v", err) + } + // Get the root of the block_summary_node + block_summary_root := block_summary_node.Hash() + t.Logf("block_summary_root: %x", block_summary_root) + + // I have verified that this is the correct historical summary root on mainnet for this 8192 slot era. + // The era ended at slot 11567103, and the next 8192 slot era began at slot 11567104. + // Slot 11567104's state has a historical summary at the end of the list of: + // { + // "block_summary_root": "0x9d73b29c6e80e8300cedfa9e53aff89523affb98f3bd3f6752ecc159a2058858", + // "state_summary_root": "0x8a38d8b000dc65641eff8f7ff2e0b6f3b129957410cb9ecf183537484630b289" + // } + expectedBlockSummaryRoot, err := hex.DecodeString("9d73b29c6e80e8300cedfa9e53aff89523affb98f3bd3f6752ecc159a2058858") + if err != nil { + t.Fatalf("Failed to decode expected block summary root: %v", err) + } + if !bytes.Equal(block_summary_root, expectedBlockSummaryRoot) { + t.Fatalf("expected block summary root: %x, got: %x", expectedBlockSummaryRoot, block_summary_root) + } + + // Since the state roots are part of the proof as witness, we may as well verify them here as well. + state_summary_node, err := hslsTree.Get(3) + if err != nil { + t.Fatalf("Failed to get state summary node: %v", err) + } + state_summary_root := state_summary_node.Hash() + t.Logf("state_summary_root: %x", state_summary_root) + expectedStateSummaryRoot, err := hex.DecodeString("8a38d8b000dc65641eff8f7ff2e0b6f3b129957410cb9ecf183537484630b289") + if err != nil { + t.Fatalf("Failed to decode expected state summary root: %v", err) + } + if !bytes.Equal(state_summary_root, expectedStateSummaryRoot) { + t.Fatalf("expected state summary root: %x, got: %x", expectedStateSummaryRoot, state_summary_root) + } + +} diff --git a/shared/types/eth2/state.go b/shared/types/eth2/state.go deleted file mode 100644 index a8f278f61..000000000 --- a/shared/types/eth2/state.go +++ /dev/null @@ -1,306 +0,0 @@ -package eth2 - -import ( - "bytes" - "errors" - "fmt" - "math/bits" - "reflect" - "sync/atomic" -) - -// Important indices for proof generation: -// BeaconStateDenebValidatorsIndex is the field offset of the Validators field in the BeaconStateDeneb struct -const beaconStateDenebValidatorsIndex uint64 = 11 - -// If this ever isn't a power of two, we need to round up to the next power of two -const beaconStateValidatorsMaxLength uint64 = 1 << 40 -const beaconBlockHeaderStateRootGeneralizedIndex uint64 = 11 // Container with 5 fields, so gid 8 is the first field. We want the 4th field, so gid 8 + 3 = 11 -const beaconStateValidatorWithdrawalCredentialsPubkeyGeneralizedIndex uint64 = 4 // Container with 8 fields, so gid 8 is the first field. We want the parent of 1st field, so gid 8 / 2 = 4 -const beaconStateValidatorWithdrawableEpochGeneralizedIndex uint64 = 14 // Container with 8 fields, so gid 8 is the first field. We want the 8th field, so gid 8 + 7 = 15 -// See https://github.com/ethereum/consensus-specs/blob/dev/ssz/merkle-proofs.md for general index calculation and helpers - -func getPowerOfTwoCeil(x uint64) uint64 { - // Base case - if x <= 1 { - return 1 - } - - // Check if already a power of two - if x&(x-1) == 0 { - return x - } - - // Find the most significant bit - msb := bits.Len64(x) - 1 - return 1 << (msb + 1) -} - -var beaconStateChunkSize atomic.Uint64 - -func getDenebStateChunkSize() uint64 { - // Use a static value to avoid multiple reflection calls - storedChunkSize := beaconStateChunkSize.Load() - if storedChunkSize == 0 { - s := reflect.TypeOf(BeaconStateDeneb{}).NumField() - beaconStateChunkSize.Store(uint64(s)) - storedChunkSize = uint64(s) - } - return storedChunkSize -} - -func getDenebGeneralizedIndexForValidators() uint64 { - // There's 28 fields, so rounding up to the next power of two is 32, a left-aligned node - // BeaconStateDenebValidatorsIndex is the 11th field, so its generalized index is 32 + 11 = 43 - return getPowerOfTwoCeil(getDenebStateChunkSize()) + beaconStateDenebValidatorsIndex -} - -func (state *BeaconStateDeneb) getGeneralizedIndexForValidator(index uint64) uint64 { - root := getDenebGeneralizedIndexForValidators() - - // Now, grab the validator index within the list - // `start` is `index * 32` and `pos` is `start / 32` so pos is just `index` - pos := index - baseIndex := uint64(2) // Lists have a base index of 2 - root = root*baseIndex*getPowerOfTwoCeil(beaconStateValidatorsMaxLength) + pos - - // root is now the generalized index for the validator - return root -} - -func (state *BeaconStateDeneb) validatorStateProof(index uint64) ([][]byte, error) { - - // Convert the state to a proof tree - root, err := state.GetTree() - if err != nil { - return nil, fmt.Errorf("could not get state tree: %w", err) - } - - // Find the validator's generalized index - generalizedIndex := state.getGeneralizedIndexForValidator(index) - - // Grab the proof for that index - proof, err := root.Prove(int(generalizedIndex)) - if err != nil { - return nil, fmt.Errorf("could not get proof for validator: %w", err) - } - - // Sanity check that the proof leaf matches the expected validator - validatorHashTreeRoot, err := state.Validators[index].HashTreeRoot() - if err != nil { - return nil, fmt.Errorf("could not get hash tree root for validator: %w", err) - } - if !bytes.Equal(proof.Leaf, validatorHashTreeRoot[:]) { - return nil, fmt.Errorf("proof leaf does not match expected validator") - } - - return proof.Hashes, nil - -} - -func (validator *Validator) validatorWithdrawableEpochProof() ([][]byte, error) { - // Just get the portion of the proof for the validator ExitEpoch. - generalizedIndex := beaconStateValidatorWithdrawableEpochGeneralizedIndex - root, err := validator.GetTree() - if err != nil { - return nil, fmt.Errorf("could not get validator tree: %w", err) - } - proof, err := root.Prove(int(generalizedIndex)) - if err != nil { - return nil, fmt.Errorf("could not get proof for validator withdrawable epoch: %w", err) - } - return proof.Hashes, nil -} - -// ValidatorWithdrawableEpochProof computes the merkle proof for a validator's withdrawable epoch -// at a specific index in the validator registry. -func (state *BeaconStateDeneb) ValidatorWithdrawableEpochProof(index uint64) ([][]byte, error) { - - if index >= uint64(len(state.Validators)) { - return nil, errors.New("validator index out of bounds") - } - - // Get the validator's withdrawable epoch proof - withdrawableEpochProof, err := state.Validators[index].validatorWithdrawableEpochProof() - if err != nil { - return nil, fmt.Errorf("could not get validator withdrawable epoch proof: %w", err) - } - - stateProof, err := state.validatorStateProof(index) - if err != nil { - return nil, fmt.Errorf("could not get validator state proof: %w", err) - } - - // The EL proves against BeaconBlockHeader root, so we need to merge the state proof with that. - generalizedIndex := beaconBlockHeaderStateRootGeneralizedIndex - root, err := state.LatestBlockHeader.GetTree() - if err != nil { - return nil, fmt.Errorf("could not get block header tree: %w", err) - } - blockHeaderProof, err := root.Prove(int(generalizedIndex)) - if err != nil { - return nil, fmt.Errorf("could not get proof for block header: %w", err) - } - - out := append(withdrawableEpochProof, stateProof...) - out = append(out, blockHeaderProof.Hashes...) - - return out, nil -} - -func (validator *Validator) validatorCredentialsPubkeyProof() ([][]byte, error) { - // Just get the portion of the proof for the validator's credentials. - generalizedIndex := beaconStateValidatorWithdrawalCredentialsPubkeyGeneralizedIndex - root, err := validator.GetTree() - if err != nil { - return nil, fmt.Errorf("could not get validator tree: %w", err) - } - proof, err := root.Prove(int(generalizedIndex)) - if err != nil { - return nil, fmt.Errorf("could not get proof for validator credentials: %w", err) - } - return proof.Hashes, nil -} - -// ValidatorCredentialsProof computes the merkle proof for a validator's credentials -// at a specific index in the validator registry. -func (state *BeaconStateDeneb) ValidatorCredentialsProof(index uint64) ([][]byte, error) { - - if index >= uint64(len(state.Validators)) { - return nil, errors.New("validator index out of bounds") - } - - // Get the validator's credentials proof - credentialsProof, err := state.Validators[index].validatorCredentialsPubkeyProof() - if err != nil { - return nil, fmt.Errorf("could not get validator credentials proof: %w", err) - } - - stateProof, err := state.validatorStateProof(index) - if err != nil { - return nil, fmt.Errorf("could not get validator state proof: %w", err) - } - - // The EL proves against BeaconBlockHeader root, so we need to merge the state proof with that. - generalizedIndex := beaconBlockHeaderStateRootGeneralizedIndex - root, err := state.LatestBlockHeader.GetTree() - if err != nil { - return nil, fmt.Errorf("could not get block header tree: %w", err) - } - blockHeaderProof, err := root.Prove(int(generalizedIndex)) - if err != nil { - return nil, fmt.Errorf("could not get proof for block header: %w", err) - } - - out := append(credentialsProof, stateProof...) - out = append(out, blockHeaderProof.Hashes...) - - return out, nil -} - -// Taken from https://github.com/prysmaticlabs/prysm/blob/ac1717f1e44bd218b0bd3af0c4dec951c075f462/proto/prysm/v1alpha1/beacon_state.pb.go#L1574 -// Unexported fields stripped, as well as proto-related field tags. JSON and ssz-size tags are preserved, and nested types are replaced with local copies as well. -type BeaconStateDeneb struct { - GenesisTime uint64 `json:"genesis_time"` - GenesisValidatorsRoot []byte `json:"genesis_validators_root" ssz-size:"32"` - Slot uint64 `json:"slot"` - Fork *Fork `json:"fork"` - LatestBlockHeader *BeaconBlockHeader `json:"latest_block_header"` - BlockRoots [8192][32]byte `json:"block_roots" ssz-size:"8192,32"` - StateRoots [8192][32]byte `json:"state_roots" ssz-size:"8192,32"` - HistoricalRoots [][]byte `json:"historical_roots" ssz-max:"16777216" ssz-size:"?,32"` - Eth1Data *Eth1Data `json:"eth1_data"` - Eth1DataVotes []*Eth1Data `json:"eth1_data_votes" ssz-max:"2048"` - Eth1DepositIndex uint64 `json:"eth1_deposit_index"` - Validators []*Validator `json:"validators" ssz-max:"1099511627776"` - Balances []uint64 `json:"balances" ssz-max:"1099511627776"` - RandaoMixes [][]byte `json:"randao_mixes" ssz-size:"65536,32"` - Slashings []uint64 `json:"slashings" ssz-size:"8192"` - PreviousEpochParticipation []byte `json:"previous_epoch_participation" ssz-max:"1099511627776"` - CurrentEpochParticipation []byte `json:"current_epoch_participation" ssz-max:"1099511627776"` - JustificationBits [1]byte `json:"justification_bits" ssz-size:"1"` - PreviousJustifiedCheckpoint *Checkpoint `json:"previous_justified_checkpoint"` - CurrentJustifiedCheckpoint *Checkpoint `json:"current_justified_checkpoint"` - FinalizedCheckpoint *Checkpoint `json:"finalized_checkpoint"` - InactivityScores []uint64 `json:"inactivity_scores" ssz-max:"1099511627776"` - CurrentSyncCommittee *SyncCommittee `json:"current_sync_committee"` - NextSyncCommittee *SyncCommittee `json:"next_sync_committee"` - LatestExecutionPayloadHeader *ExecutionPayloadHeaderDeneb `json:"latest_execution_payload_header"` - NextWithdrawalIndex uint64 `json:"next_withdrawal_index"` - NextWithdrawalValidatorIndex uint64 `json:"next_withdrawal_validator_index"` - HistoricalSummaries []*HistoricalSummary `json:"historical_summaries" ssz-max:"16777216"` -} - -// Remaining types taken from https://github.com/ferranbt/fastssz/blob/03cd29050aa2555fd4abc29ace7c1fac8b8fb25e/spectests/structs.go - -// Per-Fork ExecutionPayloadHeaders - -type ExecutionPayloadHeaderDeneb struct { - ParentHash [32]byte `json:"parent_hash" ssz-size:"32"` - FeeRecipient [20]byte `json:"fee_recipient" ssz-size:"20"` - StateRoot [32]byte `json:"state_root" ssz-size:"32"` - ReceiptsRoot [32]byte `json:"receipts_root" ssz-size:"32"` - LogsBloom [256]byte `json:"logs_bloom" ssz-size:"256"` - PrevRandao [32]byte `json:"prev_randao" ssz-size:"32"` - BlockNumber uint64 `json:"block_number"` - GasLimit uint64 `json:"gas_limit"` - GasUsed uint64 `json:"gas_used"` - Timestamp uint64 `json:"timestamp"` - ExtraData []byte `json:"extra_data" ssz-max:"32"` - BaseFeePerGas Uint256 `json:"base_fee_per_gas" ssz-size:"32"` - BlockHash [32]byte `json:"block_hash" ssz-size:"32"` - TransactionsRoot [32]byte `json:"transactions_root" ssz-size:"32"` - WithdrawalRoot [32]byte `json:"withdrawals_root" ssz-size:"32"` - BlobGasUsed uint64 `json:"blob_gas_used"` - ExcessBlobGas uint64 `json:"excess_blob_gas"` -} - -// Generic types - -type Uint256 [32]byte - -type Fork struct { - PreviousVersion []byte `json:"previous_version" ssz-size:"4"` - CurrentVersion []byte `json:"current_version" ssz-size:"4"` - Epoch uint64 `json:"epoch"` -} - -type BeaconBlockHeader struct { - Slot uint64 `json:"slot"` - ProposerIndex uint64 `json:"proposer_index"` - ParentRoot []byte `json:"parent_root" ssz-size:"32"` - StateRoot []byte `json:"state_root" ssz-size:"32"` - BodyRoot []byte `json:"body_root" ssz-size:"32"` -} - -type Eth1Data struct { - DepositRoot []byte `json:"deposit_root" ssz-size:"32"` - DepositCount uint64 `json:"deposit_count"` - BlockHash []byte `json:"block_hash" ssz-size:"32"` -} - -type Validator struct { - Pubkey []byte `json:"pubkey" ssz-size:"48"` - WithdrawalCredentials []byte `json:"withdrawal_credentials" ssz-size:"32"` - EffectiveBalance uint64 `json:"effective_balance"` - Slashed bool `json:"slashed"` - ActivationEligibilityEpoch uint64 `json:"activation_eligibility_epoch"` - ActivationEpoch uint64 `json:"activation_epoch"` - ExitEpoch uint64 `json:"exit_epoch"` - WithdrawableEpoch uint64 `json:"withdrawable_epoch"` -} - -type Checkpoint struct { - Epoch uint64 `json:"epoch"` - Root []byte `json:"root" ssz-size:"32"` -} - -type SyncCommittee struct { - PubKeys [][]byte `json:"pubkeys" ssz-size:"512,48"` - AggregatePubKey [48]byte `json:"aggregate_pubkey" ssz-size:"48"` -} - -type HistoricalSummary struct { - BlockSummaryRoot [32]byte `json:"block_summary_root" ssz-size:"32"` - StateSummaryRoot [32]byte `json:"state_summary_root" ssz-size:"32"` -} diff --git a/shared/types/eth2/state_encoding.go b/shared/types/eth2/state_encoding.go deleted file mode 100644 index cf8a58177..000000000 --- a/shared/types/eth2/state_encoding.go +++ /dev/null @@ -1,1823 +0,0 @@ -// Code generated by fastssz. DO NOT EDIT. -// Hash: 8334e51dc7fef48f4bfcdf131ce2d53118dc8c0cba4ed032b43e8a8e83319297 -// Version: 0.1.3 -package eth2 - -import ( - ssz "github.com/ferranbt/fastssz" -) - -// MarshalSSZ ssz marshals the BeaconStateDeneb object -func (b *BeaconStateDeneb) MarshalSSZ() ([]byte, error) { - return ssz.MarshalSSZ(b) -} - -// MarshalSSZTo ssz marshals the BeaconStateDeneb object to a target array -func (b *BeaconStateDeneb) MarshalSSZTo(buf []byte) (dst []byte, err error) { - dst = buf - offset := int(2736653) - - // Field (0) 'GenesisTime' - dst = ssz.MarshalUint64(dst, b.GenesisTime) - - // Field (1) 'GenesisValidatorsRoot' - if size := len(b.GenesisValidatorsRoot); size != 32 { - err = ssz.ErrBytesLengthFn("BeaconStateDeneb.GenesisValidatorsRoot", size, 32) - return - } - dst = append(dst, b.GenesisValidatorsRoot...) - - // Field (2) 'Slot' - dst = ssz.MarshalUint64(dst, b.Slot) - - // Field (3) 'Fork' - if b.Fork == nil { - b.Fork = new(Fork) - } - if dst, err = b.Fork.MarshalSSZTo(dst); err != nil { - return - } - - // Field (4) 'LatestBlockHeader' - if b.LatestBlockHeader == nil { - b.LatestBlockHeader = new(BeaconBlockHeader) - } - if dst, err = b.LatestBlockHeader.MarshalSSZTo(dst); err != nil { - return - } - - // Field (5) 'BlockRoots' - for ii := 0; ii < 8192; ii++ { - dst = append(dst, b.BlockRoots[ii][:]...) - } - - // Field (6) 'StateRoots' - for ii := 0; ii < 8192; ii++ { - dst = append(dst, b.StateRoots[ii][:]...) - } - - // Offset (7) 'HistoricalRoots' - dst = ssz.WriteOffset(dst, offset) - offset += len(b.HistoricalRoots) * 32 - - // Field (8) 'Eth1Data' - if b.Eth1Data == nil { - b.Eth1Data = new(Eth1Data) - } - if dst, err = b.Eth1Data.MarshalSSZTo(dst); err != nil { - return - } - - // Offset (9) 'Eth1DataVotes' - dst = ssz.WriteOffset(dst, offset) - offset += len(b.Eth1DataVotes) * 72 - - // Field (10) 'Eth1DepositIndex' - dst = ssz.MarshalUint64(dst, b.Eth1DepositIndex) - - // Offset (11) 'Validators' - dst = ssz.WriteOffset(dst, offset) - offset += len(b.Validators) * 121 - - // Offset (12) 'Balances' - dst = ssz.WriteOffset(dst, offset) - offset += len(b.Balances) * 8 - - // Field (13) 'RandaoMixes' - if size := len(b.RandaoMixes); size != 65536 { - err = ssz.ErrVectorLengthFn("BeaconStateDeneb.RandaoMixes", size, 65536) - return - } - for ii := 0; ii < 65536; ii++ { - if size := len(b.RandaoMixes[ii]); size != 32 { - err = ssz.ErrBytesLengthFn("BeaconStateDeneb.RandaoMixes[ii]", size, 32) - return - } - dst = append(dst, b.RandaoMixes[ii]...) - } - - // Field (14) 'Slashings' - if size := len(b.Slashings); size != 8192 { - err = ssz.ErrVectorLengthFn("BeaconStateDeneb.Slashings", size, 8192) - return - } - for ii := 0; ii < 8192; ii++ { - dst = ssz.MarshalUint64(dst, b.Slashings[ii]) - } - - // Offset (15) 'PreviousEpochParticipation' - dst = ssz.WriteOffset(dst, offset) - offset += len(b.PreviousEpochParticipation) - - // Offset (16) 'CurrentEpochParticipation' - dst = ssz.WriteOffset(dst, offset) - offset += len(b.CurrentEpochParticipation) - - // Field (17) 'JustificationBits' - dst = append(dst, b.JustificationBits[:]...) - - // Field (18) 'PreviousJustifiedCheckpoint' - if b.PreviousJustifiedCheckpoint == nil { - b.PreviousJustifiedCheckpoint = new(Checkpoint) - } - if dst, err = b.PreviousJustifiedCheckpoint.MarshalSSZTo(dst); err != nil { - return - } - - // Field (19) 'CurrentJustifiedCheckpoint' - if b.CurrentJustifiedCheckpoint == nil { - b.CurrentJustifiedCheckpoint = new(Checkpoint) - } - if dst, err = b.CurrentJustifiedCheckpoint.MarshalSSZTo(dst); err != nil { - return - } - - // Field (20) 'FinalizedCheckpoint' - if b.FinalizedCheckpoint == nil { - b.FinalizedCheckpoint = new(Checkpoint) - } - if dst, err = b.FinalizedCheckpoint.MarshalSSZTo(dst); err != nil { - return - } - - // Offset (21) 'InactivityScores' - dst = ssz.WriteOffset(dst, offset) - offset += len(b.InactivityScores) * 8 - - // Field (22) 'CurrentSyncCommittee' - if b.CurrentSyncCommittee == nil { - b.CurrentSyncCommittee = new(SyncCommittee) - } - if dst, err = b.CurrentSyncCommittee.MarshalSSZTo(dst); err != nil { - return - } - - // Field (23) 'NextSyncCommittee' - if b.NextSyncCommittee == nil { - b.NextSyncCommittee = new(SyncCommittee) - } - if dst, err = b.NextSyncCommittee.MarshalSSZTo(dst); err != nil { - return - } - - // Offset (24) 'LatestExecutionPayloadHeader' - dst = ssz.WriteOffset(dst, offset) - if b.LatestExecutionPayloadHeader == nil { - b.LatestExecutionPayloadHeader = new(ExecutionPayloadHeaderDeneb) - } - offset += b.LatestExecutionPayloadHeader.SizeSSZ() - - // Field (25) 'NextWithdrawalIndex' - dst = ssz.MarshalUint64(dst, b.NextWithdrawalIndex) - - // Field (26) 'NextWithdrawalValidatorIndex' - dst = ssz.MarshalUint64(dst, b.NextWithdrawalValidatorIndex) - - // Offset (27) 'HistoricalSummaries' - dst = ssz.WriteOffset(dst, offset) - - // Field (7) 'HistoricalRoots' - if size := len(b.HistoricalRoots); size > 16777216 { - err = ssz.ErrListTooBigFn("BeaconStateDeneb.HistoricalRoots", size, 16777216) - return - } - for ii := 0; ii < len(b.HistoricalRoots); ii++ { - if size := len(b.HistoricalRoots[ii]); size != 32 { - err = ssz.ErrBytesLengthFn("BeaconStateDeneb.HistoricalRoots[ii]", size, 32) - return - } - dst = append(dst, b.HistoricalRoots[ii]...) - } - - // Field (9) 'Eth1DataVotes' - if size := len(b.Eth1DataVotes); size > 2048 { - err = ssz.ErrListTooBigFn("BeaconStateDeneb.Eth1DataVotes", size, 2048) - return - } - for ii := 0; ii < len(b.Eth1DataVotes); ii++ { - if dst, err = b.Eth1DataVotes[ii].MarshalSSZTo(dst); err != nil { - return - } - } - - // Field (11) 'Validators' - if size := len(b.Validators); size > 1099511627776 { - err = ssz.ErrListTooBigFn("BeaconStateDeneb.Validators", size, 1099511627776) - return - } - for ii := 0; ii < len(b.Validators); ii++ { - if dst, err = b.Validators[ii].MarshalSSZTo(dst); err != nil { - return - } - } - - // Field (12) 'Balances' - if size := len(b.Balances); size > 1099511627776 { - err = ssz.ErrListTooBigFn("BeaconStateDeneb.Balances", size, 1099511627776) - return - } - for ii := 0; ii < len(b.Balances); ii++ { - dst = ssz.MarshalUint64(dst, b.Balances[ii]) - } - - // Field (15) 'PreviousEpochParticipation' - if size := len(b.PreviousEpochParticipation); size > 1099511627776 { - err = ssz.ErrBytesLengthFn("BeaconStateDeneb.PreviousEpochParticipation", size, 1099511627776) - return - } - dst = append(dst, b.PreviousEpochParticipation...) - - // Field (16) 'CurrentEpochParticipation' - if size := len(b.CurrentEpochParticipation); size > 1099511627776 { - err = ssz.ErrBytesLengthFn("BeaconStateDeneb.CurrentEpochParticipation", size, 1099511627776) - return - } - dst = append(dst, b.CurrentEpochParticipation...) - - // Field (21) 'InactivityScores' - if size := len(b.InactivityScores); size > 1099511627776 { - err = ssz.ErrListTooBigFn("BeaconStateDeneb.InactivityScores", size, 1099511627776) - return - } - for ii := 0; ii < len(b.InactivityScores); ii++ { - dst = ssz.MarshalUint64(dst, b.InactivityScores[ii]) - } - - // Field (24) 'LatestExecutionPayloadHeader' - if dst, err = b.LatestExecutionPayloadHeader.MarshalSSZTo(dst); err != nil { - return - } - - // Field (27) 'HistoricalSummaries' - if size := len(b.HistoricalSummaries); size > 16777216 { - err = ssz.ErrListTooBigFn("BeaconStateDeneb.HistoricalSummaries", size, 16777216) - return - } - for ii := 0; ii < len(b.HistoricalSummaries); ii++ { - if dst, err = b.HistoricalSummaries[ii].MarshalSSZTo(dst); err != nil { - return - } - } - - return -} - -// UnmarshalSSZ ssz unmarshals the BeaconStateDeneb object -func (b *BeaconStateDeneb) UnmarshalSSZ(buf []byte) error { - var err error - size := uint64(len(buf)) - if size < 2736653 { - return ssz.ErrSize - } - - tail := buf - var o7, o9, o11, o12, o15, o16, o21, o24, o27 uint64 - - // Field (0) 'GenesisTime' - b.GenesisTime = ssz.UnmarshallUint64(buf[0:8]) - - // Field (1) 'GenesisValidatorsRoot' - if cap(b.GenesisValidatorsRoot) == 0 { - b.GenesisValidatorsRoot = make([]byte, 0, len(buf[8:40])) - } - b.GenesisValidatorsRoot = append(b.GenesisValidatorsRoot, buf[8:40]...) - - // Field (2) 'Slot' - b.Slot = ssz.UnmarshallUint64(buf[40:48]) - - // Field (3) 'Fork' - if b.Fork == nil { - b.Fork = new(Fork) - } - if err = b.Fork.UnmarshalSSZ(buf[48:64]); err != nil { - return err - } - - // Field (4) 'LatestBlockHeader' - if b.LatestBlockHeader == nil { - b.LatestBlockHeader = new(BeaconBlockHeader) - } - if err = b.LatestBlockHeader.UnmarshalSSZ(buf[64:176]); err != nil { - return err - } - - // Field (5) 'BlockRoots' - - for ii := 0; ii < 8192; ii++ { - copy(b.BlockRoots[ii][:], buf[176:262320][ii*32:(ii+1)*32]) - } - - // Field (6) 'StateRoots' - - for ii := 0; ii < 8192; ii++ { - copy(b.StateRoots[ii][:], buf[262320:524464][ii*32:(ii+1)*32]) - } - - // Offset (7) 'HistoricalRoots' - if o7 = ssz.ReadOffset(buf[524464:524468]); o7 > size { - return ssz.ErrOffset - } - - if o7 != 2736653 { - return ssz.ErrInvalidVariableOffset - } - - // Field (8) 'Eth1Data' - if b.Eth1Data == nil { - b.Eth1Data = new(Eth1Data) - } - if err = b.Eth1Data.UnmarshalSSZ(buf[524468:524540]); err != nil { - return err - } - - // Offset (9) 'Eth1DataVotes' - if o9 = ssz.ReadOffset(buf[524540:524544]); o9 > size || o7 > o9 { - return ssz.ErrOffset - } - - // Field (10) 'Eth1DepositIndex' - b.Eth1DepositIndex = ssz.UnmarshallUint64(buf[524544:524552]) - - // Offset (11) 'Validators' - if o11 = ssz.ReadOffset(buf[524552:524556]); o11 > size || o9 > o11 { - return ssz.ErrOffset - } - - // Offset (12) 'Balances' - if o12 = ssz.ReadOffset(buf[524556:524560]); o12 > size || o11 > o12 { - return ssz.ErrOffset - } - - // Field (13) 'RandaoMixes' - b.RandaoMixes = make([][]byte, 65536) - for ii := 0; ii < 65536; ii++ { - if cap(b.RandaoMixes[ii]) == 0 { - b.RandaoMixes[ii] = make([]byte, 0, len(buf[524560:2621712][ii*32:(ii+1)*32])) - } - b.RandaoMixes[ii] = append(b.RandaoMixes[ii], buf[524560:2621712][ii*32:(ii+1)*32]...) - } - - // Field (14) 'Slashings' - b.Slashings = ssz.ExtendUint64(b.Slashings, 8192) - for ii := 0; ii < 8192; ii++ { - b.Slashings[ii] = ssz.UnmarshallUint64(buf[2621712:2687248][ii*8 : (ii+1)*8]) - } - - // Offset (15) 'PreviousEpochParticipation' - if o15 = ssz.ReadOffset(buf[2687248:2687252]); o15 > size || o12 > o15 { - return ssz.ErrOffset - } - - // Offset (16) 'CurrentEpochParticipation' - if o16 = ssz.ReadOffset(buf[2687252:2687256]); o16 > size || o15 > o16 { - return ssz.ErrOffset - } - - // Field (17) 'JustificationBits' - copy(b.JustificationBits[:], buf[2687256:2687257]) - - // Field (18) 'PreviousJustifiedCheckpoint' - if b.PreviousJustifiedCheckpoint == nil { - b.PreviousJustifiedCheckpoint = new(Checkpoint) - } - if err = b.PreviousJustifiedCheckpoint.UnmarshalSSZ(buf[2687257:2687297]); err != nil { - return err - } - - // Field (19) 'CurrentJustifiedCheckpoint' - if b.CurrentJustifiedCheckpoint == nil { - b.CurrentJustifiedCheckpoint = new(Checkpoint) - } - if err = b.CurrentJustifiedCheckpoint.UnmarshalSSZ(buf[2687297:2687337]); err != nil { - return err - } - - // Field (20) 'FinalizedCheckpoint' - if b.FinalizedCheckpoint == nil { - b.FinalizedCheckpoint = new(Checkpoint) - } - if err = b.FinalizedCheckpoint.UnmarshalSSZ(buf[2687337:2687377]); err != nil { - return err - } - - // Offset (21) 'InactivityScores' - if o21 = ssz.ReadOffset(buf[2687377:2687381]); o21 > size || o16 > o21 { - return ssz.ErrOffset - } - - // Field (22) 'CurrentSyncCommittee' - if b.CurrentSyncCommittee == nil { - b.CurrentSyncCommittee = new(SyncCommittee) - } - if err = b.CurrentSyncCommittee.UnmarshalSSZ(buf[2687381:2712005]); err != nil { - return err - } - - // Field (23) 'NextSyncCommittee' - if b.NextSyncCommittee == nil { - b.NextSyncCommittee = new(SyncCommittee) - } - if err = b.NextSyncCommittee.UnmarshalSSZ(buf[2712005:2736629]); err != nil { - return err - } - - // Offset (24) 'LatestExecutionPayloadHeader' - if o24 = ssz.ReadOffset(buf[2736629:2736633]); o24 > size || o21 > o24 { - return ssz.ErrOffset - } - - // Field (25) 'NextWithdrawalIndex' - b.NextWithdrawalIndex = ssz.UnmarshallUint64(buf[2736633:2736641]) - - // Field (26) 'NextWithdrawalValidatorIndex' - b.NextWithdrawalValidatorIndex = ssz.UnmarshallUint64(buf[2736641:2736649]) - - // Offset (27) 'HistoricalSummaries' - if o27 = ssz.ReadOffset(buf[2736649:2736653]); o27 > size || o24 > o27 { - return ssz.ErrOffset - } - - // Field (7) 'HistoricalRoots' - { - buf = tail[o7:o9] - num, err := ssz.DivideInt2(len(buf), 32, 16777216) - if err != nil { - return err - } - b.HistoricalRoots = make([][]byte, num) - for ii := 0; ii < num; ii++ { - if cap(b.HistoricalRoots[ii]) == 0 { - b.HistoricalRoots[ii] = make([]byte, 0, len(buf[ii*32:(ii+1)*32])) - } - b.HistoricalRoots[ii] = append(b.HistoricalRoots[ii], buf[ii*32:(ii+1)*32]...) - } - } - - // Field (9) 'Eth1DataVotes' - { - buf = tail[o9:o11] - num, err := ssz.DivideInt2(len(buf), 72, 2048) - if err != nil { - return err - } - b.Eth1DataVotes = make([]*Eth1Data, num) - for ii := 0; ii < num; ii++ { - if b.Eth1DataVotes[ii] == nil { - b.Eth1DataVotes[ii] = new(Eth1Data) - } - if err = b.Eth1DataVotes[ii].UnmarshalSSZ(buf[ii*72 : (ii+1)*72]); err != nil { - return err - } - } - } - - // Field (11) 'Validators' - { - buf = tail[o11:o12] - num, err := ssz.DivideInt2(len(buf), 121, 1099511627776) - if err != nil { - return err - } - b.Validators = make([]*Validator, num) - for ii := 0; ii < num; ii++ { - if b.Validators[ii] == nil { - b.Validators[ii] = new(Validator) - } - if err = b.Validators[ii].UnmarshalSSZ(buf[ii*121 : (ii+1)*121]); err != nil { - return err - } - } - } - - // Field (12) 'Balances' - { - buf = tail[o12:o15] - num, err := ssz.DivideInt2(len(buf), 8, 1099511627776) - if err != nil { - return err - } - b.Balances = ssz.ExtendUint64(b.Balances, num) - for ii := 0; ii < num; ii++ { - b.Balances[ii] = ssz.UnmarshallUint64(buf[ii*8 : (ii+1)*8]) - } - } - - // Field (15) 'PreviousEpochParticipation' - { - buf = tail[o15:o16] - if len(buf) > 1099511627776 { - return ssz.ErrBytesLength - } - if cap(b.PreviousEpochParticipation) == 0 { - b.PreviousEpochParticipation = make([]byte, 0, len(buf)) - } - b.PreviousEpochParticipation = append(b.PreviousEpochParticipation, buf...) - } - - // Field (16) 'CurrentEpochParticipation' - { - buf = tail[o16:o21] - if len(buf) > 1099511627776 { - return ssz.ErrBytesLength - } - if cap(b.CurrentEpochParticipation) == 0 { - b.CurrentEpochParticipation = make([]byte, 0, len(buf)) - } - b.CurrentEpochParticipation = append(b.CurrentEpochParticipation, buf...) - } - - // Field (21) 'InactivityScores' - { - buf = tail[o21:o24] - num, err := ssz.DivideInt2(len(buf), 8, 1099511627776) - if err != nil { - return err - } - b.InactivityScores = ssz.ExtendUint64(b.InactivityScores, num) - for ii := 0; ii < num; ii++ { - b.InactivityScores[ii] = ssz.UnmarshallUint64(buf[ii*8 : (ii+1)*8]) - } - } - - // Field (24) 'LatestExecutionPayloadHeader' - { - buf = tail[o24:o27] - if b.LatestExecutionPayloadHeader == nil { - b.LatestExecutionPayloadHeader = new(ExecutionPayloadHeaderDeneb) - } - if err = b.LatestExecutionPayloadHeader.UnmarshalSSZ(buf); err != nil { - return err - } - } - - // Field (27) 'HistoricalSummaries' - { - buf = tail[o27:] - num, err := ssz.DivideInt2(len(buf), 64, 16777216) - if err != nil { - return err - } - b.HistoricalSummaries = make([]*HistoricalSummary, num) - for ii := 0; ii < num; ii++ { - if b.HistoricalSummaries[ii] == nil { - b.HistoricalSummaries[ii] = new(HistoricalSummary) - } - if err = b.HistoricalSummaries[ii].UnmarshalSSZ(buf[ii*64 : (ii+1)*64]); err != nil { - return err - } - } - } - return err -} - -// SizeSSZ returns the ssz encoded size in bytes for the BeaconStateDeneb object -func (b *BeaconStateDeneb) SizeSSZ() (size int) { - size = 2736653 - - // Field (7) 'HistoricalRoots' - size += len(b.HistoricalRoots) * 32 - - // Field (9) 'Eth1DataVotes' - size += len(b.Eth1DataVotes) * 72 - - // Field (11) 'Validators' - size += len(b.Validators) * 121 - - // Field (12) 'Balances' - size += len(b.Balances) * 8 - - // Field (15) 'PreviousEpochParticipation' - size += len(b.PreviousEpochParticipation) - - // Field (16) 'CurrentEpochParticipation' - size += len(b.CurrentEpochParticipation) - - // Field (21) 'InactivityScores' - size += len(b.InactivityScores) * 8 - - // Field (24) 'LatestExecutionPayloadHeader' - if b.LatestExecutionPayloadHeader == nil { - b.LatestExecutionPayloadHeader = new(ExecutionPayloadHeaderDeneb) - } - size += b.LatestExecutionPayloadHeader.SizeSSZ() - - // Field (27) 'HistoricalSummaries' - size += len(b.HistoricalSummaries) * 64 - - return -} - -// HashTreeRoot ssz hashes the BeaconStateDeneb object -func (b *BeaconStateDeneb) HashTreeRoot() ([32]byte, error) { - return ssz.HashWithDefaultHasher(b) -} - -// HashTreeRootWith ssz hashes the BeaconStateDeneb object with a hasher -func (b *BeaconStateDeneb) HashTreeRootWith(hh ssz.HashWalker) (err error) { - indx := hh.Index() - - // Field (0) 'GenesisTime' - hh.PutUint64(b.GenesisTime) - - // Field (1) 'GenesisValidatorsRoot' - if size := len(b.GenesisValidatorsRoot); size != 32 { - err = ssz.ErrBytesLengthFn("BeaconStateDeneb.GenesisValidatorsRoot", size, 32) - return - } - hh.PutBytes(b.GenesisValidatorsRoot) - - // Field (2) 'Slot' - hh.PutUint64(b.Slot) - - // Field (3) 'Fork' - if b.Fork == nil { - b.Fork = new(Fork) - } - if err = b.Fork.HashTreeRootWith(hh); err != nil { - return - } - - // Field (4) 'LatestBlockHeader' - if b.LatestBlockHeader == nil { - b.LatestBlockHeader = new(BeaconBlockHeader) - } - if err = b.LatestBlockHeader.HashTreeRootWith(hh); err != nil { - return - } - - // Field (5) 'BlockRoots' - { - subIndx := hh.Index() - for _, i := range b.BlockRoots { - hh.Append(i[:]) - } - hh.Merkleize(subIndx) - } - - // Field (6) 'StateRoots' - { - subIndx := hh.Index() - for _, i := range b.StateRoots { - hh.Append(i[:]) - } - hh.Merkleize(subIndx) - } - - // Field (7) 'HistoricalRoots' - { - if size := len(b.HistoricalRoots); size > 16777216 { - err = ssz.ErrListTooBigFn("BeaconStateDeneb.HistoricalRoots", size, 16777216) - return - } - subIndx := hh.Index() - for _, i := range b.HistoricalRoots { - if len(i) != 32 { - err = ssz.ErrBytesLength - return - } - hh.Append(i) - } - numItems := uint64(len(b.HistoricalRoots)) - hh.MerkleizeWithMixin(subIndx, numItems, 16777216) - } - - // Field (8) 'Eth1Data' - if b.Eth1Data == nil { - b.Eth1Data = new(Eth1Data) - } - if err = b.Eth1Data.HashTreeRootWith(hh); err != nil { - return - } - - // Field (9) 'Eth1DataVotes' - { - subIndx := hh.Index() - num := uint64(len(b.Eth1DataVotes)) - if num > 2048 { - err = ssz.ErrIncorrectListSize - return - } - for _, elem := range b.Eth1DataVotes { - if err = elem.HashTreeRootWith(hh); err != nil { - return - } - } - hh.MerkleizeWithMixin(subIndx, num, 2048) - } - - // Field (10) 'Eth1DepositIndex' - hh.PutUint64(b.Eth1DepositIndex) - - // Field (11) 'Validators' - { - subIndx := hh.Index() - num := uint64(len(b.Validators)) - if num > 1099511627776 { - err = ssz.ErrIncorrectListSize - return - } - for _, elem := range b.Validators { - if err = elem.HashTreeRootWith(hh); err != nil { - return - } - } - hh.MerkleizeWithMixin(subIndx, num, 1099511627776) - } - - // Field (12) 'Balances' - { - if size := len(b.Balances); size > 1099511627776 { - err = ssz.ErrListTooBigFn("BeaconStateDeneb.Balances", size, 1099511627776) - return - } - subIndx := hh.Index() - for _, i := range b.Balances { - hh.AppendUint64(i) - } - hh.FillUpTo32() - numItems := uint64(len(b.Balances)) - hh.MerkleizeWithMixin(subIndx, numItems, ssz.CalculateLimit(1099511627776, numItems, 8)) - } - - // Field (13) 'RandaoMixes' - { - if size := len(b.RandaoMixes); size != 65536 { - err = ssz.ErrVectorLengthFn("BeaconStateDeneb.RandaoMixes", size, 65536) - return - } - subIndx := hh.Index() - for _, i := range b.RandaoMixes { - if len(i) != 32 { - err = ssz.ErrBytesLength - return - } - hh.Append(i) - } - hh.Merkleize(subIndx) - } - - // Field (14) 'Slashings' - { - if size := len(b.Slashings); size != 8192 { - err = ssz.ErrVectorLengthFn("BeaconStateDeneb.Slashings", size, 8192) - return - } - subIndx := hh.Index() - for _, i := range b.Slashings { - hh.AppendUint64(i) - } - hh.Merkleize(subIndx) - } - - // Field (15) 'PreviousEpochParticipation' - { - elemIndx := hh.Index() - byteLen := uint64(len(b.PreviousEpochParticipation)) - if byteLen > 1099511627776 { - err = ssz.ErrIncorrectListSize - return - } - hh.Append(b.PreviousEpochParticipation) - hh.MerkleizeWithMixin(elemIndx, byteLen, (1099511627776+31)/32) - } - - // Field (16) 'CurrentEpochParticipation' - { - elemIndx := hh.Index() - byteLen := uint64(len(b.CurrentEpochParticipation)) - if byteLen > 1099511627776 { - err = ssz.ErrIncorrectListSize - return - } - hh.Append(b.CurrentEpochParticipation) - hh.MerkleizeWithMixin(elemIndx, byteLen, (1099511627776+31)/32) - } - - // Field (17) 'JustificationBits' - hh.PutBytes(b.JustificationBits[:]) - - // Field (18) 'PreviousJustifiedCheckpoint' - if b.PreviousJustifiedCheckpoint == nil { - b.PreviousJustifiedCheckpoint = new(Checkpoint) - } - if err = b.PreviousJustifiedCheckpoint.HashTreeRootWith(hh); err != nil { - return - } - - // Field (19) 'CurrentJustifiedCheckpoint' - if b.CurrentJustifiedCheckpoint == nil { - b.CurrentJustifiedCheckpoint = new(Checkpoint) - } - if err = b.CurrentJustifiedCheckpoint.HashTreeRootWith(hh); err != nil { - return - } - - // Field (20) 'FinalizedCheckpoint' - if b.FinalizedCheckpoint == nil { - b.FinalizedCheckpoint = new(Checkpoint) - } - if err = b.FinalizedCheckpoint.HashTreeRootWith(hh); err != nil { - return - } - - // Field (21) 'InactivityScores' - { - if size := len(b.InactivityScores); size > 1099511627776 { - err = ssz.ErrListTooBigFn("BeaconStateDeneb.InactivityScores", size, 1099511627776) - return - } - subIndx := hh.Index() - for _, i := range b.InactivityScores { - hh.AppendUint64(i) - } - hh.FillUpTo32() - numItems := uint64(len(b.InactivityScores)) - hh.MerkleizeWithMixin(subIndx, numItems, ssz.CalculateLimit(1099511627776, numItems, 8)) - } - - // Field (22) 'CurrentSyncCommittee' - if b.CurrentSyncCommittee == nil { - b.CurrentSyncCommittee = new(SyncCommittee) - } - if err = b.CurrentSyncCommittee.HashTreeRootWith(hh); err != nil { - return - } - - // Field (23) 'NextSyncCommittee' - if b.NextSyncCommittee == nil { - b.NextSyncCommittee = new(SyncCommittee) - } - if err = b.NextSyncCommittee.HashTreeRootWith(hh); err != nil { - return - } - - // Field (24) 'LatestExecutionPayloadHeader' - if err = b.LatestExecutionPayloadHeader.HashTreeRootWith(hh); err != nil { - return - } - - // Field (25) 'NextWithdrawalIndex' - hh.PutUint64(b.NextWithdrawalIndex) - - // Field (26) 'NextWithdrawalValidatorIndex' - hh.PutUint64(b.NextWithdrawalValidatorIndex) - - // Field (27) 'HistoricalSummaries' - { - subIndx := hh.Index() - num := uint64(len(b.HistoricalSummaries)) - if num > 16777216 { - err = ssz.ErrIncorrectListSize - return - } - for _, elem := range b.HistoricalSummaries { - if err = elem.HashTreeRootWith(hh); err != nil { - return - } - } - hh.MerkleizeWithMixin(subIndx, num, 16777216) - } - - hh.Merkleize(indx) - return -} - -// GetTree ssz hashes the BeaconStateDeneb object -func (b *BeaconStateDeneb) GetTree() (*ssz.Node, error) { - return ssz.ProofTree(b) -} - -// MarshalSSZ ssz marshals the ExecutionPayloadHeaderDeneb object -func (e *ExecutionPayloadHeaderDeneb) MarshalSSZ() ([]byte, error) { - return ssz.MarshalSSZ(e) -} - -// MarshalSSZTo ssz marshals the ExecutionPayloadHeaderDeneb object to a target array -func (e *ExecutionPayloadHeaderDeneb) MarshalSSZTo(buf []byte) (dst []byte, err error) { - dst = buf - offset := int(584) - - // Field (0) 'ParentHash' - dst = append(dst, e.ParentHash[:]...) - - // Field (1) 'FeeRecipient' - dst = append(dst, e.FeeRecipient[:]...) - - // Field (2) 'StateRoot' - dst = append(dst, e.StateRoot[:]...) - - // Field (3) 'ReceiptsRoot' - dst = append(dst, e.ReceiptsRoot[:]...) - - // Field (4) 'LogsBloom' - dst = append(dst, e.LogsBloom[:]...) - - // Field (5) 'PrevRandao' - dst = append(dst, e.PrevRandao[:]...) - - // Field (6) 'BlockNumber' - dst = ssz.MarshalUint64(dst, e.BlockNumber) - - // Field (7) 'GasLimit' - dst = ssz.MarshalUint64(dst, e.GasLimit) - - // Field (8) 'GasUsed' - dst = ssz.MarshalUint64(dst, e.GasUsed) - - // Field (9) 'Timestamp' - dst = ssz.MarshalUint64(dst, e.Timestamp) - - // Offset (10) 'ExtraData' - dst = ssz.WriteOffset(dst, offset) - - // Field (11) 'BaseFeePerGas' - dst = append(dst, e.BaseFeePerGas[:]...) - - // Field (12) 'BlockHash' - dst = append(dst, e.BlockHash[:]...) - - // Field (13) 'TransactionsRoot' - dst = append(dst, e.TransactionsRoot[:]...) - - // Field (14) 'WithdrawalRoot' - dst = append(dst, e.WithdrawalRoot[:]...) - - // Field (15) 'BlobGasUsed' - dst = ssz.MarshalUint64(dst, e.BlobGasUsed) - - // Field (16) 'ExcessBlobGas' - dst = ssz.MarshalUint64(dst, e.ExcessBlobGas) - - // Field (10) 'ExtraData' - if size := len(e.ExtraData); size > 32 { - err = ssz.ErrBytesLengthFn("ExecutionPayloadHeaderDeneb.ExtraData", size, 32) - return - } - dst = append(dst, e.ExtraData...) - - return -} - -// UnmarshalSSZ ssz unmarshals the ExecutionPayloadHeaderDeneb object -func (e *ExecutionPayloadHeaderDeneb) UnmarshalSSZ(buf []byte) error { - var err error - size := uint64(len(buf)) - if size < 584 { - return ssz.ErrSize - } - - tail := buf - var o10 uint64 - - // Field (0) 'ParentHash' - copy(e.ParentHash[:], buf[0:32]) - - // Field (1) 'FeeRecipient' - copy(e.FeeRecipient[:], buf[32:52]) - - // Field (2) 'StateRoot' - copy(e.StateRoot[:], buf[52:84]) - - // Field (3) 'ReceiptsRoot' - copy(e.ReceiptsRoot[:], buf[84:116]) - - // Field (4) 'LogsBloom' - copy(e.LogsBloom[:], buf[116:372]) - - // Field (5) 'PrevRandao' - copy(e.PrevRandao[:], buf[372:404]) - - // Field (6) 'BlockNumber' - e.BlockNumber = ssz.UnmarshallUint64(buf[404:412]) - - // Field (7) 'GasLimit' - e.GasLimit = ssz.UnmarshallUint64(buf[412:420]) - - // Field (8) 'GasUsed' - e.GasUsed = ssz.UnmarshallUint64(buf[420:428]) - - // Field (9) 'Timestamp' - e.Timestamp = ssz.UnmarshallUint64(buf[428:436]) - - // Offset (10) 'ExtraData' - if o10 = ssz.ReadOffset(buf[436:440]); o10 > size { - return ssz.ErrOffset - } - - if o10 != 584 { - return ssz.ErrInvalidVariableOffset - } - - // Field (11) 'BaseFeePerGas' - copy(e.BaseFeePerGas[:], buf[440:472]) - - // Field (12) 'BlockHash' - copy(e.BlockHash[:], buf[472:504]) - - // Field (13) 'TransactionsRoot' - copy(e.TransactionsRoot[:], buf[504:536]) - - // Field (14) 'WithdrawalRoot' - copy(e.WithdrawalRoot[:], buf[536:568]) - - // Field (15) 'BlobGasUsed' - e.BlobGasUsed = ssz.UnmarshallUint64(buf[568:576]) - - // Field (16) 'ExcessBlobGas' - e.ExcessBlobGas = ssz.UnmarshallUint64(buf[576:584]) - - // Field (10) 'ExtraData' - { - buf = tail[o10:] - if len(buf) > 32 { - return ssz.ErrBytesLength - } - if cap(e.ExtraData) == 0 { - e.ExtraData = make([]byte, 0, len(buf)) - } - e.ExtraData = append(e.ExtraData, buf...) - } - return err -} - -// SizeSSZ returns the ssz encoded size in bytes for the ExecutionPayloadHeaderDeneb object -func (e *ExecutionPayloadHeaderDeneb) SizeSSZ() (size int) { - size = 584 - - // Field (10) 'ExtraData' - size += len(e.ExtraData) - - return -} - -// HashTreeRoot ssz hashes the ExecutionPayloadHeaderDeneb object -func (e *ExecutionPayloadHeaderDeneb) HashTreeRoot() ([32]byte, error) { - return ssz.HashWithDefaultHasher(e) -} - -// HashTreeRootWith ssz hashes the ExecutionPayloadHeaderDeneb object with a hasher -func (e *ExecutionPayloadHeaderDeneb) HashTreeRootWith(hh ssz.HashWalker) (err error) { - indx := hh.Index() - - // Field (0) 'ParentHash' - hh.PutBytes(e.ParentHash[:]) - - // Field (1) 'FeeRecipient' - hh.PutBytes(e.FeeRecipient[:]) - - // Field (2) 'StateRoot' - hh.PutBytes(e.StateRoot[:]) - - // Field (3) 'ReceiptsRoot' - hh.PutBytes(e.ReceiptsRoot[:]) - - // Field (4) 'LogsBloom' - hh.PutBytes(e.LogsBloom[:]) - - // Field (5) 'PrevRandao' - hh.PutBytes(e.PrevRandao[:]) - - // Field (6) 'BlockNumber' - hh.PutUint64(e.BlockNumber) - - // Field (7) 'GasLimit' - hh.PutUint64(e.GasLimit) - - // Field (8) 'GasUsed' - hh.PutUint64(e.GasUsed) - - // Field (9) 'Timestamp' - hh.PutUint64(e.Timestamp) - - // Field (10) 'ExtraData' - { - elemIndx := hh.Index() - byteLen := uint64(len(e.ExtraData)) - if byteLen > 32 { - err = ssz.ErrIncorrectListSize - return - } - hh.Append(e.ExtraData) - hh.MerkleizeWithMixin(elemIndx, byteLen, (32+31)/32) - } - - // Field (11) 'BaseFeePerGas' - hh.PutBytes(e.BaseFeePerGas[:]) - - // Field (12) 'BlockHash' - hh.PutBytes(e.BlockHash[:]) - - // Field (13) 'TransactionsRoot' - hh.PutBytes(e.TransactionsRoot[:]) - - // Field (14) 'WithdrawalRoot' - hh.PutBytes(e.WithdrawalRoot[:]) - - // Field (15) 'BlobGasUsed' - hh.PutUint64(e.BlobGasUsed) - - // Field (16) 'ExcessBlobGas' - hh.PutUint64(e.ExcessBlobGas) - - hh.Merkleize(indx) - return -} - -// GetTree ssz hashes the ExecutionPayloadHeaderDeneb object -func (e *ExecutionPayloadHeaderDeneb) GetTree() (*ssz.Node, error) { - return ssz.ProofTree(e) -} - -// MarshalSSZ ssz marshals the Fork object -func (f *Fork) MarshalSSZ() ([]byte, error) { - return ssz.MarshalSSZ(f) -} - -// MarshalSSZTo ssz marshals the Fork object to a target array -func (f *Fork) MarshalSSZTo(buf []byte) (dst []byte, err error) { - dst = buf - - // Field (0) 'PreviousVersion' - if size := len(f.PreviousVersion); size != 4 { - err = ssz.ErrBytesLengthFn("Fork.PreviousVersion", size, 4) - return - } - dst = append(dst, f.PreviousVersion...) - - // Field (1) 'CurrentVersion' - if size := len(f.CurrentVersion); size != 4 { - err = ssz.ErrBytesLengthFn("Fork.CurrentVersion", size, 4) - return - } - dst = append(dst, f.CurrentVersion...) - - // Field (2) 'Epoch' - dst = ssz.MarshalUint64(dst, f.Epoch) - - return -} - -// UnmarshalSSZ ssz unmarshals the Fork object -func (f *Fork) UnmarshalSSZ(buf []byte) error { - var err error - size := uint64(len(buf)) - if size != 16 { - return ssz.ErrSize - } - - // Field (0) 'PreviousVersion' - if cap(f.PreviousVersion) == 0 { - f.PreviousVersion = make([]byte, 0, len(buf[0:4])) - } - f.PreviousVersion = append(f.PreviousVersion, buf[0:4]...) - - // Field (1) 'CurrentVersion' - if cap(f.CurrentVersion) == 0 { - f.CurrentVersion = make([]byte, 0, len(buf[4:8])) - } - f.CurrentVersion = append(f.CurrentVersion, buf[4:8]...) - - // Field (2) 'Epoch' - f.Epoch = ssz.UnmarshallUint64(buf[8:16]) - - return err -} - -// SizeSSZ returns the ssz encoded size in bytes for the Fork object -func (f *Fork) SizeSSZ() (size int) { - size = 16 - return -} - -// HashTreeRoot ssz hashes the Fork object -func (f *Fork) HashTreeRoot() ([32]byte, error) { - return ssz.HashWithDefaultHasher(f) -} - -// HashTreeRootWith ssz hashes the Fork object with a hasher -func (f *Fork) HashTreeRootWith(hh ssz.HashWalker) (err error) { - indx := hh.Index() - - // Field (0) 'PreviousVersion' - if size := len(f.PreviousVersion); size != 4 { - err = ssz.ErrBytesLengthFn("Fork.PreviousVersion", size, 4) - return - } - hh.PutBytes(f.PreviousVersion) - - // Field (1) 'CurrentVersion' - if size := len(f.CurrentVersion); size != 4 { - err = ssz.ErrBytesLengthFn("Fork.CurrentVersion", size, 4) - return - } - hh.PutBytes(f.CurrentVersion) - - // Field (2) 'Epoch' - hh.PutUint64(f.Epoch) - - hh.Merkleize(indx) - return -} - -// GetTree ssz hashes the Fork object -func (f *Fork) GetTree() (*ssz.Node, error) { - return ssz.ProofTree(f) -} - -// MarshalSSZ ssz marshals the BeaconBlockHeader object -func (b *BeaconBlockHeader) MarshalSSZ() ([]byte, error) { - return ssz.MarshalSSZ(b) -} - -// MarshalSSZTo ssz marshals the BeaconBlockHeader object to a target array -func (b *BeaconBlockHeader) MarshalSSZTo(buf []byte) (dst []byte, err error) { - dst = buf - - // Field (0) 'Slot' - dst = ssz.MarshalUint64(dst, b.Slot) - - // Field (1) 'ProposerIndex' - dst = ssz.MarshalUint64(dst, b.ProposerIndex) - - // Field (2) 'ParentRoot' - if size := len(b.ParentRoot); size != 32 { - err = ssz.ErrBytesLengthFn("BeaconBlockHeader.ParentRoot", size, 32) - return - } - dst = append(dst, b.ParentRoot...) - - // Field (3) 'StateRoot' - if size := len(b.StateRoot); size != 32 { - err = ssz.ErrBytesLengthFn("BeaconBlockHeader.StateRoot", size, 32) - return - } - dst = append(dst, b.StateRoot...) - - // Field (4) 'BodyRoot' - if size := len(b.BodyRoot); size != 32 { - err = ssz.ErrBytesLengthFn("BeaconBlockHeader.BodyRoot", size, 32) - return - } - dst = append(dst, b.BodyRoot...) - - return -} - -// UnmarshalSSZ ssz unmarshals the BeaconBlockHeader object -func (b *BeaconBlockHeader) UnmarshalSSZ(buf []byte) error { - var err error - size := uint64(len(buf)) - if size != 112 { - return ssz.ErrSize - } - - // Field (0) 'Slot' - b.Slot = ssz.UnmarshallUint64(buf[0:8]) - - // Field (1) 'ProposerIndex' - b.ProposerIndex = ssz.UnmarshallUint64(buf[8:16]) - - // Field (2) 'ParentRoot' - if cap(b.ParentRoot) == 0 { - b.ParentRoot = make([]byte, 0, len(buf[16:48])) - } - b.ParentRoot = append(b.ParentRoot, buf[16:48]...) - - // Field (3) 'StateRoot' - if cap(b.StateRoot) == 0 { - b.StateRoot = make([]byte, 0, len(buf[48:80])) - } - b.StateRoot = append(b.StateRoot, buf[48:80]...) - - // Field (4) 'BodyRoot' - if cap(b.BodyRoot) == 0 { - b.BodyRoot = make([]byte, 0, len(buf[80:112])) - } - b.BodyRoot = append(b.BodyRoot, buf[80:112]...) - - return err -} - -// SizeSSZ returns the ssz encoded size in bytes for the BeaconBlockHeader object -func (b *BeaconBlockHeader) SizeSSZ() (size int) { - size = 112 - return -} - -// HashTreeRoot ssz hashes the BeaconBlockHeader object -func (b *BeaconBlockHeader) HashTreeRoot() ([32]byte, error) { - return ssz.HashWithDefaultHasher(b) -} - -// HashTreeRootWith ssz hashes the BeaconBlockHeader object with a hasher -func (b *BeaconBlockHeader) HashTreeRootWith(hh ssz.HashWalker) (err error) { - indx := hh.Index() - - // Field (0) 'Slot' - hh.PutUint64(b.Slot) - - // Field (1) 'ProposerIndex' - hh.PutUint64(b.ProposerIndex) - - // Field (2) 'ParentRoot' - if size := len(b.ParentRoot); size != 32 { - err = ssz.ErrBytesLengthFn("BeaconBlockHeader.ParentRoot", size, 32) - return - } - hh.PutBytes(b.ParentRoot) - - // Field (3) 'StateRoot' - if size := len(b.StateRoot); size != 32 { - err = ssz.ErrBytesLengthFn("BeaconBlockHeader.StateRoot", size, 32) - return - } - hh.PutBytes(b.StateRoot) - - // Field (4) 'BodyRoot' - if size := len(b.BodyRoot); size != 32 { - err = ssz.ErrBytesLengthFn("BeaconBlockHeader.BodyRoot", size, 32) - return - } - hh.PutBytes(b.BodyRoot) - - hh.Merkleize(indx) - return -} - -// GetTree ssz hashes the BeaconBlockHeader object -func (b *BeaconBlockHeader) GetTree() (*ssz.Node, error) { - return ssz.ProofTree(b) -} - -// MarshalSSZ ssz marshals the Eth1Data object -func (e *Eth1Data) MarshalSSZ() ([]byte, error) { - return ssz.MarshalSSZ(e) -} - -// MarshalSSZTo ssz marshals the Eth1Data object to a target array -func (e *Eth1Data) MarshalSSZTo(buf []byte) (dst []byte, err error) { - dst = buf - - // Field (0) 'DepositRoot' - if size := len(e.DepositRoot); size != 32 { - err = ssz.ErrBytesLengthFn("Eth1Data.DepositRoot", size, 32) - return - } - dst = append(dst, e.DepositRoot...) - - // Field (1) 'DepositCount' - dst = ssz.MarshalUint64(dst, e.DepositCount) - - // Field (2) 'BlockHash' - if size := len(e.BlockHash); size != 32 { - err = ssz.ErrBytesLengthFn("Eth1Data.BlockHash", size, 32) - return - } - dst = append(dst, e.BlockHash...) - - return -} - -// UnmarshalSSZ ssz unmarshals the Eth1Data object -func (e *Eth1Data) UnmarshalSSZ(buf []byte) error { - var err error - size := uint64(len(buf)) - if size != 72 { - return ssz.ErrSize - } - - // Field (0) 'DepositRoot' - if cap(e.DepositRoot) == 0 { - e.DepositRoot = make([]byte, 0, len(buf[0:32])) - } - e.DepositRoot = append(e.DepositRoot, buf[0:32]...) - - // Field (1) 'DepositCount' - e.DepositCount = ssz.UnmarshallUint64(buf[32:40]) - - // Field (2) 'BlockHash' - if cap(e.BlockHash) == 0 { - e.BlockHash = make([]byte, 0, len(buf[40:72])) - } - e.BlockHash = append(e.BlockHash, buf[40:72]...) - - return err -} - -// SizeSSZ returns the ssz encoded size in bytes for the Eth1Data object -func (e *Eth1Data) SizeSSZ() (size int) { - size = 72 - return -} - -// HashTreeRoot ssz hashes the Eth1Data object -func (e *Eth1Data) HashTreeRoot() ([32]byte, error) { - return ssz.HashWithDefaultHasher(e) -} - -// HashTreeRootWith ssz hashes the Eth1Data object with a hasher -func (e *Eth1Data) HashTreeRootWith(hh ssz.HashWalker) (err error) { - indx := hh.Index() - - // Field (0) 'DepositRoot' - if size := len(e.DepositRoot); size != 32 { - err = ssz.ErrBytesLengthFn("Eth1Data.DepositRoot", size, 32) - return - } - hh.PutBytes(e.DepositRoot) - - // Field (1) 'DepositCount' - hh.PutUint64(e.DepositCount) - - // Field (2) 'BlockHash' - if size := len(e.BlockHash); size != 32 { - err = ssz.ErrBytesLengthFn("Eth1Data.BlockHash", size, 32) - return - } - hh.PutBytes(e.BlockHash) - - hh.Merkleize(indx) - return -} - -// GetTree ssz hashes the Eth1Data object -func (e *Eth1Data) GetTree() (*ssz.Node, error) { - return ssz.ProofTree(e) -} - -// MarshalSSZ ssz marshals the Validator object -func (v *Validator) MarshalSSZ() ([]byte, error) { - return ssz.MarshalSSZ(v) -} - -// MarshalSSZTo ssz marshals the Validator object to a target array -func (v *Validator) MarshalSSZTo(buf []byte) (dst []byte, err error) { - dst = buf - - // Field (0) 'Pubkey' - if size := len(v.Pubkey); size != 48 { - err = ssz.ErrBytesLengthFn("Validator.Pubkey", size, 48) - return - } - dst = append(dst, v.Pubkey...) - - // Field (1) 'WithdrawalCredentials' - if size := len(v.WithdrawalCredentials); size != 32 { - err = ssz.ErrBytesLengthFn("Validator.WithdrawalCredentials", size, 32) - return - } - dst = append(dst, v.WithdrawalCredentials...) - - // Field (2) 'EffectiveBalance' - dst = ssz.MarshalUint64(dst, v.EffectiveBalance) - - // Field (3) 'Slashed' - dst = ssz.MarshalBool(dst, v.Slashed) - - // Field (4) 'ActivationEligibilityEpoch' - dst = ssz.MarshalUint64(dst, v.ActivationEligibilityEpoch) - - // Field (5) 'ActivationEpoch' - dst = ssz.MarshalUint64(dst, v.ActivationEpoch) - - // Field (6) 'ExitEpoch' - dst = ssz.MarshalUint64(dst, v.ExitEpoch) - - // Field (7) 'WithdrawableEpoch' - dst = ssz.MarshalUint64(dst, v.WithdrawableEpoch) - - return -} - -// UnmarshalSSZ ssz unmarshals the Validator object -func (v *Validator) UnmarshalSSZ(buf []byte) error { - var err error - size := uint64(len(buf)) - if size != 121 { - return ssz.ErrSize - } - - // Field (0) 'Pubkey' - if cap(v.Pubkey) == 0 { - v.Pubkey = make([]byte, 0, len(buf[0:48])) - } - v.Pubkey = append(v.Pubkey, buf[0:48]...) - - // Field (1) 'WithdrawalCredentials' - if cap(v.WithdrawalCredentials) == 0 { - v.WithdrawalCredentials = make([]byte, 0, len(buf[48:80])) - } - v.WithdrawalCredentials = append(v.WithdrawalCredentials, buf[48:80]...) - - // Field (2) 'EffectiveBalance' - v.EffectiveBalance = ssz.UnmarshallUint64(buf[80:88]) - - // Field (3) 'Slashed' - v.Slashed = ssz.UnmarshalBool(buf[88:89]) - - // Field (4) 'ActivationEligibilityEpoch' - v.ActivationEligibilityEpoch = ssz.UnmarshallUint64(buf[89:97]) - - // Field (5) 'ActivationEpoch' - v.ActivationEpoch = ssz.UnmarshallUint64(buf[97:105]) - - // Field (6) 'ExitEpoch' - v.ExitEpoch = ssz.UnmarshallUint64(buf[105:113]) - - // Field (7) 'WithdrawableEpoch' - v.WithdrawableEpoch = ssz.UnmarshallUint64(buf[113:121]) - - return err -} - -// SizeSSZ returns the ssz encoded size in bytes for the Validator object -func (v *Validator) SizeSSZ() (size int) { - size = 121 - return -} - -// HashTreeRoot ssz hashes the Validator object -func (v *Validator) HashTreeRoot() ([32]byte, error) { - return ssz.HashWithDefaultHasher(v) -} - -// HashTreeRootWith ssz hashes the Validator object with a hasher -func (v *Validator) HashTreeRootWith(hh ssz.HashWalker) (err error) { - indx := hh.Index() - - // Field (0) 'Pubkey' - if size := len(v.Pubkey); size != 48 { - err = ssz.ErrBytesLengthFn("Validator.Pubkey", size, 48) - return - } - hh.PutBytes(v.Pubkey) - - // Field (1) 'WithdrawalCredentials' - if size := len(v.WithdrawalCredentials); size != 32 { - err = ssz.ErrBytesLengthFn("Validator.WithdrawalCredentials", size, 32) - return - } - hh.PutBytes(v.WithdrawalCredentials) - - // Field (2) 'EffectiveBalance' - hh.PutUint64(v.EffectiveBalance) - - // Field (3) 'Slashed' - hh.PutBool(v.Slashed) - - // Field (4) 'ActivationEligibilityEpoch' - hh.PutUint64(v.ActivationEligibilityEpoch) - - // Field (5) 'ActivationEpoch' - hh.PutUint64(v.ActivationEpoch) - - // Field (6) 'ExitEpoch' - hh.PutUint64(v.ExitEpoch) - - // Field (7) 'WithdrawableEpoch' - hh.PutUint64(v.WithdrawableEpoch) - - hh.Merkleize(indx) - return -} - -// GetTree ssz hashes the Validator object -func (v *Validator) GetTree() (*ssz.Node, error) { - return ssz.ProofTree(v) -} - -// MarshalSSZ ssz marshals the Checkpoint object -func (c *Checkpoint) MarshalSSZ() ([]byte, error) { - return ssz.MarshalSSZ(c) -} - -// MarshalSSZTo ssz marshals the Checkpoint object to a target array -func (c *Checkpoint) MarshalSSZTo(buf []byte) (dst []byte, err error) { - dst = buf - - // Field (0) 'Epoch' - dst = ssz.MarshalUint64(dst, c.Epoch) - - // Field (1) 'Root' - if size := len(c.Root); size != 32 { - err = ssz.ErrBytesLengthFn("Checkpoint.Root", size, 32) - return - } - dst = append(dst, c.Root...) - - return -} - -// UnmarshalSSZ ssz unmarshals the Checkpoint object -func (c *Checkpoint) UnmarshalSSZ(buf []byte) error { - var err error - size := uint64(len(buf)) - if size != 40 { - return ssz.ErrSize - } - - // Field (0) 'Epoch' - c.Epoch = ssz.UnmarshallUint64(buf[0:8]) - - // Field (1) 'Root' - if cap(c.Root) == 0 { - c.Root = make([]byte, 0, len(buf[8:40])) - } - c.Root = append(c.Root, buf[8:40]...) - - return err -} - -// SizeSSZ returns the ssz encoded size in bytes for the Checkpoint object -func (c *Checkpoint) SizeSSZ() (size int) { - size = 40 - return -} - -// HashTreeRoot ssz hashes the Checkpoint object -func (c *Checkpoint) HashTreeRoot() ([32]byte, error) { - return ssz.HashWithDefaultHasher(c) -} - -// HashTreeRootWith ssz hashes the Checkpoint object with a hasher -func (c *Checkpoint) HashTreeRootWith(hh ssz.HashWalker) (err error) { - indx := hh.Index() - - // Field (0) 'Epoch' - hh.PutUint64(c.Epoch) - - // Field (1) 'Root' - if size := len(c.Root); size != 32 { - err = ssz.ErrBytesLengthFn("Checkpoint.Root", size, 32) - return - } - hh.PutBytes(c.Root) - - hh.Merkleize(indx) - return -} - -// GetTree ssz hashes the Checkpoint object -func (c *Checkpoint) GetTree() (*ssz.Node, error) { - return ssz.ProofTree(c) -} - -// MarshalSSZ ssz marshals the SyncCommittee object -func (s *SyncCommittee) MarshalSSZ() ([]byte, error) { - return ssz.MarshalSSZ(s) -} - -// MarshalSSZTo ssz marshals the SyncCommittee object to a target array -func (s *SyncCommittee) MarshalSSZTo(buf []byte) (dst []byte, err error) { - dst = buf - - // Field (0) 'PubKeys' - if size := len(s.PubKeys); size != 512 { - err = ssz.ErrVectorLengthFn("SyncCommittee.PubKeys", size, 512) - return - } - for ii := 0; ii < 512; ii++ { - if size := len(s.PubKeys[ii]); size != 48 { - err = ssz.ErrBytesLengthFn("SyncCommittee.PubKeys[ii]", size, 48) - return - } - dst = append(dst, s.PubKeys[ii]...) - } - - // Field (1) 'AggregatePubKey' - dst = append(dst, s.AggregatePubKey[:]...) - - return -} - -// UnmarshalSSZ ssz unmarshals the SyncCommittee object -func (s *SyncCommittee) UnmarshalSSZ(buf []byte) error { - var err error - size := uint64(len(buf)) - if size != 24624 { - return ssz.ErrSize - } - - // Field (0) 'PubKeys' - s.PubKeys = make([][]byte, 512) - for ii := 0; ii < 512; ii++ { - if cap(s.PubKeys[ii]) == 0 { - s.PubKeys[ii] = make([]byte, 0, len(buf[0:24576][ii*48:(ii+1)*48])) - } - s.PubKeys[ii] = append(s.PubKeys[ii], buf[0:24576][ii*48:(ii+1)*48]...) - } - - // Field (1) 'AggregatePubKey' - copy(s.AggregatePubKey[:], buf[24576:24624]) - - return err -} - -// SizeSSZ returns the ssz encoded size in bytes for the SyncCommittee object -func (s *SyncCommittee) SizeSSZ() (size int) { - size = 24624 - return -} - -// HashTreeRoot ssz hashes the SyncCommittee object -func (s *SyncCommittee) HashTreeRoot() ([32]byte, error) { - return ssz.HashWithDefaultHasher(s) -} - -// HashTreeRootWith ssz hashes the SyncCommittee object with a hasher -func (s *SyncCommittee) HashTreeRootWith(hh ssz.HashWalker) (err error) { - indx := hh.Index() - - // Field (0) 'PubKeys' - { - if size := len(s.PubKeys); size != 512 { - err = ssz.ErrVectorLengthFn("SyncCommittee.PubKeys", size, 512) - return - } - subIndx := hh.Index() - for _, i := range s.PubKeys { - if len(i) != 48 { - err = ssz.ErrBytesLength - return - } - hh.PutBytes(i) - } - hh.Merkleize(subIndx) - } - - // Field (1) 'AggregatePubKey' - hh.PutBytes(s.AggregatePubKey[:]) - - hh.Merkleize(indx) - return -} - -// GetTree ssz hashes the SyncCommittee object -func (s *SyncCommittee) GetTree() (*ssz.Node, error) { - return ssz.ProofTree(s) -} - -// MarshalSSZ ssz marshals the HistoricalSummary object -func (h *HistoricalSummary) MarshalSSZ() ([]byte, error) { - return ssz.MarshalSSZ(h) -} - -// MarshalSSZTo ssz marshals the HistoricalSummary object to a target array -func (h *HistoricalSummary) MarshalSSZTo(buf []byte) (dst []byte, err error) { - dst = buf - - // Field (0) 'BlockSummaryRoot' - dst = append(dst, h.BlockSummaryRoot[:]...) - - // Field (1) 'StateSummaryRoot' - dst = append(dst, h.StateSummaryRoot[:]...) - - return -} - -// UnmarshalSSZ ssz unmarshals the HistoricalSummary object -func (h *HistoricalSummary) UnmarshalSSZ(buf []byte) error { - var err error - size := uint64(len(buf)) - if size != 64 { - return ssz.ErrSize - } - - // Field (0) 'BlockSummaryRoot' - copy(h.BlockSummaryRoot[:], buf[0:32]) - - // Field (1) 'StateSummaryRoot' - copy(h.StateSummaryRoot[:], buf[32:64]) - - return err -} - -// SizeSSZ returns the ssz encoded size in bytes for the HistoricalSummary object -func (h *HistoricalSummary) SizeSSZ() (size int) { - size = 64 - return -} - -// HashTreeRoot ssz hashes the HistoricalSummary object -func (h *HistoricalSummary) HashTreeRoot() ([32]byte, error) { - return ssz.HashWithDefaultHasher(h) -} - -// HashTreeRootWith ssz hashes the HistoricalSummary object with a hasher -func (h *HistoricalSummary) HashTreeRootWith(hh ssz.HashWalker) (err error) { - indx := hh.Index() - - // Field (0) 'BlockSummaryRoot' - hh.PutBytes(h.BlockSummaryRoot[:]) - - // Field (1) 'StateSummaryRoot' - hh.PutBytes(h.StateSummaryRoot[:]) - - hh.Merkleize(indx) - return -} - -// GetTree ssz hashes the HistoricalSummary object -func (h *HistoricalSummary) GetTree() (*ssz.Node, error) { - return ssz.ProofTree(h) -} diff --git a/shared/types/eth2/testdata/11567104_roots.json b/shared/types/eth2/testdata/11567104_roots.json new file mode 100644 index 000000000..26e7f569f --- /dev/null +++ b/shared/types/eth2/testdata/11567104_roots.json @@ -0,0 +1,16390 @@ +{ + "block_roots": [ + "0x7a59889b35d9f806a5e25f1184d38d10a5807324f0c4201a6ece0f7558319200", + "0xa05298717b1b27ea9c8dcb0ee800dacc51306ea8d51b1eeb18252b130206f0be", + "0x2a4f014e6be86c61840a607e37821213f4a6f2cea23ca14561cbbd557974072a", + "0xc957767374654cf46e3e3377f93611684ef256164a0a98d0352c9467ce0f501e", + "0x8b2d970f7a258f551f00a810743f94992b23dbfc77dc2c98bff9597299407fb0", + "0x6d6f4f1f002d860faf13966270f58c9ce265b7933999c3365a9971d1811f5b7f", + "0xbf823e234dd372253ebf12b03a0cc521b339fd8ea450f47cae03b8d70b2938f9", + "0x1a9ea66471341d2a29d122062b4ce1ecb89ae812477f69bc4ad949814a26025e", + "0xd6a4a8c2d53b94ea7a0a953921e29db211ad57a361dcfb25ced368590f6a484a", + "0xcb016f3945552a61da29db97d2b3239e26a8740b8cf5960afac20c62ac49e03d", + "0xbeec34586bf132e9ba12e820c615f909e006473ee8f3044787e3249e6ed30959", + "0xf4d9aebcf1753cf04451f4b06e5f6af7321aaeb25910b33b007e11abe0fd7db5", + "0x9b0e9c4dec9677b2fd4d76bafdefae7bbbdf0aabd71c50624c2787da317bf541", + "0x217f5278b516195fbe0fcf6aebb85e2ab3032456ac72f4b572c39846af46e63f", + "0x3c15a2826f4c0d6279d5bd34f7239b66297a8319f443351b98158fcc10858fc9", + "0x388ebb11ad1778d5b5c4de6fb80bfd07f9f55eef11e68fa154161e6ee5ea8559", + "0xb268433ea243988a7beb2377068374f8647a8b93f3ea158393ee2ad3d6f3cd04", + "0x49fdb89db0a731fc062225a43465ccad2555bc81ff3c3a99c781acc25f7d374d", + "0x06877b30fbc0e0864f59248f278b5cca327ebc9c86e3dba2f2dbbefc059fb0a5", + "0xcaea4b0871b074ce8429632e117d12095d4a4af191af0a342cb79d1aa6920fef", + "0x4fc343d85748d5d0bbb3f036cf78ac1485115c009bd916d864809cd3cc77e0d5", + "0xda5bb57a68e7ae7e5f6bc67b4e3853d4537acb754f8b93766144776466cc0e53", + "0xaf613559d7ddfb0f638432e0d287d0afb88134efb1fa2c410bd963516ed25759", + "0x1512838fbce5fc41dcd8cfed7145d9726693aacbe58981fdf689e74715a29ce7", + "0x7356ee2258bf2e6060a5608a43fefbc5208c276a0f0b3d0f62c59da3e1af3768", + "0xe5cd39ddd55c82e78b2f4e87913934157b075344782afce0f7f7cba4f852964b", + "0x566ed3a88bc3fec4d2b492440af2e6b951afbe78ff8693f4c901bb922993a92d", + "0x269de782a1fa6d44807f9e8fc865fc636095647e59517ff279f3bb6b09985807", + "0x4416fd9521ffb037fc4a2764b31be201472637a4b4b2546f674ad3813dcf72ae", + "0xd15b89745570db4fb5c312af44c4a3a9a9b0ead154345a99bc38603621bf8cdb", + "0x34a35e0f9929eafa58a907fc1257acd0b811b5752b64b1b2cd0b8c69923a1a6b", + "0x89cd9273963825cf3b9e0351c8da37e4a2362ac1541e240f1ac2c971098c39d9", + "0xace355178c1eb7ca1a6df577db2651968d17c7a8654a296b23905121baec41ab", + "0x32208f7317c0982b9723d586d3652c0164ca389af83f47a0cfe08cbb4cc20970", + "0x6e9a7516add85b08a5321dd8648bd4c02967fde554864c09499fe7a22cd5f9c7", + "0x2eba5e74d96fcd8a551f619a1edda2d7143a6146049a97d8eba734fd843bf1f1", + "0x485c08134859added0e9ea88cb2205ddfe283ef3967f9efaf0345f6e408ba481", + "0x6b68cf9e3c01fd7096674dc615116fe53f323e6f355bb8e731ff17a442f83297", + "0x418c0ff03447f558abb0d6b43fdad25a811ed478f6389f9d4b4ba6c534720c3e", + "0x02641726a7f850b59f8abb0632fede58c2e140a28369ae4077abace0de5476cf", + "0x1e30231773285ee8271fe36f25c48b8378e8a2a4f6868774f4a0117e395e32a0", + "0xaec8b58da93b6fffec2878bfe53ad4ac1ad781427d74a441c94e9e3188fd5750", + "0xfa26ef4e04f4b397b9a512795751cfe3040e630a6eea97e1172acd3109a3e687", + "0xd2088f7da489571fe882cf3e6a9f22878545b48757e63121347d6dc9a1dbcc8c", + "0xd483e522f2fefa7f075ce0e274690cfc35ad681b132c9a147e15830c69c90b03", + "0x36cea5cd18240605cb24b65421a6556e371b6da00d1f83890a0fb2043162a1f1", + "0xa9ba22e7a0ba17ac7f312a83cc796f9b865fc313f736a3fa6eb3cd43736714ea", + "0xecec0ab749c143f4bd481820949623ffbc0d3f97b3e10b7a4348dd7412e533ec", + "0xf4c114cbef59cd4ae6d4389c1c1f8bb99f3f7490bef56ea7e94a0e4401b5016c", + "0x13872b2e2cbb634e42102b59768c51e892ddb36e2599fb3e2935d8bdd09669e8", + "0x39c23e0c3885fb231d811f8df5ff041c6b47144bf4c715e54a1c08a6e43f387a", + "0x83bdd0ddd5d947f55b5b97f97dffed6ea5826fe4c8001c16979d812dd782e8a4", + "0x408f11e4510ba80193b8f8b1f4dc75621eaa0e1f9b5eb3a324c80d75b29599fe", + "0x716327c369908929f1381004516e0001ac0d1484b3c341c0c8c7e1a73e217ecd", + "0x3758c49a2e0a8e5055a1413c3b8caaf65c733979149da7fa23992f67ff297d31", + "0x4890953beb826aba320feb02fbe56254c986add25d00a1a27de340d34926cc10", + "0xfc19c211367ba0a41cf8ed13c22957bdfb5e2b4c9d840f12fa619a3b547d2970", + "0xed04958257b77c238f9df31cedc471576c5392ca4d8956ab594d55b9da679357", + "0x61503b87ad0b0a12395900ede1d35457f5f929e3d71ee98cc1ddc3168ffdf179", + "0x658d67e5abcd733b1f9f48c59948c85a55b679682f4bf4f6abd561d8605a8723", + "0x85f9988d4051b60d19cbb8ffb36a39158cf389a7d1b2c3e142db2f0a81a279cd", + "0x1c3fa9d4359906169d8131f29012e54633d13763ac124ea8b891aea4cb8b1b4b", + "0xaa4f2177785a56f30b78e3523d3ce127577553dc3b431f8b18d05e30766f2f55", + "0xde8ad4f5a397d64753f587b32f2811024c5bb1d276541a6f2c82607119c43c3e", + "0x1f2c6a272914e663ddca369f9bfdeba17dfb595178f8638314f1d57adbcdf64a", + "0x3d129230feb761708cbbfc4ade2b74f4029d46754e97cd3060c566fa685e2748", + "0x4544a1df100b10e6421ca371669ed279209d49cb4d9c604aba46ef6ace64398c", + "0x953c701b5ac75c34a6ddb83de28894539a7ff3c042d22c9845e64ed8cbeab37b", + "0xd9d9934364391bd109c717640863a7c2d90fa8d1273fe465f5755d1eda497668", + "0x490d4b568d189fc6e40a0ea0eb295c86aeec8645923c08a6310cd9cdb9b0da7c", + "0x9c89332d0060ba42a7dd78ce920c004486030b78b97d08d3615fd405fcd08866", + "0xc5a0079429c530762cb9ad0cc9c082294b0ec1e97017b063544fa67c0d6c8367", + "0xa5a0083d03cfe4e73acac59cbd87f393b04be090fb0075cfe0ebe481f890dee8", + "0x600e1a931728cca8e0b9adca0e5aec8612e1ee992ba3939f929ac1ba21018e67", + "0x722a321dd03705cefbffd429e620e44953bb9502b6909c751b1d5957eaac9f0c", + "0xac39b99b9f3269f554f4e204715681e893293a74e1bc5a2aec02aec8b110771c", + "0x092b33caed18a88e0396db1f54f779877d17713ee6d73961df65873fe3065140", + "0xa868b9bc6a89b03eacd5729bf1afb09354a5a1571f0d3b5fbeef5d44a783645e", + "0x288c1a1a16e8afa2ab8d48a892432523c408f9177e2980ca43a357ae08c7b58d", + "0x6056042b0d5986c7f50cbcaac8e0aae4d15bb29510d53d066f32e213b04183d6", + "0xc41bf579a846bbb0ef92e5ab423337f19f35b61c885ba4aa1dcc85f4c3098be1", + "0xcc7d918743ff95be8ad899f9a0c33f111e41e0dc2f09213fc4efea5f955c0f4f", + "0xe5f72d94936a25e11d06a8e6efd014f7c1882d9d57cddf49a2042cff4c712d9e", + "0xedcebadebf4ef0e601a854410f42fb13ed2b73ec1e11be3fb3056735a1cd7777", + "0xc031058c9aaa459f1ac8b289b993d916dd8f8139e935f4ad4eb2eafe437ee4f3", + "0x1e1ea064bdd8686ad3362ecfe400f6e80ab327e667fb9464acc1b111815acf4e", + "0x933251f9e8f58dddf9aa59db00a7ba1581f26415c10e19ed4ee269d9d98e4372", + "0xed95a207b1ce4d14af321a958ffc66018f5c01c43a562a87b90675cb42a86576", + "0x5637dd5eb0252f08e4a2f4928b549c79178d4819d18ae4fe9623a69f214db24c", + "0x95b3094bed31d71b23df7c2590dbdd9050af41d912e238c65a91b47e8adff020", + "0x557ed3e1ca980ef5375b72d784dcbbe7a45430a33957c8fcfbb450f30ddd2015", + "0x50002cc24137f2ff9639f5ef567b288fe4e956869a7acae74a8d95abe369e16f", + "0x461e9eaca98e6aa9ff4eb359e7ddeeb7a0b4da3fc6ca4be6aa34df2ec29ee470", + "0xe0dc55f0c8f3d7bcd1ea206440596bacb65a39274caf410e1a5bc644b2231c55", + "0x48dbde82974ea6c976bd81eb620bc986648a677e00a59890b554524b73b00c3e", + "0x4dfbfe3b680ebb7c4903086891e9f077c62f83d265769e4b8ac1216fc261a756", + "0xc0ca129e1f9d439ee3fa771458c07f234e360bfb68bd44af93c4da1d4aa65309", + "0x87007b288949d55805b130c216d550d12c304a005f94c1b3f92de380f24fbaab", + "0xe9aa8e85425e7e028f196228039b34d5e4a72d429af80104189fcf8af90abd88", + "0xe9aa8e85425e7e028f196228039b34d5e4a72d429af80104189fcf8af90abd88", + "0x87c110ec37df45fa338abae7139f4ac521eec96cba1d2df95389a18b639c2529", + "0xccdabf76c9a306b7ac50935cf66dc3d8b51b395063bbb1dd1dbc15c1664d1d77", + "0xab74917634d15ee1ea6fb52108396172a741981f64c100208ee5c2edcbd4c895", + "0x81673ad014f9f3a72f3af5fdf32e24afd9f838f70cd721e878315056f1829001", + "0x4a3a00f715b41cbdba78b16993a5d9dddb5a48ff9cef280296e10681dff4aede", + "0xbcc4cf45e9f51ad9931f7b3a2cc4dabdeee1edbc5fc8c99847074b0f9619bc82", + "0x34092cae70d0447df4e136578fd9f994d672a0b4d60add9a1ad5d804ba8d47d2", + "0xc3b35bbe1b8bd50c324b473824626688672827963582ccf76116fa418cd9c15e", + "0x3037e6cefb9c28de957f5fa99f18f76fc1d896b5fa21c3acea62b40dddb09176", + "0x2ae60b5633f40d15991e762fad8f0541847ec232253f782dca584a195fb43aaa", + "0xb00bf775fcc70a0a4db905d25fce834d3985d8b5985c401fde11e8df9d5f131e", + "0x1065d7b71bfe7cbef4039e3a0954ac572286681f9735d2ce77f247778bcf78cc", + "0x66aa73d0ad9b5fbbeeb15281e156d81ff0b3fa2bb000d9b5972a257a44cf0bb9", + "0xe862791f8bbe8010e3c8d5a07fafc733e77adf53fcc902c321239782af15cdc7", + "0xf20a04abb10e60fccaf7752a02a7c73987d099716585187be7007607bb88a5cb", + "0x7f95a3d5b98446eb36b56e0582958b9091494674e89805170fb5002f32a96d0e", + "0x98559ece05eed2dab698862b500052a59d5f756d928575b716a91a37fc247f37", + "0xd32d486fc687eb01317a3dcf14637408bf518a7eb23835d9004d49bb9fe80076", + "0x41821ef00489370e5dd1ec5f73b1802c6e4c2fdb243890240a84188b372e740c", + "0xb5266ab0c8911516cd46757ad57756cf8034a86d6d3bfe8ed3674d94ff44a2ee", + "0xc3121e82fab98d810e5db10a11700fdc68fef0000cc5d1dc6f2d865b33e1467f", + "0xa4aff25d9b898ab8bb0a0a9155f0f10550242973277a465d8c464f746a6488e1", + "0xa317cf5b271355c91e0568077ad01d274c18140ffdad8a2cef18f61a78111170", + "0x23d37a778022ecd1c80427490eb95e5636099e04bddb4daacf389904fdbd9d37", + "0x1fe5c9709404053136c086c514dc96ec00febb775e255bb401369d65151b6bc4", + "0x6ef63b94e1988e44671ea17fdceb99c4595ba7776578da0349f9c4b36c42ab9c", + "0x28f1b594718bd674268a5ba21ab4d5bcb767395472c98b786d25f7563c77918f", + "0x351a4bbb5ffd9b570ee5ce1d57040a5dba0d9ee46a29e7733f2ab9be7b483098", + "0x78c2039186290ad7147ff91942347bd0e1ac0072e486fdd592c0677999a062cd", + "0x780b3dc23013f953d6934ad175b626e99450c835cc713d42d790fdcb164922e7", + "0x18bee4992bde5047c9b80193f74dc9c86813f3d693ca6a4b9697d54eab87e0a2", + "0x593c3e8a04478745c0e1d6be3c0a84435e53692bb214792417b856a32f9ae1a7", + "0xc45754b44bd61dc717e067090c1205c6f2811ecf97a5f9900f3b572dfb58479f", + "0xb2c8e99d26ee02ee66da266e251fe8db50566fa483d76145965ad067c62fe755", + "0x4ef4fc689e393822bb83bb82138d3e1f77c298443cadfdb118dc1ddc6b0622dd", + "0xae5eae78aa1cb439a300b23ec0917382109cbdde22578ca00b54f2676c0aaf2e", + "0x77948d39c53d8d27e5e8de623a6a67c949d5814838fa680b7c7411d8222fa4ce", + "0x8c2383d50534baaf01f0585865fa99f749aa99caefdd2ba145f8264c0112c463", + "0x4cf167174a52d496ad6d86504501eb7d527a7710c6c29669efedee76a5427b0f", + "0xe8885e76a58a5061a3e09148e9972f6fcde77202172711c4732032db95a9a7fa", + "0x7d023d5062a0390ffca870c3f982892b62be7c28df460823fd5e9f5cd72b1c39", + "0xbe74b701729debd5f9cef109e8066d7845b8bed3deb1621cc551406f915c20e3", + "0xc9e260b8f62c109abcf2b3e732c44b9e62817e30cc006ece426ac56cd60e94c2", + "0xb30e63aac33194ef790cd44f58804024bb06fdd7448a3856bfb086412377586e", + "0x118c98ad4a727ff2b281be3947854d2b6d5d03847de699d1d809869343f913de", + "0x07099e68b4276817260eace9b344c6d3bc298b3287d176fb4cc59790abe79e76", + "0x8bb7474ddbd88c621ea188555cfe69a4f4455e2645e84ac63d8bd3d1a2725d2a", + "0x155fa2a89f1c0db75e72562ee192ce624a04deec9d0b04e27c914cbdd254327b", + "0x63639e104a5b61eaeeca35b19e53693e1f988154a9cf1e531e887fb1ccb8a019", + "0xff7e9174e623515a87d35c0e29c9362c6d7c9b3cbdc31df775aaeff75bd0c97c", + "0xa78d36a55dfbba396f0ef70a302b53240e782b69f28d9e08f495a324d1bae1d6", + "0xf8b19c3a4a62c81a1c1459a40f1ab927d2692985b4ecd1ff1dfe8678e7f17d8c", + "0xce3ba49650ec27c6aab85bde38c0130db3a50ce13033b8449d8233121b7a5102", + "0x272ad89e3451114458bcc29ca9be00e531bd8d1007f2c1e375a888c500145a8c", + "0xf559b4ed57b8399cfa1f03a3dd5b438bf3b4e50af92c008b1f605a955dce7a89", + "0x40abe936c721bb8934951e5262bebf2ffa52f319c66f510df5768a8852d04242", + "0x2508b1c27744b574a088114132ffc5eaeb480d181a02f2c5c0029de86e445acf", + "0x0fc9afbad79b7a603c5482f34fd1cf17c16927bc7bca8bca691495b0ea98c064", + "0x15d8b4f2c1f2214e9dabd55b9ff0df8698fed529f9449ce746435d166511ef26", + "0xee19b3c689deb5a5885e2adbb90358a315b9bce0d8f487aca3f560c3c6f9cacd", + "0x5fa7cbb9c48004a983841f071d8641bdf46d2c6b1b2cb04be1264e732aa87cc8", + "0xb1aa6136c6ac3c70fdab6fa26cecc7d923fe0d0aee5fdb3726aaff22d447d091", + "0xcaafe43754060ef0008ad60a6c1ab2803f28ddfbb52561dc1a863dfa1479d19a", + "0x20dfeea1fdb91c8ccbd0b36506815dbe3ca8d3ef22cf6f46468776f06497aa7e", + "0x045a1a9268af7d128a80aaa4db63ae1fe5fac5c8721f84c03d2232610dc961a6", + "0x671b32336b50c48df68fdb63c6b233501de53d18149dee8a6b38ce2c50042e31", + "0xc00939f390ff53cbdf1f09e4da168bd6a021011ef6bf19dc264c2330644eb437", + "0x873f38f984ee7d7f2c9069cf9f45c41e57d11d7a68e90079673e9c62ac96f9df", + "0xb34ec7a874f564221819d5091bbf790e203609722a4b99be19ac250cbfb72dfb", + "0x92b2fd5030550d8f525412ef9e0ef1388afa60cb04cac3c68a4eac672eb6014f", + "0x416fb93ce1abd407cfdeec9d7777274a8b01098c49a4cc9d45882cfa0b3c2af9", + "0x1bd4116200fcd33ea5120fcd5abce1d3dc492258277ad2ea411d5d5543bbfa73", + "0x693e0fc3b61aa0723beede62d5e08241000c2d3708c3b2f6e43b3e5de1eff16c", + "0x01bd64663665ff81457c4787ed08a2b86bdaceea92e3f54c8a46cbc116f623bb", + "0xce1cf98af18cecfc51cf50d6b8ff7f6045047b0f398fcb0c4e42d26d0a7c7e53", + "0x7c851aae6af898cf66577cc1aa89b21adb9d4fb4853ace7363998c880b32db99", + "0xae43b3123a884edcd99fe1fb52872c0ae7c06a572eecad96326b7da32a7c2528", + "0x5b78f0c5036fb10ac9256a737fbe10565664d6bbea8e5f39acee04b4f1be54c9", + "0x42faee27f532f245a2e450eb7f472d28aa4baa3d077d1c28e6feac148ced8571", + "0x4479b8872bb71d2506eb01333ac23cc62180c3739f3e92d6614a0935ca3f2f5a", + "0x8c69a4edb351765484269be0d476830078f9aae1554a231ca422e4643ee5c019", + "0xfe2b88854fd9d445c01685aa565dc95849449a04f73c3e8e034244d3a3087038", + "0x47011a1452be214981905a9c6ec718d2246861c2df46c18cd438d8cdc682559c", + "0x0ff3da6fa963c8e887baee82da605bbce92e53d7f10e0d8d37451f92e54cb7d5", + "0x3bdc984fbd9b04de692d87fa3f1d14bc6ae53a72e9d35e3a8b78c1b0cf7ef548", + "0xaa53adca458e3b582423267e359f8fdc0d7577e4d7541474c1aa5a332025119a", + "0xb71b299752a1917b8a0d2b70f83364f8bfc5559f57cfea1c1c3388fe6ce79bdc", + "0xf8a3872d4ed074416da11950329e073adf91bf015675a4ea22651ced9e9d6730", + "0xdfed87ef02ea6f98d1ea01e984042f4a5371777cae84230e425d5d40be9d5cba", + "0xd729a9ad42c4b724c320ecded0a4b8894a457b27ce07e2bf00790b33a49b7fc3", + "0x191f37c5a125b42747b619bb109a5929e795a48086e01f7771a9336ae166c022", + "0x2304021a914f03b5e70caffe8749dc7e559666acf7a7682fdcd47acf9d83eac0", + "0x76163168f0884cb68991fabbcbabc644f7c14d568de8596ab68c0de2e3d8d0ef", + "0xba09172a9ff5076728a9de0498aa0ed15ed3c0800de15a0a4ba28fda46b85285", + "0x6df97dfba65146ba61f98a18f9c1d079860be085cc490d5b0d096874748b0173", + "0x3fbb43e4cb3bceec7fb3dce04e915e10963bd6f8bc91813007912828b2678c15", + "0xabaff3abb2f06f9aa2ce54b0516efe43909e60239455b2f740252430c95ddd6c", + "0x5dad48d265e70f112892377b1a7071c2b08f4dcd415958222544a61c7be45797", + "0x2d3a9003dbc81334dcc1f8f2f14da2739b10b3f1844a97a7ebc220b7a2ab95f3", + "0xb0e4e742702609797a3ef2573bf45e5ff864aac192bcd9575a6ecb08d79b399e", + "0xe7af80552aacfbc7380eeccaeaa501021797b8d5d2e094e33f3ecdc2642311fd", + "0x55599f7a9a25c1d54860c5c872d635b326f7c701ec84abcf980a0848c0b57fad", + "0x152b6c4d898a30e530607f066949dc4703f302c5e21c6a1266abcd2e7042d47a", + "0x55aecee9a0fa50e618134b4a93889ef97f5d1f894822535026e1a67ea6a34937", + "0x49b84282a6a2d05d642f01afd178ab147c9bf3eedc51fcc19a5250476ee1f9c8", + "0xa2e84434003cc4dd8ff8beab03154bc4fad1a05d501506baf6785ab551f0129e", + "0xcd210f01cc13888cb64e51f0040a504585a9fa3d4b55996cd8e0cc5c9520027e", + "0x84d26609c4f105daa3a6122442288864d70aecb8d708fd1b55767e29ea8664c3", + "0x42e8c2f44afc51560052eeb9a290c2738a283aa5d392ba94a7b85ab8db90bc00", + "0xdf9fc6666c6f4ce436dbb0ba4bba918db051607b568f7471cf1b2b1082a681a3", + "0x0db7bb455558b7132a40e23381366f3a492c31655f2a5fd2e73c7388bbe95fcf", + "0x8c133dcc3e3ff58d490c1f73e0bfeb41bafacff6c6fc04751e062e1be4d7b592", + "0xf476f5a56072358dd0ac87fd7a3d11f0ef646e4130476b937262499281d5e0b4", + "0x5c694d29b61522f284c07bdf4da14507fa06b4fcd1559ff695537dcea8877cdc", + "0x592374280d73899405b037f4cd57b58bec4fd1e0384192a787a1c4b60fd9ef07", + "0x5ee1eb7a1bf9ef750e8fef7f3cdb04b1fcd9d9beb2a0a8a9f354a06c573064bf", + "0x9a7c5a1859e9e4376dc556593064b447620a73ceb20bf55f9e498ec2449af436", + "0xe48d18516fc62f0fc839125a38de317406737a605f7f4501c46612318178aba8", + "0xafb6f63a77c9422586177458a64d8a4f73e4f1ce07f6c13da471416135823219", + "0x2d70069147216be9971175ad4aab6e7568dc2192f6af19635d6bf9189a143ad0", + "0x963a4115c60250f5b6b84db028d14d695f2e75b4e7ad84a270201e8787f48894", + "0xd0771f07275734f6fea361528fab05411f901d3f6331db930ea7c014336c1807", + "0x402b019fb7a081fc2022e080e7bbb04646135b1c5af677a93396e0223556b7cf", + "0xb91c6b2fa639aa68bdf8e8622b4e3d3ca2299060d5127c747b0c93e5a66213f8", + "0x4122ab1a7c93888540bd22181a1426336d9f8cc4ecb87a57492fde866f47a6cd", + "0xa8bb6d200bd0850f1d285d63d54e730442edcabfb472dd58278383f2f366f8ab", + "0x11814f37a575c472d3e4434055a76a2d1000515e4e274ee21d7711009fb72a99", + "0x569c19e85623bc67b036680a3c38fa5453d94a6e2449f37217eaea25737ce298", + "0xbe728e36a541f2665318648887f8fb0b3f244a75f8e030b19081bf8fe50d2f4c", + "0x8f1b1531d28d242bfb40bcacd5d89d01d560f44a165bc39a993806199ab5c8f2", + "0x95d251c1c625c8b62a48910cc72c6b273ba16bbe1acfdf54c86607c269b1f171", + "0x364fa2272b4513996169ff312556f9dbd0fa0e19d18232128a14f28bb597d798", + "0xbbb980986accbad236ec05817b1046ade371075cd05327f08ab25efc778299f3", + "0xedef77dd82fbd586a48a072fefd5107b7d9b11e24ce458596918aa4e691632bc", + "0x7344326e1d6d158580b000c38729990b81be5e8bb00a9232e8c25fcc72795200", + "0x69c815ff947e489eb2e6c3a6fcbb94f53bb522622ecf501377f036cc3c07b9ea", + "0x0f9769d51dbdff0fcb20e8da22db175d9607c7b2e4843a7eff76e248972a88ac", + "0x22151f109b96c120bb0e3c9843c5d5fd34b5cfbfe5f9d33dc77197e20575caf7", + "0x21147285c69cb78728e7385f62cacad76c081d6b6b174e9ce3b0ea8e348e2c57", + "0xbc53c447628130a80b708b22e2b56bdb387842aed4fdc74bce9717e3bc0e21ac", + "0xec967d77b6b137b29e820199892cbc60027c97bc1d496225bbb7dda217b3ce84", + "0x6ab8c06f05a4c7b41c258acc7feddb64854d891cfdac8bb571bdc42563e42889", + "0x86b2d2bbdeddbffde7157ec6d086b9b7134234014818fbe89dd03a8149f90a39", + "0xaee61f31a13e43ac27e6dc68d4158d04e0cd5089710ea6908b3b9a7516706d5b", + "0xee8737db174674a30035a3ef2cdb727f6e1317dc10fc8538fce5e0b3e161beac", + "0x75a1480cc1a9fb55a8c179c2f3d8526f54fa431b86bcc5960ed90b00da5bbb2c", + "0xa526904d8da96e701d8d844f7e70310f78171eaddf32477a979e3417a762711e", + "0x7334c716385744897335e0e037dca5d65955d0fb3ac03d198f24560c3f51c381", + "0x6fd7735d6d7346755ae9b167886602c9ee05c8bcc238741a4f8ca4df47395ab5", + "0x4d0ab818f16f6625d856b378eac76e16ac6df6b2c2e6b889bd894e2caa9b03e4", + "0x45b3e38b5911387e9bc27944aee68b1cdb877d727d75e18a63d2b96118ea41c5", + "0xedd71998eb3686a5e1d34a42e90c67aebbfd748dce158224c12520cecfbf7ce5", + "0xc1d2e4ecf98bd6588e8f07ce1ad45f1e4bd5590e79d6d5b5f1d5b42eab8a82ab", + "0x51b6bd819465060aeec5d11c1bebbf32d687c99e3ee9d34ec896913f2c9c89f6", + "0x160200158982ddeaafcac0907c28e19130c572f358cf7293185af80865623aeb", + "0xeb2af82404848f5011c52b8bb8a745b8e45ea527a6715e27716ed5e018e46a5c", + "0xa3c581ac820e8aac8fa8148696e5b33f460eca46814eede8bff338c040596171", + "0x014cd53fecd4c2f7b235860af63726e27171752bf72c22c08b0350301edb7e4d", + "0xe57e0937340e1310029b6fb8433480f3b3493aeb89cab67d59d3b8bfe0dd22e7", + "0xd2e32a5f43831b040d5a4b1f9eeb2ab3eef3e501c1348e546a667e12d727f486", + "0xca5ae13c159692a832964ebdda071bbbd1f3c8e1bc82bcfa3d2b5250c97a568d", + "0x2148ca5e18192a19a4044c54e63c09f0a79c85495bba33d4477070e8aedc49bb", + "0x740907df9fe7dcee8982e5714a49ea66299a0b76e0e8ce8d15000af7a00c142b", + "0xcb96528098a426f280ab09006ae2f4c3a20de34233ab2c89862da2b4ca14838b", + "0x7ac67a8f0112a7be7d8d9e29f5e06eb4ed2753b362a0c53c9f09cce1a089d674", + "0xfdc438579c8b311cf3c75afb7ba3767a2a3c352a31fee98aaae34934f12dfb79", + "0x08a6635b569f9bb0f3897b66acf6f6eacb6e0b41003bf423bd010896e5112e8d", + "0xe64d4468b3f349f85b713b4a95c072cbdc70040c071d2df22a118ed86773f670", + "0xf428f288ef695eeb18b45980683250913ff93faaf1c8462f9bbb59d9e6666037", + "0x77086c72b545018bc4a846fd8a884e8b9fb80e5c4b6f057ac83bb709382eb875", + "0xb348e661616abde18d2dfd73e52a50ffc02944e7ea15b2be7a988aa7ce030ee5", + "0x2dd28abafa20b28c141e1cb3f9ffa4f6d30e43c9fd701ccfbd701cba10691fca", + "0x426e88cd325a414bf6965febbd0e9d5b93405066f44402ee0a76831dc287f3b3", + "0x56e57e7dd9da901fbf347f21ae22f98a1a38a704f4a8333578b849c168d5dee0", + "0xf1ab6ebeb73cc09d12badb2f4b79a20a6c1e3bc7622bb331c8e18a4aeef8421a", + "0x1130b212358c8e46e8afd84e95686df508c50c42f2b280838e333b3e4c3371dc", + "0x150ec11faa3c01d86f02c9eeea0c719c031d0e51f1b5c293ad675a0a27d29c83", + "0x48026c4cb0dbc8ac0f1f2f359a9701b60efec3279491d80dc13801d24d553bc1", + "0xec3708defaba12552e534aae21bcd6aeac4de41d337981c09c1f26ab9569ee76", + "0x0d82f0d31b3b07b2e9fece976afb34fd926f552a4ffbc098cde0a0f9262bf515", + "0xbc52b09d4b62affd48a2200b44a25fb15304610d88f6c3841ecb9551192ee6e5", + "0xf91cd972586a07780cf448af4fc8f6ca4188d845cb5c1f5b4945539a5586bc38", + "0x2cb0540ac065edaa884e510c1ccc595431ba308e55ba831fb07478453613ec4d", + "0xbf31a62c7fc0c676af86eabdce2746cd0e8f64525962b2656a89390e84100472", + "0x8711faeebb0d0f3e62c88addcb79d8e1085fcefab6ede27004e308a13d932fc0", + "0x746fc9e29ebb582fd3f092fa8dc8ecf4cbf5ef4ecb5d02a5cbcd869983308951", + "0xdbb37616a88b0e7e2e44ceb15f363b044a515172da128b4f75e8ef23f8f559ed", + "0xbceef0d35d689278a9016de8f77bd756988b3d307733f765350a5af4d88418d3", + "0x509e05895e2d066b42a7a79b0fd6d76e7ee847ebbe87a17683b437d69ac80e39", + "0x497fade2076594813385315f7ef81c76437fc7ae452aa9aab105729fbc9d1bf8", + "0xb7fd3f44dd1de55b60068d083662497ac4a9b7bd3d3e6721ea6f6c5ab8979ab3", + "0x4d32e664aa27e0637a7f0833f94a7db1ffc0107df3a2d43a6d4dd65fe727dda1", + "0xa88ac6b3e2c5e7dd4184e717b1eeeeb3cc645a40814b840d4d216c57afdc7f36", + "0x4905a9b1e3219f14ccb13284def35bd42324147af7c72cf02f8bd1341a9e3e02", + "0x134ab05d8dd18daee83bd4ba54d55df754894b90a87f7ae62fa804cef68b8745", + "0x7f7d1be881e5603b81cdc6d516cdd4fc6ed0924cfab9c8b41ecdef30425a6630", + "0xb8d4e699aa22ce33729446fb35dd5d1142532d54ac37f8cb3821c0fef70992ea", + "0x0ae6f7c243978b313e7279841baf79ad2db95597315064c5f88ea33a85a7c447", + "0x3db15f95c3d59dfc5e81df487457841c1aaf58d310d4bf86e2a2b049c6bb9d6f", + "0x761873bf065390a4b6b739d386a1de17798a538d81491b1f6acf8f205c400e58", + "0x2c151966917695e95a2cb74fd7ed9febcf64c01378c8b1d3467981bab7a01451", + "0xa3de7af25021c7bc09cd18c8a1810904693278b8a03b582d4ee671a4ee386ff8", + "0x9344ab66d802cb5c13651700057fcd53e5f6464fa40a02b6851b9be1f5fd4f81", + "0x7cc2ef6cbfcaaa249fd68d7f4acf51b8e7fb7593b8ec86b7af5d8d6c1a2d3cdf", + "0x748b77aa485eca17c4dd5cbf7603e10229322dc73e2459928aa1f6d34f39418b", + "0x482afe0c06a77d7791194a45fff21ef053836153492e5f6b39b6f61446c8e747", + "0xabb23210ade663ab8926b9bdcd3045bb866f9344612fe9aaff2c885d04d8ce50", + "0x6d8de559a92650b6037b3efc19132a147373228f86cfc5d4f21d138947cd2f3f", + "0xbd56730bad652397965f15b572d11d4cb7d819246980fa834066ee324793e6bf", + "0x19803415ce92f1159b4a8b75c5620d53bc3559e0a94b1dc2f95cdb3a82d16af1", + "0x8e0e9abcc1b1261b933f9d8e26b574fe9e635346c1c8e99f033d6f8e4b144f14", + "0x33806f77a2942c582cfa76f776279bc923f932b42f92170fb2eb57cf7bf6d6a1", + "0x85ef1ea3f31a65d28931323346db3975b04c19f016ad41b16be1d98ba54500cc", + "0xed77ae9266a2125d47e853bba89448f2b95668087552aa20486459c0adaca8fe", + "0xfb9f532b28b0ad5147e87c7c22e2d7486c89a28a7ebee91f9ba6df5d1b8de746", + "0x2f46541a19415fda214e09545f5a25025ba32b5b1fee906ce701f0b6ce6126fd", + "0x8587f0e784aafd34f27b6a44b4a303de4841d66deaf29f4801c83479a2cbc119", + "0x42a7651639195a28480d2352cc3490917e6f307ba6c449bb9454a4d5c5f7e4a7", + "0x33e1fccd5fcd9f74285b35c72a73ac80f39c88c18a0bd304159e71f7cb003870", + "0x6e43ae2304efdc7caa7fbc27c2e31a59d3c314960aced7c96aeeaaca9d8e9593", + "0x84ef32ae6cade51a828a91234bc370c7de6d8959f1d6178372481c8804d3b492", + "0xd3fe96fb28b4a4d16ad09a8ab4e12381e506e62562b46893886923ba9ef9f8bc", + "0x02f10b3bb9fb8d8390caa5800399ea838cff7fc2da5c0da2889f3f9bd42fc69f", + "0xea2c8a0f3432ebd79b8302312e3cf22c82f85b2587bdbbbd6460f16c10c92346", + "0x5e3982cff13efa32f243ed0c25467d3c4a782e2c512501d711852e8fd36ed118", + "0x560cd5a87b4cea33d8eb38976e4db5ace584d2f72c0a663e193df9ce1e81da82", + "0x35b2117bf21c13677d3b8ae283f44505f75197dec289c97227b60e7f8dae9796", + "0x9cf0e0596f55e9aaf967a7b522aadcf97be5da567dac13f2faf72869c4a7819b", + "0xdc87c3027a07fe287dd8c3027e0008959d0f1b7c98d3dc3deea51572f733b0f1", + "0x979d24964e0fc9ca602f5a8e212ff45d082229624b6fbe9d3ef19cba4d6fa4a7", + "0x481fe7bd59c48e31df5b456369a67031b52977cf10b3bc0fb128e964a70c56ee", + "0x8f2e9646738f68ac73829ef5a092ea039e37de9c3ab890bcb2ce32124b407d6e", + "0x34d79f0ba147561da2b8cb9516ef4ee3e242e747a5c9a675a6d4c471d8e55cbc", + "0xbeaaac61ae2ce15d7cfa26febabe6e20c42529865ed62d127eb109d617b9c5e5", + "0xe6bd5d1990fa830caea0a7f237fdce1aa58946068983c37a834ba468cb9e3d6b", + "0xcaa10e42e1a9cdcb182ceaffcb1e88e0f7ddf7429def7a1219c74c89dc5ec4aa", + "0xd8c0e42da31988b4b7cd90369fc3fd2bdb25110cec27018f03c610925edffdc6", + "0x383ff05cf91d60142c9c3e64def96b357275bac8ae9acee7415e6f365666d3cb", + "0xb708ef8306f7c0e2ef026520d3c911c7cbd8f71a2d258321652705696163a765", + "0xe8398db71f93e3cb0560e9dac19a9c3020c7a40385814bf3f75483754e1fa5bb", + "0xdbc2cefa3d25164f4e66616df5a7cb70767a594529916ab66bd31b3455df843c", + "0xa3fcfbacb2f9d70365ef7d40db073a002f9812552f59fd13e2624f9dd6341d96", + "0x5f880edd5ce2bc5a5d51fc6ab33d48d422d337b54c4f8c213334c826d854629d", + "0x9c412d5156a0cd0c142eb6a258e695a2123ef7504338c7501ad2140c5a94c0f2", + "0xc17721a0209b982661f3670ea91715de0b334c46e089100c282e495ec2651acc", + "0x0fbb1a4095803b6f55848f0431af2d49f1ae9e1a3c3113be9cb0955d5101c844", + "0x61544e4a656d729d55be9ff9559bdff6cf96a2901ecc53adb4da4484ee112c95", + "0x1cacaf422560173ea8906d253cfa2a00514efa9939a4bb86f465f3b43017e2ad", + "0x22accda7d39ff453ff8c9b4da0527797e3681b696d84bed9daa10cb71d3b5452", + "0x45fa0de73a677e51f5e7d15be86a103060e6f36c60da9487a61ed103aea24173", + "0x76285d1edb55b42bb69842d9d1ed9a58b7ad56cd6e4fb204b9057f66c29c9803", + "0x562e9e5b3a52e3346203b47c72bf81481ce2fdd5643317568b3d9df16735bfca", + "0x818b2d6adf91916e5c0801326eca227e90548fcb1d590ae5c757f7c0c52849ce", + "0x77ec4f3c30681d4b018e17ae489dec9cd34c83c720f0787114c0dc1b01fed502", + "0x78c2f37247773af8448526f1f2e881c03f5dc1f802fdcfd71349f3b83f132532", + "0xbbb8cdcfcbdb185d18c0a91308785b2b6fa6d17a35bf6d99dc6e182e000dc998", + "0xad4ceeeaa428b2f432839ed80bcd5b219bcb231c3d0b2a3f647406137bc2ff41", + "0x06b0afb3998406f946d723d3b184cc0129c60b6f4049040b3bb81d757cefcff2", + "0x725faa534e54a0ce73331ee831dab9995469c8b203de4b3e633a1c5917458764", + "0xa01e626a6f157a6ca431d5018eae4925c563eee96472576f62a8275a51653559", + "0xc79673a14f63fe49df2518c5a00bd72667941ca67c571059f95f339b5012d55a", + "0xe3ab33b053c5c2e64675eb66f34ab48f34c8ab8df570438a9b6343458f15b1ae", + "0x693886cf0a2d7584a4c85648fd210f56567547ba0473a9666452d2f49e4dd4dc", + "0x7969a6f7df067036dde2958dd3b9df22efa98e41cc1bc658421df07657a816ff", + "0xe650e809e464350dae56d72f77af932b647fda4be3186a0ef1a78a93be8a9f25", + "0x26e90c16761f6fe7fa85769bdeec21ae78a3a1674454b12506f5e4bb42b52448", + "0x95b967964130549dc543df367742629437b8a6e6a1ce5c0bb678c6a6e9eb6ea7", + "0xa7214e05af5a49cac6b671ef82cc26f60a1b4350bf66c4faa26e1f7f17e6a4a7", + "0xd745f2f7e077e5e650705f074e52fa404ac926d2bc9793eecc08af4e2f46cb67", + "0xff683fcedb72930de4abb97c4508840de3d3ac60e9ba9915ba3aed246055992f", + "0xffa61f0ac0921f4e5c3b104041f150b97c8f656bf781ed2c814aff35da648bdc", + "0xdb7dea6f3f7600bf921f653a214560a266ff25c36f2090255b13dab136ba039e", + "0x2fa8032ede11635ad4f062180cdc66e632cb1fd13fdb7cd21e813f2694b5bbb1", + "0x2d4e76f58c2755f053a599e20052d508075fd801097a4c1918a9fbffb00733e3", + "0x31e609e0512a4006d363715e5a8c914c99adf3ba160d770adfb06cfd1ce4d116", + "0x19f77f831464027fb525a23852ca1c6fa3274bfc5372fc48faf6182499a2607f", + "0x5dd966e6f2fc1245a4210f725f2607d739717aab2ecf67e5c3fec5b96b61cf2a", + "0x5dcca4633da8bcb4168d10b4dc98500c3c8ca46f12578705355788d9f5c6f844", + "0x120afd9dd6adf372b782dfc9cf8839cf011018a28d1077234030e31ed6454402", + "0x4ab5008ef9f72957fdfcd5fac2d4b028caf038e7335d5d6d1058b8242a76ff5d", + "0xc86026842933fc8d3b74ca82304ce080dc88e0128c444042a698b8fe9913588b", + "0x02649cf32b76f8ce3de47b7caf774295d89dcfaddca861180b45f8d14335329f", + "0x24c62c2e3280135b6a8fc41078ecd3b0170736264d153abad21985efd8307ae1", + "0x2f9b30efac9ef79dfa2c48b69d9afe909c3eab259d84736a22fac1af6c61ef0d", + "0x62579171879e86933f26152b08354df40092df5a9ea627a88a2182fe58a3e491", + "0x9d4ea1bbf21a3027c5cc0046ad6309990ffbbd6dd6eac7a2778e8a57db55a7e7", + "0xbac8731c28a2ac4e9c41e4524eeae28d996b646780591f60387c947eaa3342d2", + "0x8083f3bd12ea7b916336ddbfb58e53c80c4f6260743a633104a425fd0942fc57", + "0x41cf2210a84a846d19579c9736d74a0207af9bcebc34e7bba224cc00d68a7416", + "0xbfad9f75099f8ec9ce61f80a905a98e4bf25cbbfa3256f2a77fcf782ac0d5813", + "0x5d9bd2ffa1be4363f4eab0a53e0fae0e79af09ec036ecd54c096cc72080e73bc", + "0x99cc0efb33b37004d0e40a15c5e5ca82a8949165f6d44524811529c6b2551a70", + "0x80b938899047bf3b67bb63f8908d31ece140bead7e8416c28a76eb7b38d68b83", + "0x5846002845d26fd6ab11bb98d757425f1ec1d5904a0f11f98a9b7d7383ae876a", + "0x56334210d876f399e8601d7a4e34e9cdbace6f536733f8a8480edb4ee90d978b", + "0x5fcbb761775f7b878c0cc71eb5b2a1dd9123e7b4f428dda94f249a9ef530dfda", + "0xfd522d1bee05e2a243368ef9af84c2a6e01513a49bf0ec5cd4ca64d833cd6f2c", + "0xee296a4fbba152870e003e88b9a69c9e00896c3edcc34ebfc346fb0a4e76fe1a", + "0x51ae00573125c8accbf262dbc5024052bee84df20010b5952bc940465f955181", + "0x63c8bdd2b1abe4a47470c3cabee3d22ee308f77c9f92897dd38a8ade8bb6f8e5", + "0x7dbe3352d14ed2b425244cb58a1d324a39c4bde86ce96bf72cf6ec07f712202f", + "0xf5a4591d71deb382a5790d5d2fc4d65a4ad0d8c4b91c6f7c336101a2a0a78243", + "0x7bd36da5bc8be1bbcb75c4a571ce3c687468a35731e8121e0a12a36b95ec3310", + "0xa8db831a72b9e5a589867d25dccd9c5442e0668b1603fba04928176e91555bf0", + "0xcab77b9cab9405918847a0c6b00e0cfac9efaf196f4e81d88792816d1f30148a", + "0x3afc7cd22646b7d0cf145dca0460523e349face6ab2ff5e2c59489c582ffc18b", + "0x43ea9166fe16bfa2c6c7f6fb2a5e3ed8cc46bdd40479578a56fccddd3f1c8dbc", + "0x48875278d371ed62685136fc755332f37e1eaacf16224026b29f84ec0f71d9a9", + "0xbf174a491151dbbf42123903b52a76f43a987da04d18625b8ac8aa6d5344e9eb", + "0xb600fb15b9b2225680208dbafb308ab5630c18ed1796bdd4dcb6c7fd4272fa76", + "0x6bd077c5a9a7630cb68ecbe5a43dba8e0adbc4962fcd9060381d74e429f95565", + "0xf355e2edf0896371f884414faa942d1525bbb70ab6b1df4a6b698ca2be5af2dd", + "0x3de2104752be3ffdd2f563bf6018a1053e18506356774240591308de197832fb", + "0x6a97c6428bfa15c7778c229c2fbdc660f7fa4bf250807a31547a19083fef168a", + "0xcab19446edb5599c38c86b01b6ec613d55e7d039480f9ac03db41ad5eacf8cd8", + "0xbf586bd3d8e7cbfbb80d0f8c6265f6c9686b3cd30fdfa9b295dba6956324321c", + "0x8261faedfa1b27e6125e0c0baa8484d98aa70339fd8c94c3ec9c052b43aacb87", + "0xf78362c207e0a33e45e6bd086120f92465e81adba81b6fb99e29cf35f0d570fe", + "0x380e7016749638e40c1d549063e6f5d9d2f5a9348064f3539834bd36144fc33c", + "0x42fac7c40529cce4a8c610af7f0e48df260c88c20aa29da6fdaac15890428c92", + "0x5e618774c33aa72105ce955dfdf9aa45a6ecdd6d42ebbba234924b86bc662c90", + "0xe67c3ff57affb776ee01c0af350655bffcf01b54a2038a3a25e0919d74174601", + "0xf9ac7d64780d4b487d384e7a622c2c38251efeb16c0c27596030c61b9e80247d", + "0x24f98dc4072db07fceda8936f3ce01da92d532e1c700bdd8da4ba5e1e930c32b", + "0x5f38b171dc51c481936f13d0e16cd463c34b7743c7987dd2fdee2c0f021f5e2c", + "0x97acf58a4a1d5867fb719705da45f27c200cc2e41c42c8027136ade2678d3106", + "0xc43ddeeef7ddef067aefc9542bfa0b40f477805d2ed1da8fa307552fd6fd18a2", + "0x7eb419d42559a9eca2559e15d6a9d28b1224ce078a68a9650e5abab21d402cf1", + "0xd62b2c225fa17388436b42c329ee420046f8357cef93d7faca5724a8fbb96623", + "0xfa5442c2807d4cc83d7bf4df3d57a5cf2ad8e58d0cd59ae3147ef733e4037cfa", + "0x4320d49ffe12d61befaf9e250f7e85e8674f6763c0cd464c71cb6ba7188c0579", + "0xa13c5d8a599dcc4a7cee545e56a74c1d1670f3ad2fdd711fe38387eb8d37b7de", + "0x031758495fdc1ead5d89ae4c65c50f428e2cec239499c5c157d268eeafc2e1c6", + "0x5d3b64ea4805d8e202ce0278e3dc639d1064a8d23b001039018db174e9c40f1f", + "0x425174640faa1bfa392ae535d726ab8360ac4a4ca90c6a72d71e888f7674fd1b", + "0xd922fea22d2f83a791942cdc16a87bc6d44d3616145adf775acff7b3f87b7903", + "0xb4f59391e8f3291db1bc21aef98c134f739cc88d3d1af89416455c58e788fa1d", + "0x6851456ab82e3151d6fc47c0b51c2d83915703e99acc779f918a6c946bfdea66", + "0xc9e336e24e2ec5dda5e91530013c69547e2b8c04585628b94e447cbaf3ed1d4f", + "0x104b59c8b6ba4382144fdd35881ca618cbd0de0b694bc62bb9e74ee2faf022af", + "0x2fa7dee77a31974ddcb3feb9cf66f64e6fda8ae00121e7c534b02c5ba46f5750", + "0x30ac563e99b2655db7d8398b7dcb2c240a498fd4e24e7fd28d6dda19739a41b9", + "0x1ef864c2a0f2d15ad23418fd81ebc78b841fd257e7cb92a9726cf8ddfc67ebd8", + "0x7128f64822ef5025f3f70a52e4de6aae3d21074783819606e43d965bbea71962", + "0x35085202403bc307467c929f61875ca613caeb8ffe32a94af0c774cd3091848e", + "0x00011d899cac6951e50d28dd49c2d93f0bc15b0d00a0440c8f0866304e89e4bc", + "0x3217e69db50b9ca9fc2e6f01341d56f39f91b72561a0cb2cdd5a8e0d3008a565", + "0x1f72d7e398b789e5fb8197b193ad1bac3ed6f5032cbb64e4e7594ad58e09a9ca", + "0xbb75ccbd52a4a52c03433e8468d7b88e220576a86f091f553ba4356983a638ba", + "0xaef3df703e90ab59108449c2430ab99d42e328629411b0258064235165c49e13", + "0xdf37cb75c00e797a3182c4d3614e29647c0de54a7be473c1db67ecd0034c6bf1", + "0xab523b936f5d03e5c38bcd2d16d109f2742d3ae6074d86c84f5897fce88eceae", + "0x9e3246f503887b1e4fce6bf9d98a44b597c788d457d72931d74987db5dd5cc46", + "0x62219ca3bd086ba233e3ba21c0d931df9aadf78e31f0265fc2ea2ba86092bfde", + "0x028405cea94f9cf88883131f1f4b89de661d7afca03b03a2fda8337cbb1877ac", + "0x70d827cdc8260fb4f9af48a7430cb9364faf8a8238d867fc668c1b90b99f3ff3", + "0xb700f4badb6d0a90da75b8855ec3b9a1dd48997d9f66b12423b277de3bf762da", + "0x1d5da7daabbec1bad87869e938599f94712d702e6c0619de71f759dc853955e7", + "0x3e276f02fb3a89c5cf5deb81ad3c09d2d6e09a7848b0dd135ccadfbea863a3f5", + "0x92f58adefb107a1ffd33c7049f7399e8bd4148d21a41ad9fed181f8476458608", + "0x78eec2f7e7d64fb42015a52a9659af33d5a73a2bb61cbbf2e24f976cd156ae05", + "0x24edf024b368f06d360b4f90fd97a1a538fd1e3f8be9eb4fe68ffa15ee6ccf35", + "0xbc3ef33b2d162f16e6ec0c3800581a8f0472700f5944e10f913bf62b4f30b213", + "0x851cc39d4717ead9b3097a34d8921c158df13016f850c06f355a37d7f9916375", + "0xdf4573e1b0a249f1b378f5b90c28229b72fafb7393470c9663eae2e6337ccced", + "0x798d1afc8d307220c9ec1d8cefd16a95b36d88007669678d150226be04ee0dcd", + "0x33b3dd2fbe3664050f0a8962ac1ca5bb3c8cb62de518b1b27423d11c3e8dfaa5", + "0x55546dfe14409611431d05cc1d56b54a8e2a5c82a9dd2bd18819481b04917012", + "0x75e99f80270232914aeef5aa03560ff0f1e71226fddda7502e23303ebe748088", + "0x2f4bdcb8d68ad6ae7c6ca92ac2833d955c8802b747cad7bbb24c1048a2cf5095", + "0x7eff947ca0c04750124cca501a67c4c9df604f23ca1b352bc87dd4e31e8312c1", + "0xa02c21ce435b7450a5164113b39d5260a2cac92bcb94467c3249538612ac4f1c", + "0x2c7ceaeebab66fd0fcd7e18b06ca22819940706a4ba2d978513fdb0dfe5c8294", + "0xc242579dc567b35e076766ace85ef09cdeaa3979cb761835aaa9e8022252af89", + "0x80050b33dff018270c6ca9aec689c9489e3b848b1e4f240d0b9cfcb9d170cb9c", + "0xc81cf93d5b5f4c0dccb5eaae7354d3b4fe0ba5eedab9fe7c6d4a85040ba24290", + "0x729de61cef0fca1f2f19a03be2e391e37f7d837dccd3eb773f43ef2db7337c5c", + "0xb573410cc67d500f3feedd221d1219151029b2f18745f8fdba911905edb8ce7a", + "0xad10e8b9f4d6aa16334f148ebff803a9bfcab9bd70a75c30e848f5c659e08dc9", + "0x1de2ec2259738cd7cb79cf85dbfa82e3d1aa6db13447160e5769c5b7fd87469f", + "0x0e7c0087c4669e7a2ad404ecd8c8f1cc45b830142f3611b9a34a08898adc4c0c", + "0xe051b7c698ef13ba983212747a54c67a3b8165f35d7b7054d7c752066787fe13", + "0x6db9bd03b9f5f9dea82405e4b43f7f83c25f363869eafefee6c2c53009995a80", + "0x719341e21b5ecdddacd0317139004b83fb8103bee1dce0a6e50bf3422ddd063e", + "0x7d326d238aa5221c33085d11a0cc803afe3c4f99d34ae2c7a32b0aae7d1fbd89", + "0x4727555f93ef3d3d6e7d8807b14959c69bea6630a83febe27ea5fa77aaf546c1", + "0x3fd84a3f89f6b8330e3ea0caa52c042fd702d1a0244671cc607ba3d2e2f71617", + "0x08a097060c462361993372bf43ab2d43125b0c232ec211de5eba4e095184a91d", + "0x0a0e2fe6c8d52d76abed9864696eec3000b5059a957a1fd44815aa8e8e8fe9da", + "0xd3f2a39977e71b4b9bb324a1338171d19ae5f175f060aa2f1549462f0a030106", + "0xea79cfe4382e204ca8f93efab99b5859e74368949b1700ca05dffea0afb2967a", + "0xcc1fb4ba391f520d7af3994d9fedc05641d7e7e85ed0093379cf1e4b3fb375a7", + "0x59f3e50c53a94b5d4f43b1010cb41b21ff65aa7704f487ae35d8c874ea36d0a8", + "0x1ca6de364f824d99434d8e1914ff01c7caddb0fd9d1ad3499c9c35e46d5ce651", + "0xa2795754898d77e9cff2ea15ffe33f9346b59c8c02960b23b8ff23ea79b8577b", + "0xe0f7b5e591c744f0e3c0c851cc6c204b56efd06eac37237287e404b12e33f9ed", + "0xc424ca4ead09fd81e7a11d629428e42d872c2bf5da627ea394e7fda5a4ba7ec7", + "0x434e7ef0a8eef23ec17f6cd2a5a03efde7732761c7d6bbdf963126d20c683e26", + "0x7f84864f6dcf8cbe2ea087d4c78b23a194627f259a6de42a2ced2a6d9a1bb508", + "0xaf74a6e1ac80c932facecd777025d8762b578c869745aa0ba78be17dd1fb5be3", + "0x6389bd890ffcb9e3f30211891ca08ddf75493174d8667dd0d71708afa778e76e", + "0x2039ae01ba76b1eab6e92646cd36e6250dc12f7941a5e365c17da6f4c24ef1a1", + "0x58c3e7b9404895bd6a0f5fc3425109d7a341b4fc08bd1d233417188f284d38c7", + "0x58c3e7b9404895bd6a0f5fc3425109d7a341b4fc08bd1d233417188f284d38c7", + "0x931e399d6dfaf295c451d45f0321154c612f41f0b0b00f622fe61dcd1a7ce3f0", + "0x45ef5e1d91c504768f58625c7ff61d55d1982febbb9763f87192c1f6b823b81c", + "0x224d2fefc8b00b2ac5dd58d78637d715de28941c9a44b9505065fc0c60b9a27a", + "0xc63629627efa6b2f7fc31e5a726652d21a30886cea3d5cca3c1ae8f6989d7bcf", + "0x025c2c203fde991824ab661f80f399cc135a84cab455ec55a79c49a24bbc4551", + "0x7892b4bca70b1a9b5d54b328eb27be8341df91f0b445b541227f857ed1c4351e", + "0xb0011333b2a9bbda9ef0460c1d6344849883f79d37597f06cb3caee42355bee9", + "0xb63702cbca74de4bb7fc10d80cecfc6f3e5fd4b27fc21c46a9791d78553dc009", + "0xbd90a53809dd2b27a2f088aa87d9b68ba13308251890ed15432593265d42e5e6", + "0x172265f9fbb3d962a3dd61d357eedc5a306fa3d333173e6358ea3b113e2377d0", + "0x24519b01bf05b6bd74be58ec706acb5c10a87555fc12a9244cfa5579a9516ccf", + "0x843269fb7a8b4d49dfe19a0e309d31bf3ab507d1cc887f40d2a35a57dbe90986", + "0x553293c5afc8a6d5fa6d75f8c6979188364f3715ac88cf12c4c5ef98b33d539b", + "0x93230dd0e187fb4249689a65f2681863a3f486f4f2df72da77eb9f3df34b5b39", + "0xafc6d8a7a5f965f1c153ff2f9c8fa47838c43bc5e7a7ed05461870da10deccec", + "0x3321cd7eea22114db4300c23ea8c183175763307bdbc8446dbba56588c029090", + "0xb057623cbc0b2b179d96cfbf272de2f17f9e9c21bc713a8e7e589f873068def1", + "0x7e44520bbeb94c372fbcbb75f7d17178fbd3d5e9be16736f7142637b31a2cbe1", + "0x488a15feae3e8d97ee2f8d6e5fa063e363f8a466df825e074a0af330fecc7edf", + "0xeedde5e06a6078a554525e3bb39a0d55e01e5af7833e5c7d95b275373904f29b", + "0xf86d9b618276289bdc64997ed6b317e7a9224f934044a125656892fde4d5a053", + "0xc487954adfd0595115004b2c989d1315ed0976fe756cca8ecbd9db19af9a8668", + "0x88f2b1a5616fcf46461477a6873c8c47594dcf3cbd9def47eb5ba7fa7a180bb0", + "0xc33f0a8a39aeeee90be2520825b35e96a6724e57d721dc111ade93d19fabddd9", + "0x7794d98893ef719c6a39dfbdb7c6e01f5466f7931e255e464eeec4c139a8b884", + "0x280975f99852a9e9048ac6b8dcecdd91bf36177a4a3e8b2f01465ed608425d0d", + "0x56c10bf00e861ab83d738d270a0cd9de6ab22cbc40d504530fff9d489b754676", + "0xb3d6e7737a82bfa35ed13f1b88c9e2c635a74f56c2ffa70cf1b32fb0aed4791d", + "0x1ade336ef28be22c9db5226d42c8667c30c8abd0e0d99ebed0cc78dd2fd92492", + "0x68e93d96bff03a30eca7865904a68ac99f15c60f3b8f44cd6e8cb48c286bed48", + "0x36f4784b0856babb4adc43165f17bbde9a0d027e2304b18baa86ccb9d442132c", + "0x46b07d7ca433656d5f897580b6bddef5b061092b8559125f19f71ccb65cc7561", + "0x81aa00c8fa8ec0bc80c4580f4e5edc47b9064037ec42fe5f3c0e37dd774120c4", + "0x9a8877b541526a4b23a3e21cc9ef72d787aa0f9368836b2d071d331e37526baa", + "0x7e8c532b99cb4963db7c08dc26f01dfd067913b5ed71e5e6c7fd941974cc2439", + "0x7ddb9626c275051b2bab36c849b1c38a035363f010589754aa15fec769ec52e0", + "0x3807b41ac6e96f451eca372dbd527ee1ec67e11296b767d0f5aea8813ce9be1d", + "0x6822f5c2e344705b5ed6dae3c7a645c60517e2a4bdcbadd894c91d9b12d65ac8", + "0xeae2b27844892490359ec1700078a156fcb79f726f0654a44dbbeb2e6143e5cf", + "0xb0107a99b1d6c3c04a8528db2d029cb795b4476b9f19ba03f1b87d65016b9c02", + "0xc8b9447a9866ed533d9a4a6d0224cb4bdcecf3819f477eac374ac0d31bff689d", + "0x613d2c5c95d939b466c5348d2e7c17f3002c9568eef5576c0204103d7576c940", + "0x8bcd0a259150bcae8b3bd98268616d767dafd627b38a92950cdbcfea219ca628", + "0xfe4fb8c2057d4c91f0c3200033df5eac1470bc323a761551899b6bf4bb18166d", + "0xadb384d40db0dc9fd19ecd337c5bc68573a9a8ec3667a1e82302746677e44744", + "0x6b396845e8ec6028b7f7148e34d0984fa729a03f6cf0d200f7bf222cf3225466", + "0x68a39e11b281f9822a088c398cea3d63a3f0198071c6d8b01ace2a7715509784", + "0x37a462c511bcd7f33ddf3b4f2afeb2dbf8e2a0bf5836e5cd2d3c580262532d1f", + "0x24caae6932388122ae2bd3f17eb94bbf842a73da6226600eda13efa1a800d105", + "0x1385090b9f581873b75927e690664b1ff4a2e35da30d37f6fbe4de9e453add94", + "0x44f3b765cae07cebd073f629d2310d49ac9cc5a00af1e5cdf8c6b7c57377d3d1", + "0x61ada4cd1662e490de57677bf500294326684f44187578ba4b0981b144df2147", + "0x8b03fbd66b59caeab4a60e6fb394708d53953bb7ec4abbe0fce0796939823ab7", + "0x4d00f1a72b89b011ec41621621194bf94f7d4e95649a520931eb3bc2ee39f960", + "0x0034c5b8a7a84704b4edd54962026d07c1a4ef894d7586c051316e6e2ebc35a9", + "0xba99ba290696caa57e65d33b09f5a4a2dabdcb177f5579d3fb93fa1044130b15", + "0x4a4d533327e7e1849585d9236726b63007c70f71699f66b100334858ca4557af", + "0x3d85009b7b3c80d3f500f91cd34a74c2e6fca6d9ffaba333c782ece544af5e26", + "0x651f60bbc228bfb4b7e9d7c94897df1b73ac692b47a4e75f7315b49c2ed9aeec", + "0x6d7ed3bc53a217dd0b9bbd59fd18ddd02119240153716806b613a4c3aacd9b49", + "0x9f1040b1b81a7169e05e7548a8c8c5ec9b18ac0e60980d3deb2b8e7817020643", + "0xab0718ef76f4f8715e40c2fc3751d65ee102a54dbfc84568f64a7bd754a89122", + "0xda3af99f9570ab917aaad38b88087ffbe677cd5a39514f5a3aae371bf3a92534", + "0x1bb5b4b7b061c3e965126668371978067b4c26697d1633c58bd4096d6b7b8cb1", + "0x151dc7f3055b4ab76a791871d3477b9f80bc0bc6338a0552d27fe5fed0979044", + "0xfcfb55fef1e52c7573a3c3913d2d1eaa4ab3cce8c6d1115e3055ae88d9d13684", + "0xdf8381b774dd7b09ffa8d00469bc2ab20a0db3e9b671ef34a0530436769f6678", + "0x55ef11fae789004a615ac08e424f49f191a27d6b33591af0408caf4692ac83bb", + "0x5a53b3b9f0c607f007d796aa8e8572684254a77883b5dec18729a01747c59a10", + "0x00e8d426cb237fb0301a4596931f50b0878f75365d07698e75cfaf5ed855e4e2", + "0x2970f2719ac52afdb0a4518c3e81711ab6b1f29093d2b17e90ce17e1b50061aa", + "0xa0381703bbb84c411b42f241e793f59af225a90d64b2a972ed2f57c7aa41b1ae", + "0x74cca7a4e756b60fa44106335adf02487ed26c40bc3c1d17da18f7b6f566fcef", + "0xf3dcde1d0cd162e51b7e5513a64b148ebd0c5d1889970f88b3a1206d087f5670", + "0x4e2ea129ce85b25fd7d0c4ba68d4d5f5d82a450f975ae78f223b2e62b67cbdd1", + "0x18cba9f78dc3f452dc61d3c6de437ad3d13bd2990b86aa72cee2aeb267aeb702", + "0x497b4613a0834c737ead720d124d1afc2a3c9f4c0c261d1fecd18a25813b70f7", + "0x6cb05a7030b097e660d60118a15278179cb8cfba465274f0e039b79084c181a9", + "0x752142060d465352338bc57c5e06ed23dd5765ad693e53f8604d6ebf6e2a6eef", + "0xd4f3ed7c0ca61ef82b165765d6a75043ca850f5d02e98a08508612b0856dfe1f", + "0xcfd5cb2c6ff3c755c4ddfce1c1fd358f8368361946914409f863f988bfc7f1d5", + "0xba6e390a3e4796bc3fd1965955e57587c3dcb93409e920fda2f328935a569155", + "0x60ab94442e3a9a7b8a0409ef42228406cd9fedebf11db70b68de94c81a6367d9", + "0x9886457bca268851fa852077bc2455ac097658a6f1478954b0e4f31f58172e00", + "0xed4a9e2003c93e24cab465a2aa8c43daee4b25ffe8940214261909f9cd093cbe", + "0x0648fe422cf4c8cd3b3d5d31718ff0390896d275699fd2d5c3fb1b94559dc64b", + "0xa33b859fe065519236b49b53000f0c120679156a557859c9b2ea279aa3cfe634", + "0xb2dfe0a5db90ec85a51b93d76d4a13150b76ee0247d21cc3265d535dbd430753", + "0x6b2c4d951b579bfdf8a30445bb775244aa89d554ac5dee68903ba452e89b79b9", + "0x6ccce11af2a2a6c11021444e420e1a43638480f64b481e61747ae8874aa30ad5", + "0x706acf9f17f386835bb4abb3ffe229ac6a6c9cdd25f8533f2dc39ef023f79400", + "0x8bc684d178f6dd2ae3da4dd9705fc50d19308a0ebded674f6e1c3afb8bedc06c", + "0xe1695d446a4d33a8192905c0c7f5c834f444f9d8ac8b8bea19c04a221483c028", + "0x3bc5b9d59c9b89e9d6ce99e59679d16f7af291ba42ff4ec2c098558175a4d5c7", + "0xcb68699405ae0d31376c76cbb76e885e03687af70dc6f9d5e860cb9a1aaba82f", + "0xcd26c438ba78c2b9f61cfdfec607312ec511a7428fd32bf777a9cb2a600430ba", + "0xb868e783c35e138d01d8338f951b76c1b75de794cb8844354a8f667d0fe64c7e", + "0x7a1d7be1d3586f13f84b3152103dbd56045b6d8a47c64dc91e4597f037852111", + "0x596c746a3cd527be01249d03aded963521fac55d9d1df0c083d7f7f4485b3718", + "0xf8f38fd29b46f40f21f241e6a726219c6ae07f0011ccc68c43f0441740dc7cfe", + "0x09a74aa3f15288130d9dcc768e4e0438b7020cf47ca0fad3bd4517a541b4fcb6", + "0x51d390b2aba323d15148eb8e6cd88d6c1506f0b36d5aaa74e7b470c0bdd5cff1", + "0xbf54ba075a58f1946a1ab2f382c93af477e5a37c0b9f6861361b8e47c454a532", + "0x569ae274dd911d0a06b720419dea2d659581915c3fc310d22940d72c094ccde9", + "0xe686f17fd1261a4e80ce9c702c0b0dc9f4cd6f51f6643ce44987530333d3a667", + "0x05d4c6e48f4d980587d409e9c2f5e3632eca3540c999943af0bcabb8526d87c2", + "0xef1addea522eccb48efea075276b1ba6f2a71b3ff55d58a5a4dfd679691bab7a", + "0x7473b6d6f291d7438253e4e00272940033b0a46e9d722009c9799a2af8fb26de", + "0xcf3cd7782387bb1f87139806c9b2cbab6473ce4e157bcaa7734e3fb002aa5b27", + "0x6545c446faca55b3e987a8caafb3680a4dd3b1956102a9fcad9a4fb07b2f5eaf", + "0x955436f77101763648d986d8b1c0742c2e71363c429f7b1761442869f9cb3199", + "0x8a12b1d9b7fac3943247cbb5efc2ee4d7b87f0a495e2df7da10f098f6beddfcd", + "0x215309a7a6ad52001e56ab4e796b451af854ec6a416cbc1f58defca91c09e1c6", + "0xf2d13df80abe632ce64d5b515b7fd9383fbc4b9bcd20d7ee101bf42adf97b111", + "0x43a05f90d53195f38f463f5fae01893a29dfa3fa89dbf79e1f17674fc856be5f", + "0x24403a7a6af93e8c21d6850b95f065d086da86e62fd87ab829108f4dd1d871da", + "0xefbc00071d0136fe8597ba4eaa7ac7e57ab1abb9cb603b3aa697b875b4535e3d", + "0xcb3f78c31aec036461f94795a447c10849dd829cc984374e37c47c0095db4aa7", + "0x3b412a5129545240cf25c4fa87fabee471bf8863414e6b76b6e16b651fe9b8c5", + "0x23c8554f6cf7b6a54b41cf969eb28356ff4f92dc45f42c2f4b99b67e924e2d7c", + "0x0ac10b4bd5f0fd456b6ba9b5f94d462b5a6783128181c56db29335af35ce1923", + "0x872f60c33b96552c98924e23f74eaf1a5c959e8933f27246013549fb8dff9f96", + "0xc649675d9243f72bd40a4bf3c2bfaea69b262f27d21a0152eef0ce10f78ff501", + "0xd62b9829f38a8b775a782bb6ccb2eeb0b3bcaa9dc514750228b70a69f7b1d6e0", + "0x16258f043d9b721cac5fe5be04e2db3965689bd13f123132cc1635b0518b11dc", + "0x5800d4dbbdb6833767be2d7d41d584667b9c81a84f3b539471029db2571a9721", + "0x32358a76ac17266e73f0ccc317985c835b4b542adc7331d9679b01562dbae541", + "0xd42e002e36d99bf9758011c4d0041aa709dc8ed4fdf2213ee47649bd7cd8b365", + "0x25d096f8fd934b268d6979ac831b59b8f685a42f9485b97778f4be3a0f7dd3c1", + "0x046865f4c221a35ea54e4ab214da69d3643d8c91ca00ce06bdc2dfa14e3a34b2", + "0xe6dec85b1ed32797df9e9d726a8929b9153bd087ffcf8fefe6e549293c67c217", + "0x807d0a25951a3a78eb9677dae40e2f906ceaeb3efbccd8f18c8fa76f8dee9644", + "0xb9455cefbf738958298ccb9c520d8b8b5209b8276c27a5eacebcb349646c6f10", + "0x7857c32c74dee423a2230a6da11c5e3201016976c0ea48bebdace39ac89e4757", + "0xc222c40a7f8f66bad9c0f04b4936b2c316dbceb85e19bb3ef9daf0fe42c21384", + "0x80b0e8f3d9f92c0967cf29102fb03bdf19deba3ad5a00356d4b7d3933cf3b54f", + "0xb5f1b6d05dc8f8607ae336f428e9472967a5a42fbfe2f5ef4fc0432dd2135125", + "0x66012e95de354f5503dd0d080996a24014c9b62edad2e645f9b8b4b225defba2", + "0xbd28be9e86ec1b669ae7ea09e6bb7cbe2dfc243439b8399ca04d49064fb3d89f", + "0x7b4ad16c20f941d6d8864f46d2f3217f095becb7ea00d9327281659e94036289", + "0x1e0d6b6a8ec964fda8a7212af7c11cdec3300d1b59901bcba1196f6ce8fed1a9", + "0x14b69881b465c532346db780854037845b5a0ea50e6a21aaf9f7062e3af74f02", + "0xfee1acb42dfbaad5ffd5e27e760dbbd8caaf9aa200962abb4ceda8f30dd378e2", + "0x20c8e93b0f17f8c3a0c3faa89054870654143e3a352bf23fa5f2e73f0c44574b", + "0xdc8770ff742e2098c5672a3c396b4809d33481da0025e362aae7e7c2a73be9ef", + "0xc2ac3814960b84b2db1c8b671adb38a09f71d8eb89d73a4eb39aa902939ba38b", + "0xd137e5b58157b10c4fd5984a946ebe6e2f1f34f353d002c5a7abacc7b94fb4d4", + "0x8baf7a1d207932059a9b99932a684c2ae0d032cbc140fe2cd17a062f1cdb72e1", + "0x06abeccec0a85eea82d1d8fb3778ca4200757698439e02b030ab14698c2b4567", + "0xd4fddc6763747dc333930d80c042d9a47ac1550c8797c9dca0e72338e06d0b9b", + "0x74317bd3489b125560996ab4a43b8d50dd698ae832eea32ddbdd5e568b09623a", + "0x6db951e6576bd3fc85bd5b571ee7ee49db0324455ce4d296035a24101bab7e77", + "0xd90473bbfde91757c9c36e2febaab079ac1cd071eb434d1e318c603d5cf66307", + "0xfbfc81eb49e1b2dfc456f2b5ad676ea7fe81c217a682931d001408028e1517cc", + "0x629c7f793abc2325024d7b4d19b7e97cd9bea817e8edc0c41a077935dafcc121", + "0x6aae9085080705853be08191557416dea7bddcdb77df5bf08fcad6f1959a2a3c", + "0xc3425eff1036366ba8589d3c593e92942e921cbba4d04e00f7bb8c2cc9de8f6f", + "0xd2b9e5c730ccec73122692db41ab52e5bf4bed020e367b07e65427c88e0df9a0", + "0x58b798ecd76cd0820ca6e4def07a588c40c5a5852eb24514365629ddd5d11674", + "0x675f60d838dc57a3416fbe5318013ce52303da73e3a742b7f88dbde5856991df", + "0x2b0fc8bdb93e27de386077fee86b5c5daec95e2d4a9476812b66e10cb4488801", + "0xd00b9094253a7d4d967ebb23643c094ade8c77cb388b6d472ce4631e0708ad1c", + "0xe372b37df9950fb05b0ce7646d4a81ddee8460d781ab2d99b1a4167ae55e45ba", + "0xcec6e150b43183c85a3165e055a7b011cbf947d278dddc6e4a59e5f7568a77a6", + "0x4abe282e2f745b85330731144582896111c4f585e325a492dd3fd02bc974916c", + "0xf3924e790d9c89201854f391c1fc84c1a80ac6a5794c80f4192500f972cc44d4", + "0x8020b5fd5ebfac0aa1eb80bd92da63ce380d4c1e677bcb7e0104479d09791999", + "0x24a9f02f98185584f2e74526395df05210b3d14b3850435898fceb5ceb07fb91", + "0x2a7394ab40a842575d5dcf17edfa780ba14be4180b909523528358dacc1d3cee", + "0xd42c7f24a1afb570841f44da508f326110ecf3508f21c1975a393e0c37ec42da", + "0x54befabbf7e4b7999213e240708877807d9502991a787fdc730a6c4a05d6a88a", + "0x856831808e2d06665d98d86200f35fbed9a33193cd8b94af06f008a3d01ccffb", + "0xd44c6e7cd8154c9c613cf23e670dfb2367cabddb5ad59acd42c0a23778f53d90", + "0xd1e238e99ec6dda2efd47fa8bef1f8ee3493cfc80626abdd18610665908ef64e", + "0x4bff84bc43ab589f8b134d3b97b29354a35460b2603d6830aef2546fb78bb9e8", + "0x23ed81264ba45d40396b2f3481238b6f922fe2e4bb737978aa5b34dee5c3591d", + "0x625c57b1debc997c78147e36f29e25d0c225c4b11d5775cadceda75f0fb0c768", + "0x5896521f928b4d932abd49e71d489e19cd960457afb259f592e4b686b546571f", + "0x3e79f2186dc30f62ad7ebaa625bc53e726ba00c7757c7e015b368b2d2592543e", + "0xb1d2226d395a1e29da9b17002fec1517acef80b77e1a6ea778f53cce91de9bee", + "0xecc767e007fd1760cba994831a8a7652a29971e53c05eeaee2bc764194ef6830", + "0x47fd4a33d7dfc14b6d278f399314844de982b6bd90fb962a07e83a026c8e3d5e", + "0x140ded220b4eb1b708a6d71297b4b35e2825244b292207236034a19cda82b07c", + "0xdf115dd221fc697b03cfa00a3ff3ec9862c51c9b651cbbaa67e527dee7c97fdc", + "0x92ac5b711e52e50c4ea906ee3d131c85f92aafc4567d9b55d08eb5f2194add01", + "0x0cab1eebb38e003705cf7b3797b596b91af3bdfd13f132d6a4135495e32d41a6", + "0x65185e6cbfc0a18fc5af1759f50114a8f67b49624ab16956cd224e3fc394307d", + "0x7f4e8928c4acac68081064e47dbd15f13989eb5a7697379f8cfe57985175dc5b", + "0x2ddd9408679a62058790956c3047ae5e80d609ad42692fa51321a8265c582065", + "0x618cc77131a80a569f7659ff8fa197f03d99fa621b91610dfeb7f3f2e7445980", + "0xb7fc4b6aa52ff532af018f69c2ed70aa2b674505f690379afbf7bcf1d1175fd5", + "0x3eedb30929d00d519bbc5a39ef88b7083304ae0cd9e75e2a41f137edc967f81c", + "0xa7352dcf35e089f18589f536f8243efd30a35b35c40155a027eb75705b7ea26c", + "0x5c58ffd9936fbc4c7d19e3984b1f0d7e10272788d49e60c0ef5e190fe242cfd8", + "0x85930b70014c982e077e48ddb130562d11729162d209499316bef6f5dd73a562", + "0x3d8ddc9f8edfad27582c335cc6b0836b72fa06c2cc9e338af9de85b78bffe117", + "0x7a1b84272a5bd2bf5377fed364b106035b107894cef356719a2f6aa9e788d3c0", + "0x18402c853dd0c4db0dc6a2a078c41e1a367b13a5ecab90e08080a0ea4d95ad68", + "0xa72f3e5e56b8c060bd1830f6c061137937e22da2eee40306823b14c1f70d39a4", + "0x7424589cf617a0c781455e727aa238cc4116198afa5788c04ccbd93058e176bb", + "0xe7ad0c7268f7ae5d6128f41ececb5f75087e2cf5f64c659e278c40c570341f6a", + "0xf13d0ed7e4deaf96e303274b33394d9936b1c7e164127339bbe5d8bc95b3ba16", + "0xac29cad130f77a55f5e602b80d6b63c8a093146d3bca04dde5fbc52917dcbe69", + "0x5a5fef37afa4dfebfde521650e9b088790c15f3a82f210f57edcf6e610b1acc6", + "0x1fdbed1c3134537bea84ed4ee7127146324d08326d9c7d214dff90727ac9e0a6", + "0x9ab8a8cbbd2f49ca7673565e14b8e6e84f28af9a7eaa92e3e18b05635941639d", + "0x6e68e74b32bd83a6782873cddf2b9ef57a94846ba07a65321b8aa644c349f1bc", + "0x62470cd587ae310444a97191d2d5c35275bcedc01571c361c149bb775b6d1d1c", + "0x61ad6367178062d78b814eaa0ca2019308ec4cd2e704f5fceb4ca895ab64e9a7", + "0xef2ac07d359e6dc7fe8a1f4ade82f8f0b03127ed6018ddf99c25724fb7a2c5da", + "0xc7aac81f1ae1ec6f8d008f837f546ddb803d46bca894d001052581a3c0c23531", + "0x60ffcb566a91fa736b00a9bb99d64aa7fb8a8fabbc82bf08d52164d68985c721", + "0x30e8ed0af16eaab50c3a858656c5b63bf91a68fed3e737dd6796beed8a5466c8", + "0x5eb5f9ced653a634b64cb8555b6715e0357d55d3810cd32205899de4b38719d7", + "0xa29242ea4617d79bc23d4b636fb4576e579cfefd8e035dcea016ad99d5540b95", + "0x9caae67ea3f02bfe05f692ceef56b3f07ae9165c6b625718cdb2002575ee2fcf", + "0xddfdb19fbf99e4f4a2810739c38f2323e031a714aaf1848f5543098ed416fe0e", + "0x27b1035f22284be3ac06df070c63bb9250c42f3ed7a73576ff2196fe9f64fcad", + "0x599187bdd3432bc079a6b199167d3238910e2229812f38a0f2ff0c4de0a47ad6", + "0x7ac1e701421da224e0a0b3c78968e58a2f44a2013e82b518ef52d4e590b168e2", + "0xfb7b3ff5b69b1dac8cac5108136ba528163f757436b13e5cadd0e25c860c6e1b", + "0x48188fbf367e539eca98f345c663511ffe2423c2397245508560d6559ea64384", + "0x59a3eabf99ebb451b13348638efcecdcb559a67027a347bb223594220ebe6981", + "0xa72e316af7f8323f783f551d5845e4e7e5a2403baa2c05de7e26fee8bc54451b", + "0x05e7c9453bbe6ee0d11096620ab17af0b882791dc526c288ea97b100f273ed59", + "0xf8ffe3eb8cc7e066c9376265802d5530fda05d2a0164a9f911b3887a64fe35f8", + "0xb412943c2c9ba2225b8c80751af337b1504863b97ca1612344ec7cb48e42b604", + "0x8a6a112fa2e977bfe4527e0b7eb754ba5a1d2ab2cf475b57cc17de9e71cafafe", + "0x432b5a50215e0787c67921230a4ca1380da535488591ca6ef6a30969c170c33b", + "0x3c3998175ee3429579370f138d2fb69210c6b3ebdd26f9c69811a6251b572d5d", + "0x18a6eaf317ef3ee5265720801065249fa1bd4b9aa540b22caa3e19e2cd6086fe", + "0x1dc41611bd259cc71401953a6ad935e3b7d77ca5865903eef20226704e263ace", + "0xa658e6f767ce499c409c262486d1d038718129e574047ae579785d19c285b242", + "0xbaaa5412b6928032146eb298c03be0e2d872dc09fe403422e51f7fbc6d20fe0e", + "0x9b9817766f251e5c44860da605a1627ce613d1b2ecd3a5ef7d807100cdba757e", + "0x00e3430d0dd1141ab5330fdd0f7e8bf02bde4ecb474a622c731891bb0c679d24", + "0xd6a103242e74a03467a5348a057d508dcd2a39f1d8888ed9d6246e7134412376", + "0xbec2734ce9a8c87be33f717b2ca0271a5911047351b7f072e8acb0f444380ee7", + "0x518475a03b16da362d1df980c45caa6c7fdcf4d832d7042bd2ad77382dd8520f", + "0x5b913f1f1577005a2dfccc3c93f894bf6e7c9b26e256e0fdf9ec748ead4d4cc9", + "0xa3da39d92a87f21bed42943207fd29626a8f39be4cc6b09ec6e04371a3bcf1e8", + "0xc8ad3a377364a1e7e68b8d730e4d4fd43519399025c0323b1f63d3eaac48ee76", + "0x2e7b41b2b13f66dd2835a0dbb7af9d3bb4e6b71fe3bbfe55404fb966239eb1ab", + "0x4a8aa7dca912650b88a323ddd7eb5e0107917b0e97cb1233abc251d9f15c548e", + "0xb797919e34fd7aeb693d153cc06c820548d0f87d12448f7950c8bc14b29837a7", + "0x876ee8bfe9648d234e71a09e31fb71d4ca8fda1d941ee3caf11219763cf4e201", + "0x407949340331ae5482f48656bc4aa292654486d2564fb509db72be51eb955f79", + "0xb40584c496ba9743e663f5547779ebbbf5b3d5cd309a5e6ffdec8e9451622b0a", + "0x0f2750328d27bed4944c111a56423046e6faa00de2564a96dbc192000ed54974", + "0xe7fe767beb502285a82734800dbad2725eb47c88ff8503e4900e1db5d8c5a486", + "0xefac826495ebe14cc4046bf82f93f59ce1bf29c31fe2d3f4bc724c91826ff08c", + "0x92bfb5a21a59647c019c894b98575ebf78dd98e5a784c53e603677f98fcc7bbf", + "0xadd2f3effc0daa898a06d142799a9abe551c7820db3b826e5b7fa7863308bf7e", + "0xdd81fa668c26a9a3da0ac233cebeb4971340aa6f4025848c4b179a03553e3f94", + "0xf9bb4e5053e4cfb7910664a16e4305eac79b1ae49787ef16fa2053e50e4006e2", + "0x638511c7fb29a1e3ebe65bb93184e6496345b58e88e1bcc2c2e64f9a8f1bcbed", + "0x565a13e8b784df030e3577a6fc515cdf305d25dd0920846e92cb1dcc638c51a4", + "0xaa2e6777c414c580b2839f5c50c30c334b91a004df68bbf544fc46d784e98bc3", + "0x6ffce98f08e7a1ee493149c95dc711c00fd2b8ad125119094cfb981dddb1df80", + "0xab0e825c791429e7803009b12fe1ee6c9c76cab2ce468f76203e540fde9e96c3", + "0x18f15dfaf662d173d91c1efe45bde19e5d2b23cbbeef3eaba3fa70d3803e6878", + "0x825cb9f81996035a510fc0941281e8eb857375431ba90b259c7b76e202134cd2", + "0xaeb5b02682db8f605080ccdefec23c910b70fc92ae21a97e79749a0b0658d5f7", + "0x23fdca513b2f0bd247d789fee128c11b6f0d9ddccd14b0d543f07e8e84033d8b", + "0xf6934bbcecde1284e930beadbafd89deaee10d60dc7b0f78bbb517f567d1bda6", + "0xaf8de49d4b264663012ae36842fd5f0a152e190e8a1e37e6c25605275bd998c6", + "0x3e6fa0fce050f0d715cbb365acfe0eabc289a6e34f6630ba677e5fae2aa01f22", + "0x1a3aad0e76527f5944df897b73b7c612d1dd15ced136870edf02d28b23b467c5", + "0x418947879bd31f9ac447afb9d755fa45f0f474dfcbd3f95099fb0964531dda92", + "0x6fbf96db99d2b53f7f88c278f24d24e524c4b4696f45aa83ef763088cf82059d", + "0x29019ee9a72fe70188574501b32cfbb804d84cded8f907d521f04990358eb7aa", + "0xdaf40a435f4238fe9d5753c72618ca1f9de5858e5fcea1f8cd60988e7ee6911d", + "0xa774dcb65fadf27c3831e9af780555450318c86faf49b8ffe59140778b5edaea", + "0x3372bd23ea73e327b33bc9de7b2be4635f4cd78966063e6b5dda958b82ed0b7b", + "0xe25380a7fb99046766637ac88252865051fa6eb5bfb621ecf445e54d9f878f08", + "0x46eb71c6a3e2f8f794573884ac53c3cf5a2200cfeee5f660b11b6643f7b38bc6", + "0x0758a9405eb2d0db5615cb210937331deddc292f00900dac2fabb195845cd886", + "0x9c4d10bdfd5c6ddd3d0bd10f140624651b689cd0c3a502003657a600318ae640", + "0x8bac4bf0e603837c969661f1182cdbc79dfdd288421724ca9c777a6b35c6ba6c", + "0x403784f5d28c49870603f3a9dc834ff44b0adba3d73beb71dfcc7382a82dfcb1", + "0x1c5fa5538618fe03e8cd18f8c8e86d46dda887b0e8a8ffced5333b08aa8ef561", + "0xebeb32cd0ddfb17c5147675c9f37ada2a46b2fa4fbeb37a056db8873b8175af3", + "0x0dd0c8c650c70ecc948cda0ddec1a91f043f8418bf397a89d6386e6e1ca41dc4", + "0xbe17065318c2c273f32e2088bf179f3481bfc47cafb5380f3207e34d3b4c4d7c", + "0x4f059ba707a405377a4149163ca259885508f5169b6d09cbf3583a48ae258af7", + "0x377faee9fcdb1c05a63fd95323fcf6715398acb13912dd4dd03918ad61706a22", + "0x7c9889d3555b4887c85000fe2d7f057aea2e272bc7018826003b38aa9a6f3210", + "0x530e7e9ef7ce1d09aa5f14398bc41b31194e00fada9767d39da4871b09a1a7b4", + "0x347c5a50767f5d6305b5ffd5078ad291d42d3a26e9f957fae39e8f33159f3184", + "0x192a4beb856bd6f7b1554ef13b6ec7f93564de4a2a738970cbbc5f3651fed485", + "0x0773eae79be1d8542411c04793247791c9a7ece7f56db3561fd0512c1904baa3", + "0xd49e46fbfa31b4991ae333faff3d56fa7743bcd4efb4e6a09228ce774d40d516", + "0xa7d30688b53e31c784cc43ca2028addfffb06550416cdc36e947eabf0def2e63", + "0x44a9f22865eeb75b5f28e9fe58fb271e321f20b7493a8c0f80e8dab925c62b22", + "0xaea4c28410cacd3094ef5e27619f688647909c713f56cc33309a72401f3ca891", + "0xdee80aca94a748bc98196b15b34046289f811d39fc602b910568b0f525efa792", + "0x7380210107b409c11077f0155e1f2eb9641d7e82f753f229d4c0c1d7fbe9eb38", + "0xf141907ed3fb5e8585a8f2e3cebc03710e3531f5cce2160005318691bd6f5b84", + "0x80fa125af6d3e522dbe46433c8c230e8ac2c183d44a152294b33f365265b1d1b", + "0x99777399446700454228f63cb235dc1ecad80ae861c10a6032458244e54576cf", + "0x77c70c500d1c5647f3e59a86e3da52747d68252ac6c456cf2cf35f4f2b559275", + "0x65a9ce5af3a9ad5817c55b4a3c90aab1f9411f141831655fec74d0110ab17350", + "0x11da60a143e6f795cab07aef93de36816594d3c82b3e417e93c9deb385156061", + "0xa83317db7f2cd5c4fcdf671898e292ddf08537efd236ca35b7c4d503da6f0d2a", + "0x52b30971d8ae88d31099e781e559f4f411bcf751ed10ec3b78d801b100213589", + "0x1c9645c1fafa4553d96e31fc73b8256cad64781f856c2d3604aa5fa1fef6e115", + "0x389da454796b09e4ceb7c7f67e0689d0b1d3d616167e7556ea50912b84cb9c73", + "0x32db05ec9899b4f4b28b9673ffc5febb3b51e09047578bb93b21850a36da6bd9", + "0xc8b893988925f8bdcc7b9bc581fbeae31b967fd78ef07ab9c5d854398a6d507e", + "0x91538b3c77ad4e1789fca5063d9245415a967c9205631f6ff581d0d0d0675982", + "0xc98df19846f8bf036ef3521f69ed6aefddaf33517b4187cdcb25654b1a8050d4", + "0x3db482e4b888180baf6bac85d6d6c0bde11ebea3044346ec9b34889ba18ec3de", + "0xa53e2394b64cf1c054e86a08018dd9733d449eb8c1e42ce281f857a75609e0df", + "0x744eb03e97e41ee213169b2b29bc68e2e39bfa6be3b08e9c4deae8672541dc7b", + "0x3fb0e8eed200fb91135e80687e85716bf3e3317bd7cfa0a7a5a42dff44ec0d51", + "0xcbd2267c0d00ec1420ebf2025ccd4da226ba42d65737d6098b7a0eb99888fa25", + "0x95e4ab403cc3eebeeae75c38536094f801aec04f3176a719617f19bc13ad602f", + "0x3598c67b47e69b709f52404c94383dec25dd6ee7b8f71f5fd5b88705b5aa8067", + "0xb8606491fbb5d70e6471ecaea8075d876e1e00c441a1c39f4b82c1610109c376", + "0x49e97e2c05e1387eb69d2b5543a9d398ad87edbdb82b391bc8d36f2417205ca9", + "0x46e22e9f0532e38370b8e81e40d15c0cec4efad57ca50c9a39dd529b2b6eae0d", + "0xadd76ee5adafb256d6c5291b9ac937bb9323000d17c3c9297584dc21c1c934a2", + "0xff4a53f4af47ba9ab99186c8d812dfda7eb16d40998b3dd4bcc9b68ed840948e", + "0xecef39137c99f7aca3e1b489a9aa200a2cb28030cde15af63aa6938f6da2d8d0", + "0xd2c1f86ac5e0c4337dd5ff8e634dc19b6cf57992248df02ebd4bbd704f3c8892", + "0xc3b6f8f618aee42f44b93a0d9ae0e767c71d252f12fae92183153fb7ef962335", + "0xadc194765c054bdc86699be70eb791167706ee5d2dbd3c22c01f6b52d0476755", + "0x0a62c42c8400406441084df3cca7c4761a739afcc7fccb38a7e60033c25502a9", + "0xa8af1a1cf7f8f5277359cbc3125e68ddbaab717eee322c7380aba5b05f57cb06", + "0xe4a192a462c71719af024f863961c092ac7c4d163a596e5f7b50f811e4878adf", + "0x4cff3a9a70316eb6af40d3c7d93bbe6d142d24f480b3373d6a475dced41bff45", + "0x08abb1b8bc2f5612fdd0e8c6fc2519b6d99d03f786ba2e311ddf9815e9b7beac", + "0x93d1deb7e581cbd0030d103635b47e7c845344421f312e586b58d5497482cd78", + "0x6b7e48a81e27d70642bb46bb10ab79657fcffad4dc548420ea20b10f7f1d6cf6", + "0xe29d4042bda4a7499c9015dad7617bb25be2a8a58681c595c6db60e5ed44bcba", + "0xb4d24e93c306cadf185c8e28a9a1c261e0d889e102c0c53885c6b52848f6c960", + "0x1f1ae20b4c853b7b6c740eca4e97c724657171df49c98abf6ac79fb7fcd03260", + "0xa3b26fa1690a0d380e035d9d25dd9378282634a689342308b925933e43827683", + "0x0a0db808b6d2549e04e5df8c2b6c6fd5be3b77bac4bdd5f4264c3bd8277f9c42", + "0xe1b359e8213edc230ce8c25790bd7bd2e1c8491c14d38a86c5e688ffdd01f35c", + "0x090e28ea32e00cdc539e27a582725241cd9574fc14e2c3cbb7383ef439d9b560", + "0x711743fffceb0ef0ec174cdf3353b96465d8ca915bcd37ed3f2665b6159eb17c", + "0x8935df206c17334f59d58e92974d90d347760ea545389932da05f0641326fb9b", + "0x8f26b6df469e3fef3f6bd097c415b5de7fdba93fa9c035653ba232646d85ccd6", + "0x49114ffc13b9e7d5bf88406346ebcdd150c87c7a885356fc89dbbe983264f8cd", + "0xf829227b6c4b65980a071f6f2700821ea49223a9831e796e6fc4137bcf197f92", + "0x0ceab65d955df692d69493e623d91a4625e507fb2b6aa5de0f43986f102e0105", + "0x7370b25f8eef984cd2259a9d592016a6b4dd4975e66032082063ad8a451b2612", + "0x86ccadf5a5e87300003b165b65f81fa3bac4b28fdba541e9e397bd7926fc5caa", + "0xcd364e023c9bb678ca9b2b9549cf50b79c689a6e0cbfbad8222592fb849d030a", + "0x69ed630e0e4df351b2330d2353a8bc36d8aa616a6fc116397887874f0d1330ce", + "0xc01c73c34a4f7059308cee17fd5f56f5c14a39756f91cf8bdaa210b2e7124236", + "0xa87c10ef755321cdbeb0232f0c31d72647448d28219053fe568293c0da025e78", + "0x53df2e60b80493b6defc902f5f85b3df55a32193e0727e903e054d93d32e94ba", + "0xd2e14ae4b659a8d189d3409d15d19131efce3421ed5d83ea8be95d3a8dd8a93f", + "0x4973863f4c9c78af01105e51c057e63918a9fd20b72c3767024f2730d7434624", + "0xcc40c034e68301864aeeed457d04dd9f83993a54d8142289e434a1640baa8d86", + "0x3a524296757505dcfba042a4fd1605896cbe0c5d8c325278e18ac018f5fcda73", + "0x048df858f0a385f1d4411af1cc9679c75208486a59980ff03588d1814ab79dc9", + "0x6241e327f691dcf031cbb451c833b2c21912f92b0a40ee862876986321fee146", + "0xe75f114184914019f2115057f8cc2d464493f5256c194d7b4a484a1bf8d90b80", + "0xb3559619d4a049247ed3422e5db41a0106d5829afe83bf4a73f2b165c5fc82f2", + "0xdb8b857e23ac0a1280da38daa7c43da0f20a488ded42457bbd979cf15b71d024", + "0x80e46288baa5733f7aef40c3d4b890b186e0d6584f7f1e5381c7ff9d88990923", + "0x451484f7e329dda4a0bbc9359d4a88a4678be6d6baff34341e1542b2989c0aac", + "0xeb0dce828ec45c36cbb0860a08d34e22ed42a7b8a4a2c16e42bd9f9377dacab1", + "0xb24bee925ee9d0346bff994658f79263a22410a0a0fc6e250fb52e3efbad958e", + "0x1772e3bfded75d113472c7a9d2869fb4b4cdcf3ecb7c937eaff967c41fdb204a", + "0x0a87c5fec0346e757f016813f3b7f69ef661ed0210e31147273856a511f233db", + "0x78b37289f1f5e2656809647959a4037a9c8fb326efad9b0bfc6a634e591d1f35", + "0xdde49b5fb18f75bc6f3db6427a6d8aeb5d790ab4ae57347d04bd059c21248389", + "0xe72a49917506b0c01d7cb79fb2931626b27db18b215f2575f93138b18db9bc93", + "0x928442fd15fa6daf4cf52c5f0a222b9365970844de90f31055655cfef2f860a9", + "0x6b029ba65707f089cb4cc8ffec0bd611ad18899c4109958252b5b598a9fc2d61", + "0xd5a60f3e392327aa271da6d759536b2d83cb756a8a25999cbaadf15d90c7b627", + "0x1b6f93295777026f3d74a0be903ece53a56db398544af6250b6e3ca2582d5ed1", + "0xff9b35a641d0da801479ae8c61719ae9d9a16bf96cdb6bce6c44be2fae893dd6", + "0x8d76b03dbfdc0fc8a0ca5a711fecca74a628d3d1c7460290368858dcd755ba3d", + "0x30c86b8c61753889d4004c9cc7f7dc7fd22226e054fc4b5ac3a05d0b4df938c4", + "0x06fb71ff49cb01f6293072400e51776df130cfe476dfda3e418569e8dff2b321", + "0x71ac6579487e504080983e45627901edc937e69436120d6d8a3f27ff3df4e97d", + "0x97e22fd7cb89265f454ec44826ebab30008d9014a12c506d6c705a8810c92886", + "0x4cf4be7195db139ff15f4eec92a10ea395964ae3a8ddf7cdbaf563defeb66406", + "0x60e93b738fea0061242152fa16ea2a1e71f446c2034872566bcf31af1651bc9d", + "0x308c226146f6e7b12d09b2bc094d8438da7849f2065a0c94101d8f9aba6d4e7e", + "0xb76e52ffc134676848bbd13326cbefb73ca023fa4e4fa86a74c6516736495d51", + "0x6b5c966cdbd6eb3f663da37d2a1a1f3609dfc7fa012c714e2d96de09c90cb9db", + "0x2eb40bdbf66b812fa0ef0992e81440e5cb140f28ed202c34923461c26c25063d", + "0x0f5874f48df96793b989645ef55267f67c7778a593f4237abeb5a12b73a64950", + "0x530f285649d306d906d99cbd6bc2e6bb16b3dd28fe9d95f30f02e4df6dc61ce8", + "0x755f938790b84d5e75d783dc47560a7705c5682209fee81376ba2dfb596136ca", + "0x50181a782c9c70023651f485972994f04eca9b9ffca861e076158a4789429632", + "0x9377b1394cd022030683e85dffd797ddf78d0bc7c151ec5b3ffdee6185570709", + "0xbd923df50107451b8f07e16d25d148a72a513e80a45ac3b55debd2f249a8d6ac", + "0x65d985dd33237f8cffc6351d1c6faf17f6f61c7005d39814ae61d6c91522dbbe", + "0x46fa86bdddaf37401b5d6ad33f45123b4c09d0356aaf5861dd2bc6b620850887", + "0x9905bb917e0be1054cdadf38fa27a5885e15869270fbf566c78a336554fc3fe9", + "0x83ba2f7dae9485a94fe3b88b83d21c675232c6e0e691614d041cb3ecd044afa9", + "0x6d188f55dee486fc093e06df993cfb1f7683d2185ef72ddcdb97f1b27dfd9e7f", + "0xb81a12d65c79644608b964720f686139da7578602d9b0c44464272902eb7d8de", + "0x822e4cda0bbc2e09543d1a5ea69f3ebdc184331b559d46cc057a97253d2ef5e4", + "0x822e4cda0bbc2e09543d1a5ea69f3ebdc184331b559d46cc057a97253d2ef5e4", + "0xe06751a47d3ff7faa006350409a1a4c100708060dd0cbbcfe4b379eea8a77b6a", + "0x072685c942425aa86e3e0b5fe4ec379182d7c2858904b7802e49b4750b695e19", + "0x443657ed0ac614b8318c0140be3fac07cb6a8c5374717d91c54633d55f217e53", + "0x36df057fbbc8e0f8bd59317094f6e54cf8f0e791dbea38aa0e1c46134f6e1d95", + "0x21109c734246de84eec50d4739c8a4f8e79c6f6555a049ca572b5d85aca3dcf1", + "0x66d5ec6165c2e8bcffa6fe84f7830c0a962c9d0b759c215f32d113d38b0fbc26", + "0xb09fedc66c02e417a38a1b4f8717799484f8e423d2363112e0fbecd07e0050f4", + "0x8bbdfdfa2d3fec83fda7359f5786fb554a7ea255868fd675389af26faee5f86d", + "0xf47582c6498666f0205bab23a8a5b264fdf6ae9d4e964ab0c1823a4b434183e7", + "0xd612d3374cedf43b852098cb6059a509f97cec40987f8dd3883eaa3ddc49746d", + "0x108acceb7c60323d16dad7b8e3d632ed7f059dfee3003c7f55bdf265481aaded", + "0xd92e33f8becf755ca99252e6d881721d40d37a2b9c931dfa62d4e415fd7ca726", + "0x4c5169e04e47cc05bde354ef641bf2d71a135adbdced8c470b807e3efa942d6c", + "0xbeb4a7664459eb7bffcab593c66dd2754c8b70e3b7c8a843a2d8b31b95aac59a", + "0x2eab8ecaa749b3651999323b2732b29268d3d5c0f83d16320077d9f37b740b0f", + "0x6c9caad13f043a8966015ca29fde65d2cf7c2699cb95322a5c38efdc0b368582", + "0x1c403f88753276f8e5cb567ffeb5d38bf7727812ae4d8ccb115d3da38be93fde", + "0x9de96472a8066fa92373cdf37d46b94a0c4063ce86200dc95fd35518462a5f90", + "0x594db362e6bff9624d9df1cf36c12932f45b0afbb6e3c21d73ff5f0f939f657e", + "0x0a45d18c4c63379ae1378f694e745622edb2ba9fbb244c87cfa7e03eb54fe496", + "0x890588088853b531ab5f7ec4109dcfe500a79f4fadb54069c8988eeb4f1d2b3a", + "0x72093ea71acf6f57f276981551ff4191d71c3dbb498f5db139dc0d6be2ec9ad4", + "0x4afb374a510cf31e71c10ff8a08fc139679a3499e384d1ca9efe9ede6ff01b4f", + "0x0ab21174b14b53b54da969cee828e179e8c8afdd6479fe28dda061b558636346", + "0x3b92b1e56cf4bf75f3bcb83559bd726fde859b0816731110722d1f692e9d8e93", + "0x882548fade218ec8253a84b818260a471adfa271cc5d2c299a724525493440d6", + "0xb05a77ba39acb55a69a8146acd394386ef826b3de4070234fad5301f1b343873", + "0xc65055ff784d4fad4e7f52f857ca21f7ddfae465ac6f2a058f81980d58c4051e", + "0x4bda862ddc2654883f5fac93ec61642b8e8ca0f5efd0b077e3442670a1e239f5", + "0x961325ea170e4f19ed06cdfc599c653b5fb0739325a58201ee326a115af1aaab", + "0x82bbac03a46dced8e0e05bc8f6ed461c598aff8bb50fc04b97829f4bcd7cea0c", + "0xdce173a4e4b49941fed2c9140a92ba806eca75aaa7589147696d681e2fed316c", + "0x1cfb62054d87c69af83fce984abd15ac6fc51c698f81e44d9f7e7a6d58cebcce", + "0x5bc93cd22e1566d8e3c3654bffc7708c9c4abdc03d70651beddb959d6ef87590", + "0x84d16642213ea4b9db65c40994a233dde3a5fed58e0c37a1bc21192cc6fc696a", + "0x02da24b6b5dc7ae97ce22816b8f37a128ba5904132f0088a6aae40d47d2ecef5", + "0xe0bd071183d9e0449aba5e8506960f1852e46460df61254c8f3f6b7678e0b0bb", + "0x2baa7d4cb3a84959f4a472085264a5d8a17dd6187f4ba77c35e08199b5713467", + "0xb062f53c88a3fc292be331e39168662247986b939678557cb44211f9b0a45b2b", + "0x9ec483f590f94e4db4ec6ed876dde50a47f6f8a389e79b7f42964df7766ec43f", + "0x082878d722f26230e9f3c68c9ab06fe73dc7456351abfe461b9f3ff9ddcee852", + "0xa0a6e6210b3d4bf3fc6f267f341d6c0378f15b92ef71fa5ce388c0c4b5aa5876", + "0xe040b5772830b9ebdebd7011fc2e08de626ca316cb0bdd7bb90539cb5eba667e", + "0xc9902be157f44483b191bcb4965133f35a55baf093a557fd67dae91327629978", + "0x3f1736c39205d1ffb1173956ee90f3faf0e23026164eab696cb1805f00e121cb", + "0x5d7e7989fccbc01802bc1266155a80ea9b6d30651b7763f81121b7c853681a67", + "0x65b98fecfefc958e157f88c8421e032e8f8768f8f73cee2069611497cf57d486", + "0x518097fd8989a59d1e916963b9eeada028d2abd2753fd241a6e501fe9b3aecda", + "0x78ae0d9170793d709c114a65eda30fdc9113a5e034eef43b4c2ff82162a60dc1", + "0x4a146ed5b607ea6fb84d302fd99c1b9561153560cd51312bc400da221318354e", + "0x04df2631c0d8432bcf6168c6dbf0e3af5589f826617b466552083fcb88128f4f", + "0x69a3b21e5c034394afa40bc54f933dacc5f2357dc2fb200e77093e46c17e8351", + "0xc7f0b0bd5b2eadbb25a8a1a0b6d01b7019302efd0d7a04914adfe94f30469863", + "0x31ceb76fa263d724bf6153f624f934cba2e5bd359da99348139b08292604996c", + "0xec43ed674e15b82a05597f5a2169a2103de0e5cb1401886841bc03c12dac03a4", + "0xf2ec459332916bb11c4bd06e7577b33460b5f41424a7ceed94e8a998686fc6f7", + "0x166546205536782203ee8343dfc195c97f6487b5cfe908e055fb3f54a5c69765", + "0x53a630ceb5b91f91e9ea07e5fd784da15ce5fb724a6c6f36694d08987e4cfe5d", + "0xcd5f79845b8b8909d2810e4e84e840229b420e639a5d9adbbe36ae857c95ea86", + "0xe1d7833ad366a1e305f8b689fc5dcfd4fd8080f2b4da854b46aca22e47e41bc5", + "0x69c1586adf9660f4a2edca3aa3592b225c73aaeb1de0214eafb841b02e155780", + "0xe194159e9c5c036f6891ff00b0a6649fc05a802e21a7cbbb98ed03e7a6e9ef03", + "0xa356db84dac74cadde1dc3ef71dc3eff89c7097568cccdd1b8b042b59edff590", + "0x9824bfaf5f26500b39aa04fc2b8afc0c181d86c8084940dbf7fc0f944f4172df", + "0x7ea3e1a363db005dca836429b0ff5ceb78da836f038aa72c832366f74d29a6c4", + "0xb3069dcae8460e58109ad1d476ae00c95ee8f4d8703f1ebd85dcdb1d68f5f65a", + "0x11f3127ea2399c8cd6256ecbce6686ae45747d705742714be8012a5b05506351", + "0x40d7bbcd81dff8f19c07370a6968976a1d2c854edfa156f42cee47415989e4ac", + "0xa62489e7e6b236412fc06ba12c2ddd5bd3d5f1f842470da9f53af99d37a3afc4", + "0x47ce56c0b8fc4056c3ae82a8ffcc59a223f31c91ad86693fdda7d89443b4adb8", + "0xa8ef7641864c5b3763a759baacfcee794bc0ded5f3fa21b0f9ebbe1005f1b588", + "0x9fe1734d0900b1731658ba21a7a7209c2cecbc2e3b9ee708605c5694c70ff3d6", + "0x3ff93481680cbb85e6e1a3dda1b1ac9bf3f2601a69a3a37bf5d8f5e3e3594df4", + "0xe158e4c5bbf2b168ece12d069d851eade4e93c2f1ab2e164d461d546d1d5e138", + "0xbffaed895e50a6be44a450be79d59e3c9d631d97b2d62c64cfdb53651d213883", + "0x76e49645a439b0655ee97d61a0d9aa2eebb26acde137c098786a1532768eb231", + "0xada9dbfada5b684ff2247f908281d3bac9175dcd0f4224252d5b1c7b3144a750", + "0xb060f0b495be3bdabe2c72a499fe77be8865fe7ef003cb1e6bb235adf1998f11", + "0xa361dc4b33b89a545d8c4e723801dab3594537182668d8eea16408e49739b7d2", + "0x7e1c087cdbe16883e6aa02051e3d46c6dfd5831863fb3b224c0a15a0f7818d21", + "0xa5087921eac99d1db34f297cfb9d6766e7b995d24f1c36c82e9e22ab2eda9f31", + "0x026bb9ade13c606c2bdc8190af12931a045042206c27d8215487d28c8734ebdf", + "0xe9945f444a66620781ac50599fef6a7c52a294de83a9f854133a37af308b8071", + "0x054f1af61e2ccdcbd9496a648eeae9e7ed1dcc10be89d883c0f3265ab6b151e3", + "0x8c010c3732012fb93314a3176bd0d1708393d120a5d8c77379f896d0e0745d53", + "0x3db2c433e0923133ddc661e26035c63e970c021e29ae44c4d511c994732f0ea0", + "0x2585fa5dcd511f892e587df1e19a1f51e7a9ad37f546bef6c7950ec320581b2e", + "0x4ded146d59d8914de3a8fe9714910e018306cc0c0f31f8e069f32800660a4c0b", + "0xa98b6f84762876bbe4e2cf9df9ffaad000ec0a265987e3fcc78d04431f1b169d", + "0x75e8c32c81465f60f01b3c63a9ca4a6949606075778c822988eb1923ccd66d87", + "0x655dc46703bebd13ce91a198dfb6aa3445bf3b245ff4c68f4fa2837ef04ccd66", + "0x4c71efd6c4e1ad84ab31e489eee49a8f2d3d03b3c79314f2b31301c5fbffaf7c", + "0x4b6f147713e7445f73814fc00ccc367dc3e9304f3c40d257bacf4d038136adaf", + "0xe578a422c1653483a595274191f360268aab30b19abc7167a5ebd117e531b209", + "0x633b837c29063077ad93735bfaa0cb5db0838589e6fa4a77d34575922f7a50d6", + "0x7aef9747c93513de8f247d6e6f66ec58873f18ca95ec753f62d71c1704425fb9", + "0x79bf3d448c79c0046a0c41a2df1b4fbf31fe203d4e534c77ba65fa63eae4fab3", + "0x27aa0d0fdf7b0db3be6887f6774f4588c1b512d6f21dd381d7b82c627da459e6", + "0xadb2f21f74fc63a5945ee60f47b031bcb7f4785a43a261f8a96205ea95a2fbb2", + "0xe118bfdc390ad579bdb5b1a179ab5d0b3adaaf8c6f895062cda869b796188028", + "0x0f2e45ba5814149791c61d7a40754c1a18981c8eab395972722bbcd76176d37d", + "0x1c13ef46533b8562bb386406b95bf9ec5906f3d623f593684ef2e3926137343f", + "0x2d57679e181fd02851d29af76fe1fec9fdc0072953cdfc8fda9828e81ef2d0a0", + "0xe05cd514fda448e959f030b6084172a2f485742eac7d7dff7fca0d872e5e1750", + "0x108c47b261b35563335dfe7fd4710769f4eb68042305e7cf813700609db57c3e", + "0xa52ae6075265e41aa731d02da314a47cb7ec00f9744d7685614a150fc47e5032", + "0x3da07b6c03ae5f13eb97c212ff5b9f2e499ee6d29d356d448866b02c81d36a3d", + "0x6d5c321568c7b9c5365202f7364bb0f88c1e8512d9be1e405d2ed64781c44da4", + "0xde69ef8dc7eb579bea26f3f07ab1b685b47d8da65ac46d1f10149a1cf7645af0", + "0xd95f84f3bc4c54e6031da8c544715ed53da974481cd00ef0782f75d0855935b9", + "0xb5729d8cddbcd5db3be1fee8a834543c218c5d0732b19cacf6efb2ebead652aa", + "0x7bfbe09e431696a610888ccc536aeb8ea01882513bc9bcf776e07787f92418e9", + "0x5fbbe1786c9a3d12b65a2cfd923c4cf4f485ceae5861fecdcca85bcf1338e89f", + "0xf81094e3fb8acb490fa5e74430d30e9680c3a664c34c431796c27255536b993a", + "0x493395f035b9f6895eb0c03822d9ea277c3bc2ba05a0d34b25aa4ce0867e652d", + "0x3cdbed46f3f23e0879b376e2adfe7e6e35a04d5d22027fb64c08c1924070a7c5", + "0x6174026b7e93cc632052d468c8f89fa4d764839680a64df002d282a10e7cd6f7", + "0x98e0efb8cdfbe9fed6cffb70b12ec93055c55fccef7ea475f5850096e0576058", + "0xf06250b434fd039a0c8fedb1f91bfa1e93a00fa63d509bc87dc82d61a0209489", + "0x473d6e7c7217550faaf5eeecce6a3c29cdb5cfb1ef355e185050181a6f56bf63", + "0x8c57cb3f5313b7ba8b1eecdc58be10339fe1cd18438265324a5e2551fb318ab4", + "0x1b982779fc4b810cf3a60fda97b778f02f74f6418101f4760cd970974f3023d5", + "0x0cd0f726fadbc1647f9894a8df2a9ce24c144e67dc39784c219a5845228f389a", + "0xab8ad2cb53bb22cfafa3eba7b63f9a3bd617e3f415265debfb07593d2136620e", + "0xc95b35ffd04b26c1b7654bcad4305708e07efe2581e317de30acb78e117b1288", + "0x4675efd946e7bec02f82d8fc4ee1b472c8df49ecf991333ce2c9e18149af6834", + "0xb024c9be98116177d348abcb18a291bbc27ea321d60e1b391f60d51d0373db29", + "0xa9ba705a71725571065cae3655f742f76d4235b97b9d1eb9bb335c917c823102", + "0x1c029d731b4043752c8dff6df4a1045a73eb68ef3029fcc8889fabae64aaaab0", + "0x08b183f4b1e33608866ae53620297ddbf7f632981ee9f774f56dcc567a25003d", + "0x9386cbae46877ddf92257899fe7354b8d2cd8b4aae4fda99b75c2b39cbaf6d7d", + "0xa86daee6266f8bf1d56d15987b9cd0b0102ef595c741f2200db580e5e6bf062d", + "0x7fd1a1bfee0fee53c41a4f9ebfeedee05d236c06ea0f13ab5cb5db9d7c9c63df", + "0x95cfdc73707eb3de8d165f3a2caf2690eeb47616a45e67db22c2e94d875f01d5", + "0x5b4cf6c085cf9d35404545eaa510770cab722fb1215704a9790f266ca2d2b59e", + "0x9ad89fb22f142bdbf3ef2059aca68e07680d42ec1bfc5e0e69a251056665fc9b", + "0xcb838741be3ec6fd1ec704bd400b064f7926641cd7f2b1397e05e5e4b7d23866", + "0xb5c670914587e67639cd867e1f21c5aee124634256a68befbf89210ec5120c0c", + "0xf3f2409a3106ef4d3162070fe6a09d9f1a03f60b2b88226e9dd949b11d732966", + "0x621e2f6792dddee21d013353a47a0f44fbcff3c358db8ad7bc0f91338e160fe4", + "0xfdc70477952dfea21c8faf50c75e2ca352762d1397a7c72395ae9ddb816f4959", + "0x364563870ce1fdb21db29f020891d33a8e98b026c23287092eccdb27bec88a65", + "0x8cb5dae2353b2913ee58f25b485c208ada153f7520266ff309f663d5c64e70fe", + "0x195db3e447a751528a73941e508b263a01383e69db0090f6e2ae16dda548d571", + "0x22bfef7555dd93ff628aa0d55cee216dadbd181a7a887f6533ef9ee0ab071454", + "0xb3355e5e774092b3fc4a801103f9e620d008b5b10a368bd34f8bec6e9984b0c5", + "0xfb44361cf4a0580d12d4ce0247e6fb39cbf4ef9b173252d605ef6f6ed83264b5", + "0xb8919bf7b714d01a06688977c7d8c3d9b5bf39142ac2650e4d4a9648e15c0f78", + "0x34c5a584e9e4e1d13e861356d85857ec139729ee737eae4a1dccef481579d195", + "0x5b4827f261596eb264bddf7265a2c2d72b6acbea94256879859fdc0b7c8f85ce", + "0x808d00ec3d9790f068af8f898f63ed13031daeaf13db5e70565c58798195ff59", + "0xc8b1d139f1ce8bb90bcadfbf4cd407577361aca6597a4588cb3d72bed2630b63", + "0xcbd86e28452d58f02b1716b627164a9975aff7cad5e5e665c41bdba2114e0fec", + "0xc7c011c1caf77051c8eeaecee93db1d7973092333543a15832345795fcbb122c", + "0x77cb61270d9fd73fab29141d058466d0cfb5acbc08c5bdba24e3c29bfae4fc30", + "0x3272afac66ddb7fe05fc7027e8be395943f84245f35f4294939805339d1461d7", + "0x8a09fc7c4def314360690f1edfc593d37cf66cd40691797ae1a5bf88181e2c91", + "0x8d6f27c718a633738ab7245b0adc0f2900c3afbfb4e20f74422d03d695f6c178", + "0x6c91db61851a621230a6e8234848375c16ac54fd7d79c66a746b90c3028d52ea", + "0x0c30a836120942e54ff55618f3d28a87a3af5958dfab407441762c4d87d89eb5", + "0x0a4d39fa1038c36036f915832be4f4f7c4d97cc5b2617f2087d27fe5bfe99fc3", + "0x996a0bddd03a8c95ea26a4373682b0a12a480a1ba94cd135744feca77cef6787", + "0x45ff709ef55cbb3f08532acf895c634983b1fd6fc1dfcc5cf58d366620caf667", + "0x8cc7dcf479edf84fb3ea2122e1a63f29382d502eb1f04b37bd727d301ab7a599", + "0x7a01a8ef2f2438397641ca589d321b655828d64f03c527ed2994cd24b98c3ed7", + "0x394ce7d878f82e0225fc8cb6c9396dbb6f572cc175752726184301492183af0f", + "0xb4c62fa4c7ff4f8f3bb5caf6b2ca08049786baebd53bed504e30f628057d27c8", + "0x717b598dadec267308962d923f1404a96f97455833bcf73ea0fd170cb22d34f6", + "0xe0cbc3a4e8d7143531445bb79531c40fd4dcfaeb82b1a31819835953c5dce1e4", + "0xbec32d9ddab592a12b64955328500741831a3dc9c6a85c260ac3628c473ba7d5", + "0x28c58acb4e5096128f6dfcaa24ea74b3eba497c3d0edad57b3c964ce728acf1f", + "0x47efd3946a5b7a7abf49a5f7cb858ae66cce2bebf6d727ec1b379d89aed36e55", + "0x05175476afa9f9ef760cfce406676a9e873d867599ba1faa19fdcb32e5163faa", + "0x5c16df38c2de85645b37c89ce8d82c3f2df2cd6c98935c4718062c299dc62b28", + "0x41f70da27c1531f56fda4a9777f26034ef6dcad5957e1ad70c870711626378d3", + "0x50e0881e6e0e5b0fd723ca3e819acd21adc619e55e3d38b3298ec673d415a375", + "0xf6aefec3420a2946d6c4cedb29044732c90b837eae740f1507cb6a4cce44a94c", + "0xc71583fd039aaad52a482e3f8f6634312939c2ee5b15b40edcf68022c4dd8c8b", + "0xb5cbdcf159ae11d8fcfed853eb5252659346205500d3bfa9a5a081cba12ac573", + "0x88df194712a57e52336730fa3d251d5514343806aa02b32f560f2d1beea46eaa", + "0x80273d16666d7afef667b19f1fd9118ae437b1322082889ce92e71b8a3d0fe14", + "0xaeaca19b57586c46110febf23232ffd786c387287a4a9df1a01cc32cbcf53c2c", + "0x3f5d0d439d3e05d3b0f1a74f8e5e9dc8cf6d12c2cb1b1e18b357a11047a0c586", + "0x715681238e3391373db34cec4b1d31ded7ef92affecfbf6f11adb8ecda075836", + "0x12ca601829b2e1cd73f88170aae420c5192248c827b8f375db4cf5d348bddd3f", + "0x25868113d13206c33f05c7ba926c2b67e6c1ea02deff501c790cd22b5b0ff25e", + "0x0c423452d8811de4a77269101e6a33bfc4f0372420712f36a5e33a3127b398cb", + "0x23c0bce7ae7294f85c7ec88bec3afc6b7bc692b6741c2c483664a0717c1bde0e", + "0xbb6f81d8a5bc17e2eb9044a4d260e3200ce351aeecd2d42fa1d5dce96527562b", + "0xa360ae5bb3c1cae1533e07f737aea88192402ef9751c09f69d0088b2c1ff565f", + "0x3c235e243980486b70d87dd474f67f35ed2fb8a1cb0e08be900942a8cfa40e9e", + "0x64f2ee2c8913727d3fe8d06cc588fc21841133ec3afbc184445694398758e0e3", + "0xbffcbf711ccd9025b84acf44152b6842df67a2a90d1a0e49b1fddb3c1461d2c7", + "0x784c8c855981c7e44047e9d9f31b1098983081393e84da53ac8b0bf3cff49e6c", + "0xee9d9168c2bc0ec50839907e390486bc9b58f51fd7ed76be5ef6df2ad73f6518", + "0x7558aedf18170c83f967282b772573ec05fa407fe559928eb5739beb145c72fb", + "0x54d34270db006c0a04fd0cbb5ef7723a5f7c3db44665c33ece3f066b15f9d560", + "0xc39e868941c3edd42114876109732a294f7c6b079b3f20800e1b20f4ff6c956e", + "0x1ae04f9fa31038b0c6e57cade006536095cfb33e4b25d6644d818a64aeccdc58", + "0x34d48f2a2029fbeeaa151619c3fb7aafba490e3bd29e8148824477845dfef0a8", + "0x591d77cbb523b95b24d52e89dd84351db2764b1c61e35cee836c04e8c9810399", + "0xe80c4b1bde897ff4f169a8507d59a7a66bc3c0ade68028d791b924e9504659ac", + "0xc0d08d86cb73693120a3285a15f97619063b63898c64754b048f29288736318b", + "0xda9b661adc022abc846fadd10c6d9cf852aa73b2e66a782762c6b9f6ca8f8de2", + "0x972dd7829e217685a17f64da3c6446812e2b376ab93aff98988ca3112dd3f5e3", + "0x51d606a5e7b5699496bff957a15b1eaafe1244cf1fb4c58e600221373fda7181", + "0xcbdfba2e62930691a0827a7a51152aaa322934a2bc4924695564fd5046cfbf49", + "0xb5e091485145113438621f1b5a6105641a0a9f4350e7c79fe86fc7e6b1dcdf3b", + "0x0b7ec360c54cd4827641ee6cd7b32dfa0c8a7050de4511d062c3a627043c9264", + "0xce418f75eb8bb15a801e9fe5026837a8d561ed13ccb728b2077012e88b124b09", + "0x3c6bfda88175c987e50d5493db90b8469990300313bd2aa6398cc5182e3282e0", + "0x1b6a63c40981860fb35affdc78f993141c97cc42f9fb299b95ae58caaf1dd8c7", + "0x85765793b33218e027320687977acea0ee77ceba7fd8e0a7b59a7745bfc94a47", + "0xc5a88458a90379cafea8a968f59650090b27998d22266bd993b338dad32ce6e4", + "0x47a4f6329f4394f86d29006a60d9c9519ebd498e7112f54584eac8a61fd78306", + "0xe5aef48010d5a5f1a48eb6880b7bb8e71609604280073cc69277902927c42a35", + "0x70e60e9c71de2f0f448fcaaf22587ee09b1c39144efb4c58e32b6b2593fb03cc", + "0xe795e9c3208423ed159fef6c444bfcfb4230baf92869e987a1f74ea6a1b13e48", + "0x89ea2bdde16e19a72f29cec75fe310514c808db399e4df7635532b7a6eca2e07", + "0x117b9118b32aac2a5607243c1a2f8d5b62b963380cb5b7870cd3f70002c6b2a5", + "0x6c4bcf137fb189eb0d5b00a42639ef2eb83585681cd2e04d4335e4b1678da508", + "0xead9fcbae91ca16d0fec44923fde5de6f272a5cdf5bbde090b0d0b2fb60a7101", + "0xa06708ae673081ab91ec35c41b3a0fb632fe024e03a2be6e88b34a8082d3cd8b", + "0x19aa33e6dcc6debe02a3beb188088866f9ee7eccd672a5c997a2e0fc4f9f5a58", + "0x3860068ef68d867e0359043c2fd33f8897af66617cc46d63f2e29324738de800", + "0x5b0796f93526589c386c1ebdccac8256c2e48a476506da634d55049418ded009", + "0xb0fe480b416cad4a54fa8c82c7b30c00d21372d57b4d2d0384b5a26ae7d5e07d", + "0xf3c85771da821584862766883919de2d3cd6171469fea85800449ce997d57975", + "0x7c1affc2a365f09bb3572c61a75651da888faabf286500cc0d65184c0ebe4b62", + "0x6b622c234362fc16bb28ff2c20a073770d2ebe3bcbf4e8d21577e7674dab9fc2", + "0x8bd14b731babe70abc2d6895661c9b0633c930af78758633541d464ba1de2cd3", + "0x60f12988e1588b26bcef18837719a11fb8319d056879ddcf1efee9cf423310f6", + "0x56bdb7f029806dc7bf8517a8589debf7fdc6f233a2099fae6addec9ef5a4040b", + "0xa518e58d59b35a98d7649c9633156d8d07b274eea79a02f596de4ddbfd4282b7", + "0x896385e8c1d73e4ee4b587cf93e2c7435cfaa469b17c3606d27bd3f9dfa9a0c5", + "0x8a53f72a0dffc300d4647002eeb23ddfce6517e8709a2b87405d294b315fb29a", + "0x860bdfae122b398fe4b1102936c91225b1a82c262e57915ddb189876a85e3c1e", + "0x63be322ed6db541ddf4f698cf7f4d074f0b79bb0ec23cb9dea62f99e5f16de3a", + "0x6b926c4e31b3a4b423860a04685ed0ff570683c0ccbb9c972a77192f9adf511f", + "0x3dbcd9bb2bce5887c0f1987ed496c07e6befcb104ba72ae61bf7edeb460ecca7", + "0x08cf3a02fe2562b9d9bd3a5563afffbf3805f554318c2dfd942459ed3f7e1af1", + "0x45eda22f6141c30e1fe06b5beca5c2005739f216e089f535ed204c52cdea0dcb", + "0x2c520152a588d1f4051af7dcc3b5410f1cb8356c3f73dc1318704fe66c25781a", + "0x10d5f4fae352b4b6fa8234bade7cb4dd3f2e907529d05cb0df1bdf28751e0b97", + "0xa56a71befc35aea4adf0f2acccc91ae56636712679a81d79d2dd33a6934893d0", + "0xa1b381b88e8173543690c7cd3cf520e1daec29bc9491dfc790622703f6d58126", + "0x06c37f17c45460bd36978bf9f1c96280650d4d2f53997ca3cee24bc337f968ec", + "0x620f5f8feeaf8023783667b412b3c1af85aee3831a46278cf473a2aac004bff2", + "0x615cd645baa4efe5e45902efda0e9475d4a99acd53051963102744fbfb3c8b52", + "0x923336df74a9c95a1c4274d6776d2b1ac926cb22b9d20414b1b10016896f898b", + "0x63f332fbfcf6f4d9fde328dd57cb102d6c62dfc8c9a627f318f824656894bb3c", + "0x1a241cec062aa4760f24d979c119566e2c58874d9acfcfe0d9ec0f84077683f8", + "0xc6553528c8e8d80cf37c93f7f1d651a82ab95238c675749b68c41f15fb0cc45f", + "0x040836356e94790de8281e1e6885521f671fb88ddc11f675741249f56683a733", + "0x227ab9aff4ec56dd66db459f41cb21d5e124be79bb04ac007961da3140bcdbf7", + "0xe064daebbf363205a3b02c6f07e23a037d302249a01a615747a5ebb2eef7b5cb", + "0x20cba262c0dbd3faba6c8c7a6f16852dfedb13f4b2c86b13b45a525be6284e70", + "0x17024813bff2e4b42b3a8c95f13594851ab0ce5538d4264171d6c24879a89230", + "0x46c59551adb830b26825ab03f151df6ee6dd88d837abc9337b712123ac92b8b3", + "0x811fc6d18609043421693ab9ddf8cca96eb8376022cf80d6d40107ecbe155277", + "0x8822037df0d99811f65c9ac11628d7242e77c9d737bd8802159490d7bf7b272c", + "0x5ab578c3339cc6c0f01106f5e3fa4e86703b090c0b19cb6d3408a4c8e486fbe2", + "0x8dfe567e66f355c132ac731db9ae1fcf89a22680965aba1c84effef380ab000c", + "0xfae54a832d7afa2d82c234cc5f8ab76a43107d3a8e6a23f7befed9ef35609da1", + "0x3e2d48f334b97d58ee444d3832897089c9c8b1b752df117b86d1f7f93a11706e", + "0xe0eea8d18bc45f23d2485403c5ed23876172d449acf1ad6710e6f7c8e627f192", + "0xbb7a7b12d948edca259aa8275be6e4c68d3894f2266233ad3f0eb3a05fad4b22", + "0x711fa74450e1b0e82c3878c54d8fbec2aa7391e045b2da7d1f6dc87e90665cef", + "0xf4093d0503cf46b0f03ea8a98154da25b45416eeaef6e00ad62d3f3ccaf15f22", + "0x366033234907be64f43c71420a514cfcdabd55c0dbf4d7028787555d812d0aeb", + "0xd39cc541d1ffef7b4a00c67df34d452c166a066a454d0ca05502230e62fe05bf", + "0xd822e1e962cd8c08d80e3534cd3f9f304959023dff3e9ca9ab834a1c239badd7", + "0x9924fe0c317a7426790d797830398eefc7318616b03041af97ad5d94f049b310", + "0x09b219e01d1b9f3e4430cd15335a059d2d9be7f8837b74b233c6f4e239c39644", + "0x87924cf0095df629bc1e8ed1f0513d4feaafb646baa7726feeed239fa2f1b92c", + "0x1305ed5849809aa77c39d4b32e4f82ef5c15111ff818e6874815d50fd21d4a33", + "0xae48673803af579c5f97bec40e7e8be884072a99785d40cc9547bffe2699b1f8", + "0x224d58c3454bb6f58f949a37c76a8efe5d1ef3e495e228afd08b7baca9f67a82", + "0xd7c9af45ea3939616f86868de123684e5e2fae3089183b1b545409d44ace91e3", + "0x529113373cd7392251ad2de64ffd35a5a42003499a3920dee83c20ff67913485", + "0x3f5be61700eb2c9a505e426381ddf842a8e74e730a0b754e487f6bca83658d7e", + "0x699fa707646acc9d7ecdb833829f225ec3085e599789f70c20069b6c7bb1ef9c", + "0xd79ce6f129bcaa8e00445ddc2ae4ddfcf2d4e1a02f642b69eb6b974e99af0c3f", + "0xb8c2a6b1625292ed7e429258c4014f5f8606abf8f14463172111275f4e25eb2e", + "0xb1a4f7f70505afe05790997f79030204398b2d62bfec0b81079d3e0d2ddbd5f9", + "0x14e284eb864f6c046f8109089c1e45b695d58f8e08947ce87006b20e23d36d77", + "0x038ef09ceff251f5bc62147f87a156bba19036258db995681f477026655b6ea4", + "0x9b6264aac61a08b36991ab54c771cdbeaa5e27f8929b8051e37973e839556926", + "0x0a8b5cdd59895918c1d38997bcff9e59814463456340a5b5afc0a1b7a0de7947", + "0xbf30b37f511522cbc8028c737206c880c166c8e200ea4c12d49c501346752674", + "0xe10348ab9fdb75e446658b652160e745b324f33928b48fe06f2cb5ac6dbd5cf4", + "0x1df71d87aa50d5063c6935ea4a16edb7aaf473c4c4e09f72ae1bceddaab4a215", + "0xa493e86234e3c7047a766df1c8450c67f9bd7dc3bc7d9737bbd5b138c1b58c30", + "0xabcf1bb798fe6d1056b776b9cad278afc3774168c06e7cd5de4e4fdb7d4d15e4", + "0xe5f2958b752a771c82ef6f9e32c2313f7a5e872e19c6f6ac0336ec6278bcfba9", + "0x8e6bb7d04f42932a4b2929c6b38a7e6537a953b65b9a01549f87ff01a33cf846", + "0x6d300b842860292817cc698fce9fedd3afed0cd844ff36470089796619937802", + "0xc80b924802a1ef8f156a0cc7cd4d46a847e46ed4e278eed15e27aa75689b837b", + "0x96264b6423421bdf9e13717dc6b3ac2d74db523c079ecb520afb2fd4b6a9cbde", + "0x218836e90131a71beea82c2372c381ef9f29461b0e46cff9add02d5949130456", + "0x593b1a7e26778f1f6579080065461c18ce2a56e314f29e17ddd3b3d69f1da14e", + "0x5d8d0a909f51e1364d3366f697d0c3cfbf5f9248b2739897fc52eadccd554159", + "0x669e81c759a8de328ac6a026eb1bb7015f4c2e9f5018c098946b6409e19b7e7d", + "0x669e81c759a8de328ac6a026eb1bb7015f4c2e9f5018c098946b6409e19b7e7d", + "0x77586f4fd0c407fbfda4ba29a5919166b37e06b70b7b7f686f3b9107f1588ead", + "0xa80f28b6624adfcd7cfc950a56e4529c372caab7fc59c34483ef18960d2be97c", + "0x9522593b73900920bb4ee636bb0d3fc11af6d529fbb3e9a07192981209ede985", + "0x24c37622104d4d2d9ba9caa30036589f60b240b9f587a3a4d8fbf5bf5d0e148c", + "0x682648d8fcab7468c3868a4c0542816b057b31eb62a71c0a84e7a7585ffc2fa3", + "0x71042cafe175721e38b01ab81f57b1d3222de3b65c7bce6602ae6caa9e94e71a", + "0x7ad5486a5737428f661cf70ba68b0655565b9f48303a73b952f884f6be17f7b8", + "0xf9451b78480b8b8327a2cef192e6c65f35cb30c4dce6f5be0c895fff2951830c", + "0xad16060dba8706625e2845b2ec87340c554ef7d29a6809ed8f342ec4ae420cfb", + "0x20083883f870dfe8e267921cea37e959ed35af9d153924a62b0ccbe29656ecd4", + "0x728dafd0e361c532d2e9f39da99f9d310c88ce33486ad0c4d2c7a3edc5034fb9", + "0xf7d18b014662f9d557b0cf58999fdd428c02c52ac3e0394b35827b10942d191c", + "0xb5173ec878c6a73ce19c110a7864fe9db7ba277ece35a0f8f41051177348721d", + "0x05b512c52d093f35ac32da6d8d2f8c8f3fc51bde3ac4ad6df355c850a6992a3d", + "0x5684f80461d902c4c43d67e3273a4f3755dd5e7553a83cb68a9e37be785461da", + "0x9e7f1581b644c266de10907f8baec43cffc3ddb6eedf98b85b74f6758f17a8e9", + "0x1be98ac9a90b211f20f265def972a87721f23ab3a766e9ed60544ea0e63eac63", + "0x4182723b83937c6651bd76e27d5fd52ffac2c84de9f204bc9d14b8f7939b2f79", + "0x89cc8e29dc7c64921e96134d1437a5c42fc23158f25a3be2ce9dfb6bee2d0d19", + "0x067f418eae8c746e0b28cc6521c9f0a09a6e4645402fdf6b6a2483c9b391973c", + "0xfaaa1aebd11eabb91818a00eccadd3a2c6ec17e408c66351e6eb3a6638d79588", + "0x82b16ab69f573184809e83fecc418835d98fa3a930ec309dcf3a1b15a5bb604c", + "0xd4e77ab89d546a90cbe100d34f05bd6941c615482195114220dc23f24a31b79f", + "0x451dd7ee0b3a3e6218f92b972d86de2af359926ae00b48af0814cfe8a00df34e", + "0x77375fa34bf87dc94745605558bc7e1f5a69a269711d49ba34f3b980e21bf82c", + "0xe2affa0cc614e53700b057c91f406e8486714e43600e77eb989eb0bd0a7f2b51", + "0xa87a7a493fdf7e3dad07032891a5ad2858b3064ffbf20fae91608c878e420b6a", + "0x77ef85d1bee6f087788f3dd0eff12e6103e7516a84482e2e04ecc4a362bc5e31", + "0xf92cc867bf360890d018994bac9f4648bf7180ef7bdca46bcaa762a29fcb5eb0", + "0x78ba4aed96f4a5766b99e37f5772f15ab064f50951d2de6ec09104d48460d918", + "0xae8779e04253e47875b473a24c8581d9043c9fda9166d53bd04437f9dfa2bd5f", + "0x36184712e01c556fa421583105bd24ac754a8537254be24b8d30a0eb641d5b9f", + "0xfa254f9ff502680eaa78ab73db1684bd5ce6f602473e7d454fa22656afcc3695", + "0x5f4d9b6a3bbd4f988fc6e45adfe6c9b8976b4bd9031c6e9ac549d8eb35875629", + "0xb64c8adf383d1d7e3fb3433c4e4f4ccdcb5877b91eda9d2818093316ab45c354", + "0x1029514424ca012ce2529aa89753879854ddc0639d3ed2c340aa936f93bfffab", + "0xda6dbb9725d41b34af22c7a1d0096d80bddb86118e61ea343fe3543053e1590c", + "0x44ae26269c470171764d79538cf66cbb426d6c358303a6ecccf5f644d38dd24e", + "0x277023648c17ae2e4aea905597a52edce4d4e278cbe19c3445d2188b69996bdb", + "0xc15b345dcbe225c1d9c305ec497baf6bb3906b192aa40cd0e03bc570267a14e0", + "0xc40a7e2552fe6b4e9575861d949dc2a06716e80317783fe359677a4e85eec03d", + "0xf51e9ec68f10af7db4719aebbd2886f8592958b7f3b8d4ce11c19cc83330c17f", + "0xb01cd5eb7cc44d38f8955dd669b952c7195767d18f681530a0812bd4efca87be", + "0xb70f81b0f0d433649cd5cb22e4d2fd6c9058002749cf45408e766913f265980d", + "0x706c76c9c5144eeac22551cf49c1463bb35c9f19ef1d4073f4c1d34162456e56", + "0x0ac80dd380ed6959cb9d9a7b1b55429f0bcaaa55b63e74e0eef68b1ce9066a6b", + "0x0cbb2dba432ec6089afcdfe8edd0da2555077caa7e8f3f13ef115800e73a96fc", + "0x77356a36ad38298019d0f0c1b6d6903d9d64d023b0763d36a31f29f7c09dc4c6", + "0xd4ba62af3c0f390f567ca29fdbfc0c4ec8dac9a535507c2f17f8d4d910816b34", + "0xc4c67ec7c24dff282977866f093f4f23059aa1ff187714af5d720ce6041c5318", + "0x589e28b95f3b56cf13e45ffee0240dd5f6be24b73f4963d96882d63315a4ead8", + "0x58bad1bce0c512f4453038bd450035e53f431f6ea261ee8173a83f74d3e71ff3", + "0x272582201c6df47aa01f5eed921d40e536197945502f15910e8493a65ec6c530", + "0x02a4df7e81532cfd041c8d46a7ad4c7c57938162c760e63a17fe0a8137cf34ea", + "0x8de4ef95931ae60a56cc225cf4e93fe938e9731c6b0a285c7a90e708c441b2d1", + "0xeeedf5eac837edf0baf5c5a734664525ba86100db6979879e27d62cd6cc0b033", + "0xbf1bf7af1a64ca47ac0a98deac80b261ef8bed3fbecf7936adcd667ff38bf690", + "0x46ddcba3719bd5d18e110b1f7e95438b4ef51179982e77056359cd3f1fec5384", + "0x29888a2903700b90664bda4f3287e9a23df868ae8abbbad4e89f94b8cfea0195", + "0x7aa082f3e042ec8bdc0e88c92b7342f9c38aa4be3d9b210201a0f8fa1b3465a9", + "0x1a7e2fba6a4ef703cb726f951ed35ff4c606482f88d57a51a5c235aa1f206ecc", + "0xc780332e4a44572e271f6506d9f1cfb27e94ded6d53d344d4fb16e5727704c4d", + "0xbc0cd0e3828f3aa2b77e9d36ff67251aa62e40e405f5e1381959cbe627b29281", + "0x082379868b6a9e475c083cea6e7aca92fe4b0007d6acdb2aeb28670445894683", + "0x9b960864c734aa00c848d8e431b334301dfb8e5f46b71d72a485c43255a6e285", + "0xb6a1d7f3f2f0a6834c54a49e3edab51ed2614843fbf939357dedf34070dca805", + "0xef5b1a4dc4d3f5379138e237daf8724c055390b8c19fdf89a89fb420493be925", + "0x09a0f929e9d53d0823c2ccb310253fd73187ef8bc84a63c84137b26dc19f3b6e", + "0x2247891812b5c4015cb1d2ca421320c008df035a853c67125be683dd321cef8b", + "0xb33d0a7c2b7763d58c5b6650e4a7c0ae2e13a8b2a3cbe3b23fc49f356daa3d3d", + "0xc41e712a12aa356bc99fa7eeea985d65cea2ddb3bf8ed14d9d07cddb56fed603", + "0xf555a4fad755bfa6ba230f37a9aa09cd96480770b4d010fa207c4bef6e44a772", + "0x3cdf721b9f4d99eaef048c369ccc93b45c6bbb31f7e64449e6b6682fd0e3bc62", + "0xb8715a546d71ede7d5a5910c2a46699f3d1b86c35640d13e03ac543e879ae9f9", + "0x8b06c3b5adeb0e247983d651366ba0bc293e0c26f402250ada5794d55853277d", + "0x474f8797089944ba52b8b8458c517fe8d20dfe5d322ec2f0b07ef928e698f1ee", + "0x9110df8f40cec100e2b17ce1151d9120068a05575cb6a2a3f2a2b4e64e42d43d", + "0xa74be70ab29b44feb9f32da940b4ae2aeda742483c0a340f5168b1cee67683d6", + "0xffc7137a5c14b1ff4ae293b0ca2dde4a0006f0ae851db4e83a017ed68b9e82d4", + "0x82313ac80e0e364db896ea1e69324687c7a59c7e7851487c775a84c95242775a", + "0x1121ed2b6c32c2e53a579a88c66e9186ada46f4f35b68350f15fbe3278a41b9f", + "0xc362b4cfd10a42b2ff86172e60afdd398905166f0ee98122b16062b6bbc39573", + "0xe2f568a190ab5329c11591355530368ccd9c397feb7da4acb9fc360d9a5be4c1", + "0x7e118e59a6cf91f8e911bc8bda0c933c2743388a1ae8fa9d692a62216ef0121f", + "0x46230ea1112d22d670a62fc52d44a6a9b4c860d3b7fd260a9aab03adeb97d0db", + "0xcdc114742e6cd3dccc61b499ab7716987d4aa6181edcb4ac949cbe915fd52c87", + "0x74841c3ed623e3298c6bec9458675fd4dd441dd8b58e3860e2a12de01a4ec03d", + "0x307e2d35d0be77b0d3b4e341cf008f697a2b96628c92cb508b1741e2af367814", + "0x439490532f82d019044779e512102174f607f2beaa86f0def9439e2e453a415f", + "0x1d6cf6c656b74393fc735ee04a1c4aff78823433ebce2b5ebec214988416af55", + "0xb9dd27fc785ae9829f200fd14dd806ed2c57231b9b1dbe9953630dbd94e1a41a", + "0xf73b12083b66e2111c7ca2c62774102a7c9197085e3efa972c5443c925aa7bf0", + "0xf6a9ac7be97df3f7e16d3bab0e4b09ce8f0de416e19d9a5ec7fe51f11a3500b3", + "0x8b6f956fde556032e9818aa210c315e0b63b165e21acc9e4b489f1b8e9de925d", + "0x4d0ee6c31e3bd0d5ad18f27c036c8698fcaee420d4a6176f80e8bd2f13931f07", + "0xa34fc76fc08eab52582e41035cc00ec905b3450e18edc417d8b57e1073e213ca", + "0xb9391938c9b63871e0b024a692a7fd726b9577d7b039d771ba23911ff8126729", + "0x9ad41f4abe66aed67c31e5e58155ea0084c07b157920f9aeff3eda3f0570ef74", + "0xdcb182f78262163eb16f0f420916384e78ade6feab81d16864fab9ff2bc83f22", + "0xabc1d560365344eae130e2ae7fba56e5c184750cf7d802d240a680039343a3ec", + "0xa9c02d6d42b3c9066205d5663e7b6c2f2a91bd659dc04ab3d40ec0d3a3399e77", + "0x780916cef301dec3938d3c055ec070c7f58d51bf7703eedcd8e6b7b3d07c22f8", + "0xfbf76cbbfa30a1c0d45f8b0de4422d1144d9020167a5c765ca981abb0d749b42", + "0xe0742855e0428ab1e9d23d8f2ddcea89617963d91d6eafa956a99763a2a63b45", + "0x842dd8e870a16ff8f882c74f3c51fde29fbca27dce9d2a44d9c4ec633d0f4742", + "0x8fa2aa3dbcd5bf2be1b07b1bfc414add1099eb3f4578e60fa1aab286828b2a5b", + "0xa9e73c56dcbfce20607934c082a4078b9c1f7fd45cf0397004320852a091388f", + "0xbb6051b855d9b64f27786b31d01b01eca1f40dd0748fd653d1bdb1c2fa29ef53", + "0xc81fcee2df3dd27bc016d15d3ac92adf353880912e3193de3f761008009e5a55", + "0x3c12d3b25bbe6baeba0689c61ae528691b36ec0deb2950883c780735e0b4f5e4", + "0x90a94722fca8f7fc8743f630fabb89a8fe322c68befeaaa9920ec67175923c62", + "0xfba2a59ac875a9245efaad77179a8a78536174694494beee2d6c3d4cc89e2ffb", + "0x2714d5dc5e49719dc7ec285f07300e461dcab91d2aea212e75f927d7c05ce23c", + "0x17af48c2804c1a813dad6536ddb87dfa12ef763d1cc917fcc359a3493a4f0e07", + "0xda67255157e02509dd32b5f6420d0234b8a93b8ae3598e348026a945ea325709", + "0xf5d408d4627a470a19d7fee5c3c2da2bff738891792faf3917dc7560cb0e00c9", + "0x963049bc133db0a863b2f70c303cfdc31e694425be708179df0b815e7f31c551", + "0x3b2f31493a91b75ea553e60b6487aa7365e8dbd572a634d4d6b876eaee24f431", + "0x2e920b0ee4cb83ee5299064f3fd25c7fd395ffe116d5accf9fe2d5223c65196c", + "0x7b755f9b536c1e957e116b795652e9b50828754a029cffb2e7843c3159202a2d", + "0x3f7155573ef53d55b173085ab4362e3b926d12c7d4b1e1b9cca29e9ed4558e41", + "0xa71657389bd15d059a4660d3605906659b6c513221c1149771256517087cf359", + "0x041a1f8ff045376c7dcfa9002259905c6b066cf39788838aa6debb75daef08af", + "0x77350205450de5307a90bd8718214f632c7d923a5f38697351c1cad109ffc021", + "0x80948adf2632a21c7c130df39a753700f6033c2f9dda397156ffa5f86d3cc7df", + "0xc1a28e35ad794634fefcf41851de7b057e7d144db63d6ab242cb75071257bf19", + "0x490ff6f81fc9a6e2b257d7ede40b13cf52e90c774e8698d1ccd0c2a4662c2991", + "0x33571cbb1920a89ca714ae11085bea9eae70ac56c517e138eb564cff03d12315", + "0x798c79c1db4d50c33f64845bee9939d95a7ea1075a611541cbf8b48a31fcf047", + "0xa622402ff4b80df5b6ca05fb238aed3f86e18aa10df5ebb12fc42b00cd080f68", + "0x92fb22cb067062027a9ac7fb79eaed4f6e372cf02c209efade5f54f83f602d84", + "0x5f9760701bc78786a898a16423b6a949d199f5986ec4456611659048b2408bdd", + "0xa7a9f3458f51a4adacda03b77c1de3ab04bebfe0abc5d0013b03c7d6ae6bc98a", + "0xd4b660ecc62ef61204c1f72783e59068a1c2b4b4e50ba337c8596db6e204b75c", + "0x018cc73f6f6e3c3dc5a71942896565fd3e5dea85810a6932b03c0983b3699a27", + "0x784068d4f7a8110564214344d36f8ad2f6feeffdcac249433d88bfd43e494145", + "0x184780e295e15cfdc217e69a0eefe753205742b7837527c6b672a236853f207c", + "0x178ed873a084b80b0a24c5df430fc8125a01b91b27b5b0d7e3e29080125f8aed", + "0x57e192b1ff7c3e897392c5b016c35de91d579cd69d7f9aa1305a6026f49fb4fd", + "0x85270f0af37db2d55945c345183885899b2b593698627593c6714e93c2f0f8f5", + "0x7c4a94a0946a658d69c638ce67b2d7715890c94c5e1c24414e5f791b4959f4fe", + "0x5f9d2c39e246e447a471417e6f029c600630d5a50d197696b0de479fb4b194cd", + "0xc0188578297e349beab4322f674c6eda58e1be27101f6f1a91edb71906a71bd9", + "0xd5f0f75e9ca2d596095cad6e5385cdc8a0601ec2cebe8230677a0f77c57129de", + "0xd65777c1807b0e3c772193af9c7a01c0c0e5cb89f4f24dc93bc5b5ca8ea0a9b0", + "0x9bb30c4d1c5974acb70b5a853a63fa307efc021001b68ddf820e2d886298586b", + "0xe04f109653a773519b7bacf2bfa9685f7f5f6df867d247418543b5b2f10e55a0", + "0xa51c7c7cbebd8607377562cae0730a409eb776549c2b835c0cbb71d5ebe827c2", + "0xec64be167f81f60bc14348cad9e128ee9fca272cba21c76b6cb9f8a466481d5f", + "0x03a415a9662b32c1e11b9a066693d7ad49d38680089a95cce5d9731f9a501e2a", + "0x291976fe61fd155b0cdbb781818a1db40261f2e5415bcd9269e330d8551b0d8c", + "0xb5f4c5832646448964416e1ed6054beb279e3de9b121b89ef029d9747e784edb", + "0xdcc4182349aa114e6016f2d8e234a395cefe0538a3fc76e3600797280e82d8a7", + "0x6dc3f6d424dc4e2ad1437fb0a7ee93c0cb26bf4b98f8d3ba8efe08f182681a84", + "0x9f085947e3b79aaaea4a1f34c3607ef8423b1829eaf0ca96957933f48231e7cb", + "0x3300f28679c561469672ed32412a30c866f2777f2077ea350cc0c36341df4e17", + "0x835376dc69ab083276bf6843503034068395e2d3eae9539efb55266ecb21591b", + "0xf7e9abbc58f50b6060452a43ec49d7a02b07288045673be420a8fcfc551e35ff", + "0x2d003f44d33b28d46722cf7c762bb7b14908fdb505d9dc380f9ad4c1db93758b", + "0x5efe41dcf3c066af02e8bb8d3a52eb928565b4ee94ff5aa2cc8764c06500327e", + "0x1b377b8b35aca4ac77842255f218998b074075a06dac8b55778a8624d3309d9d", + "0x68a60e8ff42be5906858af15a9a9d7e4b587823ab135ba195dbc2ddd6eee81d8", + "0x827de58fc1a270abc800c93839c4282b13b871677eb2166ecf127d65daa75ced", + "0xd9419a8a3aa7044e85c220bf0e3b32cc882571ef40a7f6321b8b68710d161e0a", + "0xffc9a2426558c93ca686e62bb6569c6488c86e60142e4f00aa43afd50d7c1399", + "0xa1a9c06f6e0e2d97b498d76509737dc62d4f0abf21611b6bc12126254824edfa", + "0x04c446287820a8c8b2ed0f6f3a03b164b2dcbae592800b89d7193dd571a52e16", + "0x2d9b2ecb83416d15e9153cf40bb61cbf94acdcb7f3de2f57996a399f82795fdf", + "0xea3e482cb4da0799b41ac28f9375205b7ad05582a5ec41b3b896d7f9a841b70d", + "0x6caf11e74fb8c5864f90c33058f87c49326fad4cd0428f0c65244f705da11464", + "0x615e2af91de61a45f3736cb065bf741447fdb7395ff1bcbc72743cf7bcfd265d", + "0x51cf425168e853154f952bd94c9c54621349084a59559b2a313c41ea26f7b9b1", + "0x77ab7d853df0a3070b95241f10325ea3f8021cdaa27c2cb19ca65eac2a726008", + "0xa155072bfcf1b35e5f9e06d58d47635080133b1d10edd98e7a46673e3474b884", + "0xbff79b3bc51b5de6361b890fd23e40becfbf1964af8f5106571ba513c0430aca", + "0x843cacd2ba2be635553f4ede644435a6613a6aa84a0b50b4612ca4f2f4b21187", + "0xe76a4ddcb05c6fdd474a1fe9c4215da05f97b9fcf8781e620007cf85d4298888", + "0xb7af25104227596255c520c6227ad96a72a09a5822ffc1cfeb949ab2308ac8fa", + "0xc244d70ce384565e08431e2128965623617a2d5c5a44f964c38223f632cbb369", + "0x180d5df2b80c77d48b7dce8cf3cb52e3ea92586051d8f164a2312586494542d5", + "0xcc305a7cdbff0fd46b4116bd246e71dbece683db228ecd24a7d0a8d2f4a61fe3", + "0x791bfc2bc5f5a82982403f98c3e86d9c8ff784973dbbcb65e1c58287e1112d49", + "0x2c73ae44e0f8ce5f1bac7129f0623a6c6af7fa3fda4b88e511c44cd82bcd19d3", + "0x49ab59ce2686aacd566abc3372bb2848ad29494475708a2bcea15113ee48fcd7", + "0xc0a594efc9256036ac568377e811f739e35b6928ec7705e10118cecfedddc7ef", + "0xa3a3d3b969ec126c673f4a2f9f4946963d2edd51f95f334b63222ed3ab47db4e", + "0x572eecdbee814fcf91799906ff26e4571757fb9274ab44527fd25cdb682d458e", + "0x5bb2b8048a99755d62b1450036d9ff452352e10c1de702262383bdf045c18182", + "0xe8539dd14a67cba8f37ae0e3b20946484726f1b6f1aac3e8d013b966df5e4306", + "0x53d0894476f28cffe8679099cd3c5f2a1cf79a995537673e38c7a969bc9075c5", + "0xfd7ee0f4a808f05948647dc5ddf37bc39bd13d4f69675805ee4782fa5e90282c", + "0x0ef5bbbb50a3d4ede942eaa03d03c6f2ebf5ea909b789d09fdc0d8fb45a38119", + "0x7d2314a47e501b50c5b6c00e98a160f833590efbd47dc6e32f62dbbb32bdd627", + "0xee1c03b730eca7286038b0c833f9e9782c4bc55d88ac1f88104825637e395237", + "0x79e74e7d4b2cb9e371d27b4e39868ad04bdcb0046b8b942d1a3ba49ade1a58d2", + "0x233806e7c2eb7f3d1b172a89b51897066d78e90ddfd9bd6e31a51acf9a25d177", + "0x832806b251040adcdf8de18142ea6631d4301e55fb93a52ffba02fffcc86ff8d", + "0xc50ad523e66de83b698b168a574875e0a80e89f790758ca2aa2f80119b5aeacd", + "0x5c7ec607ee3b8cb2289909ae8cc06829d18b50661ed20a9e1b465a21733abc08", + "0x78f59e43843555f90e67ed1fb36ffc72c76a9f8d37502292a1e9c29b4144a4b9", + "0xf0895140d9a1ebe4b0d355ea2fa1f614abe60ca9d67b4d92983c1d9186dc82ba", + "0x62a287af84cdda31b32dacaedd875d7dcb251d4f9011235eada43eb008b2e868", + "0xce77017a9da34b2f547240e4d6a24dcdd9b4df26bb21a69a19c2e8299834d834", + "0x85af10b8d8f46730ba591400d8295e58f86fc4838d2101fd12fbb20791218071", + "0x3a064031f218b836870b0dac0104b704c27008093be9f215c6154ef9f8047120", + "0xcf96155b6bd921c125ab37c38d4550fb943d1613326ebd9344162f64f6e8fd01", + "0xe1d69b357487bcfe17c9b0830360e86b6c8eef0c5fd900d221032007d5ec8fb7", + "0x3eaa5b2d76b665c4cc906dedea39c6174b41277761d95fcb491b19449dd99f02", + "0x2a3879ebe684593ab04da81c6ad82faeaebbac121d12b93d2e6ed53cbeae96e1", + "0x18f4a8fdae299becf83ed826618e00193694dc0a2cda49993b386a77e96ab40e", + "0xeed846e1a3a4d64fa5d0813272298a54c4462c95e60f9747c0e7f79e94bf2ef0", + "0x46ff67a4bc69c8c9b1b97ef4d4e8f352cf6d5d18a220ee44127ad3c71fff8091", + "0x9cb4c5aece0d31e90933c28143224dadd90634e6ed8420b8feeedb5bc6a8fbe8", + "0xba37fd64b2a9595cd233b1375146464e0d2239cd94ea65ebc396f311e22cc8b5", + "0x598bf18f8f4a2bd2dabca1ee8cab4634d925dee5d29f4aaf7c74b9ff621334da", + "0x935c0b68a44cafcf1f84c8f80d96ba3d11e3faef7ef1243c306da0d7280e871b", + "0x868636c067aa5c3230d4b4bfdcecfd9235a10c37870fdfb8550a5379b9120164", + "0xfd7f404eda5014935c321c499bcc4132c37f0fa77b40caf985aee150b30249c4", + "0x33980fd01a63ad3c0bcee5bc0ca4f37c5d34233b7573f4be24b11bf1737fb7dd", + "0x36667446f928f023810ebd26510f95effafcda3e690d9160d5dabdeac455ac0b", + "0xc8275bf87d874512e4c6a21e4effc925a5dd057ce71f503eb23799f1f5029508", + "0x5a8360aa7057bc7b895950306c4c15466536cbc6f9e44a45743e7cbf4ec027f6", + "0xe07ac5f6bc55de44060dd2fde1db00d3d8e2c132b06b15291fbb2cc88d219157", + "0x00f72c4ee20aefcc9886ad8534d42cc74be2b6a4caaa483d9e16daaaee7643e1", + "0x14fa8d0e3ca7fa824f5c96524d78a4e605fb02d107526b0be367a177ab8120f6", + "0x54e0736f8c84f8046bba6c086784f8bd1a42d4df04327b5bb1c9d8468930f0b0", + "0xfdd26be191712bf654935324fa673f501092ae0feb1f3b75c055765a4dbf04c0", + "0x3399716fab2e9493ed2a343a0815fb11672077c706c418261bf8521727694512", + "0x7b3107b83d3951979a0d99975dd06756db1150c72a7e0e353fb760607d52881b", + "0xed9b2844d43761fe49128b8f6dfb11e803bcc079f538737721396c0269d6f95f", + "0xeaaca86623e0bfe57137f4e06bba6a106bf383f13e0c54487a30e31cb3d48fcf", + "0x2f97c0076a3abd84ffb73f7540cf221b8d22b334a7da2745052a8a1592664b9c", + "0x39a591cfde91fad5bf675b014e0ee6edff836ae0174a159fc2ffb591c232b499", + "0x25e6e73d7773a3483e2d1a6b3c7157a41b1c105ce0d5dc4956e064300fbfd877", + "0x1f6f9d5d66b08280d3a67220fbb217f9c1024c3301a68665afa58bd3d5d9434f", + "0x18c9d7a1a93ad6b338178805f4ab23bd478a5e1982b30c95f135ca2e262a37b7", + "0x00733735c485e734b14403af2160066c641bee0f94273a1f0a5d89204907e749", + "0x170210816f03873dffdcc8333b9fa4f22c05f6098d17dfa2f9727620236aace5", + "0xc759b8ea571ece7d46bb283802566d62cb5315da888ef2180b7a203e566ca6e8", + "0x32ada835a037957cb5b5ff3ca8f89a7a2a2b896a793e1b34f4b199b59390660e", + "0x29c6d25d547c580469d85b24683a032c709edcebbd5ad6214a239ad2a9383ce4", + "0x785d98dc729fb7acb8841790c0362af9ab333c95ea0c0f5f63a9ff59e35534ff", + "0x2d62108142e5da42043ab95cbdb6f4fa1d7c74a3990cf2aa6fdb19ada7303797", + "0x4b81cae93a6ff79b16af3f7814850d9d300ce11f321dc968bd723d3d01e6c032", + "0xd1e8d4ad5fe6487855f7d470f2e15ef49cb94b45a67ef8702ec62637c3d8be01", + "0x7740d028f9de66244266b9f6348f92d44220d04e837239dcbdb70d854e6d313d", + "0x55beb3200a54cab7bbb6cfef515c814a7d27926b3df7f8cb0c64f10311ef1d9b", + "0x73d23d9e2c567910729e6c4915286bf9e3afffa14991c53b4f6a0026f3680f71", + "0x92f2276dafcab360e90dca17176efafc300d25eabc67b4d2cb7051a844e95488", + "0x7a7ea5b69c32b002e8b42df9d89d468337e09c3dce51f857ff2b3bd6de920554", + "0xf264237c4b293ce9d2f71f98c33632416ffeb63035619e5da75c0b1067bb2a9f", + "0xe8d297f771f0c6eddc7e93d3fe3fcad48e31d6fc3117f6f56610118dfaabf787", + "0x4e41bcadffd48361c7ce3cb7280cf13c72469047c98eeb2eed8de6d9636a218e", + "0xa82a670df073963e26ee9e09a19871490cf81e2ba3cb3fac148e29585a78406a", + "0x1e983e89901b90f6e08fffac767f626ec000c11885ae1ce5d4926889fff39ffb", + "0x5531e2a5b4e863d611f045c12bdc9b311cde4a787bbb4005fb0d290684e91e59", + "0x068e8f888bd060ee8a119eda484bec1a3a37da143f8ae3884fccd0b18f43e842", + "0x04cd1a1dab2cdd8d81b75fc70a35afc72548725c9b6a29bd2f4c236b9b4848dd", + "0x0aeaf9f278e8ae52c16da83b733cc0f308b88a6d970298e2ce48211ef2be0375", + "0x04e74ac34c4f7e92c75c1723218d1732e1ff982663588acc97321705bb03d1d5", + "0x381cf585f395330b861f572924f47db051b50b02571e49914efc54a746386bf9", + "0x9e4f2518df2fe6cae1c9dc639fc67f8f282a807c0ae93e4c540bdbee35465c48", + "0x49876f5fdd25bbf1207ad898c062a255181f82bc9a80af53325e1b90e2cf396f", + "0x4ab6c5a16ef461392e59f7041063bfb396be56addcdccaebe86b8de35b196c14", + "0x658ea6691782dbf7f84c4704dca46df17b33337a9207957d1942745ef4a90f99", + "0x451d27fbd7f074f97c15212c36c216691b0e50cecbe6683211ecb008ce246c26", + "0xe397e2f380d306e5943e99f2f66f66ea206c165a75f282b307a0a44ed275f64a", + "0x3609e8a5a34211d73f36a0c61a75a0b123f15ed11f70d35e7cbde6c768db22d5", + "0x67786e43c3044e0f71715e8bf8d6f215cefd0aefc81a168e17c9e77576c5ae45", + "0xe8633dd14a7f2f303fb495a92ffa9fd8172e89a3578f213cb04c77da2477f7c4", + "0x18696243e2cd60fa150f8fa2f9a091f0be3d7f7fac466c8b17b505352e86a48a", + "0xf3fa6dacfd10fd77bdd0bd31cb65e6e34076ba7a0d8d611a16708bc15af0cb9c", + "0xe4c65797d78c7b0285d1b621aff50e23126d31991a62dc81b178fd092f89c42e", + "0x0ddc8d3694b46dee36160875fe6ddc40c35dadac57916cda69a496fe44f7d00e", + "0xc1c8b080af435c9fbdb09984c8e095cc2fb6790e6c29093bfa897d09893be9c6", + "0xecf1fcb84193a6ba0838258ccea066318ac296bf815dbb9473f10536ff1686ce", + "0xd1b6a7907e45cdf2020c859a066248f4af31fe8a0bab09e357fff5b1cca47213", + "0x77012b9a39e6c0931ae9aab7cb98920c1606e762f39f07e493f9f819d5ca73ea", + "0xaa466114f9ad05ca770f8492744e14a9302835ed70e39e5e3c181b44fb12a8e8", + "0x4a9cd5a07f758797dcd8435ec78b023b6cd0c364c15199084e28ad842de2fc45", + "0xf94cef08d9b8d45c0bacb3983cb90b7b08189e3065ce23af476574012e79e98d", + "0x44be8a39c16e5fbeee3c2841eff841bc2443e116ab64a33f85a498dcb01a2e57", + "0x8570c4e4e0c00f57ab8cfc1bbc924ce57ba6365fa21c4a92df0ead89603e86fd", + "0x24945deb0223010987176a8cf2b21c0e5ad9b418b747660bdaaaf4368944abd4", + "0x6ebf1b7e12468f94036b7087ca20815cf017458a6331ef11ab0dfddf0cf316a0", + "0xf4f2d907a2ba196e12a8a3e3c28d0ef797c3786d16c529e6b135d9907e29e551", + "0xf3aba8ff3c64f355abdf4eb595fcc5017b3cd7b2434f631f4a586becd4d71fe0", + "0x5e50eca6df87f17e09cebf12cc4b899367c7fbb3f261ee4593e641a7ace84095", + "0x458ecc89137fc988bde5ac7b127aa031313ba4d61f549cd2ce9b48d250249c1c", + "0x41d38af16ef975ffa16fb3debd4486ff24148825fa6382c3be8d6c3515f5fbd5", + "0xfa3821b8fe918b288046b463184531c3c27fec0aaeac65e0e229c0f01fd5aafd", + "0x8f3679c5d1c0765c9a1f83a3e6afc2fa7fd0422fda81f73f8e2275872db29d15", + "0x46bcc8ab98dbdf7e291409fa3f6bd02caf4c36b32f838df5324d57685dc7de1d", + "0x2ea880a717efa5fb46f9884df442ef25b897a44bc12755ec313d56248d021975", + "0xb9e45d6b2abdcebb39bfe39affdfd0f6fcede6981e7d7bd4181cb48f6f353abb", + "0x9cf7fc6e66350fdc8c2e991ae534b98ac210ae33d4e9a09c8cc7236c695f2ece", + "0x062b84233cc1df40fece8ce79ed521c2fdc5eecab6b09b02b57b1204f9b15372", + "0x2a5412dc7b35685c147b8768382b97347b12cc769f2ab791a1c88719be411634", + "0xd0305a249ef168512d930bd2fb2c052abc5620c245af5f2ba282a4156ed6591c", + "0xb964466a1e09bcd91bd4b4c82f99617720053adf82b7cb0f8654f274ebe7ffa3", + "0xe01d5b89756751e7fa1ad351e56f8613baa2fa4eab644ec3cdfceb03464408a3", + "0x22b6749ede4f0201031da3964c02288c1ce23fdae2fccedaf177075349b6aad3", + "0x07b3d5f25e7fb64db37aff9e9e7bc6ff3831b6a022c9b4dec051833b8c9a9987", + "0xe863e63d94ea625f09d0ecc542b97a048dde7db107fb3640cd88130420b3a854", + "0x0322ed4d31a5599f23144d504835c2d4f1db475d7fc68e4282af623908d57a6d", + "0x81d78d1bcdd72cb2aed5ba6630c200a06e5eaeaf0a0b06fa87904d505e07a505", + "0xc3d3cb266a47e230c9ed8f4f6a1a55338b5bb68a76442c0be453d923824e8ff8", + "0x9d30ccc46f241d302e23f71dd71d9153d24c66a054a157069b1d8663edb71fc7", + "0x2de8505b8380aecd8063d7a5af1440317e9c76dd32386798c2ef200123b6bd64", + "0xe8429a4fa26cd08bb5f75b450911e6c67f66818864e8be915df3e0899cf68ca7", + "0xf7d0b4fe3c836f0d57c76659cd0f3a1974c3cd7ebaa051f59543942486ae4af7", + "0xe9a37b29fab477ad859e6e0bf1cc902ffb55f2107dc52052f28dc44958aa194f", + "0xba37c8b77f572095998beeb09f5ef14374b9a6c82cae0a4f5591a66cf98ebc49", + "0x5067e312bb33e7da091405316ae9bfa2ec09333eff20428ebeacbe87fd3c8fa7", + "0x4b2c613e1ee7c50f116a2262991612eb1274ccdf558c70fec009f6305bf3ccab", + "0xbe5495812d0050f97d572c57330fdf4e1c46a49293e003249893d11f33ce004f", + "0xbb3bd19ce282a8f4603d57f28c3ea103d731ef40455bb64624735984a74f6fbe", + "0x5c920605aa9c5c815526e802d632d4cd6721bdbc01d660abce09aaa9b383ea1c", + "0x96f1434a3767ab4cd35bcca1b0cd369f4af9b074a3e25d0183b7ac87f6d98510", + "0x2d4e586301d21528743c321208255e7081cf3a5842aa3150465e5316a14807da", + "0xe0f52bd907981dc9e722ddb2bb956e12f9a2e83fc8efeadbdf538ff9d99cd101", + "0x38189d2c70a3155098ddfd4dacb13832e9925b561a2ff742135cd896c4b68088", + "0xa3a3482d6630093d5fd0272f4068ef917e258c983b270c2414d11d9c4f0a3fa1", + "0x114779518419888ab4e884e4a369da1d6dca7e5f7d9d162b101b885d6b556630", + "0x7d61ba2cb1ad6d6fc46324894b8827668112cb6a3e0edb6439aee36b9027b7be", + "0x0cb4379b032033e2e3e9a62a5a5a2a3c2456ea4712c0af20295d82ca0f92187f", + "0xd73ea5b864e05e4414c940ae69a7109caeec44332616530d7edd510ad4704bce", + "0x42eb6e751f61214dd372132c0dbb0bc4d29c00cfbe6f2d2c0154c5c45b759544", + "0x63e53902f589c82212584fde8665fd1c43b54764d21b4a46c7eb1fb3a191ee69", + "0x52d62658500c287d0ebdaba8539c953fcea3de368c249e0289358efa3a0a7b24", + "0xbf6d9fcf10ddaec7ea0a31aadfd9ebd529c45a798c6323514f169177fb0a3d13", + "0xdcc21b4d106c28eb31f9a9d946b16add6fbc06673a708a78f0aedb0a80067a35", + "0xb925d7020370d0dc5ac415e88589b90b7b11c9113b40238a87f4524165c61594", + "0x763c0a50b25af735f494f49d711edf537e59308680c7db2ae6b26a8ca54d459f", + "0x7212eeb3e86413478ec5970963a8a1c9a95c78138b1bcf30c8c11862fe2c4c5e", + "0xe653999d9b2ba853e1742398b6598b515d7002c1dfcc96945585629dad7859ef", + "0x2f3d0e47d03b793510eee85884f78f9a686da9d0b96be3b9355b02ff408807b9", + "0xd1cb2b6afa6c621a151033e09533ecd614031f0a9a2d8d76235c135e1a66c10a", + "0xd5c71d69f04804d4ffaa8103170aa969c3026eeab51fa8021391533c6e5a1df7", + "0xb5f5b5754122e16eb14e7af185c1e92709a12c2d73cfafd80228cb5624499885", + "0x4361f298926ca55f26eb20951c4c769c7d298b5f4530edac65b2ec81200be0a6", + "0x1a48b3259b51a3aff355c6a1f6a2a467a2c61eb2b18c64c3846d2df4f507c7f6", + "0xceb11a5a5815ab07d785a3995053099682426640ee536436851ddde91e1a3832", + "0x033c8b1e3a833f27a38ad8bc2d6e22d80c79ccabf787ec6fae2ac0e41836003c", + "0x5c57e3400a8a59b611122b55505bbe8b1f5460dc3e88657a43abe15dbaf572b4", + "0xc744a533f9f858e81fcc2180286b661644151ad191d271a9506109e426b9dc92", + "0x8c7a3e03d3575d872b231cf1ab89220033bb9072e07b2c965e864c3258274bd4", + "0x7e7c792896b150607d9c5de01479c3af3ade8a301c86d44d02c02b14946360f7", + "0xb6cb65461a2f3eac8bd32bfaf6b13e640976250a2f225a6a2e4bb42d4f312aa6", + "0x6f4f7a9869634f2f16b13cd0549fe92ccce864147deb7168c23d1fe11944a5bb", + "0x2f748dd6b8f43a076c82cd4a27a36b68fcbb5428cbece1a29f3d121fd37b2ad6", + "0x3bc5fdb95b38398352852b6d661c9e54744f51039c1736ea3811debe42df825f", + "0xb4b389e17820bbea1a9109beae3a18979e899ae024a1029d1efb7c1ff8ad7161", + "0xb8f34fcd26a4b0aaa728557aa596ef7e6250f6cf288cc31a8e26c8b9142eed93", + "0x5aef18d92f64ce8160849108bc0fed66eb4840d533bf3cf43a67299ce642c37c", + "0xb99158495ff59ad89f19e14b236c2dadfd3de245e00d711f2113dced16ae0ac6", + "0xe44a35eed6165556a7a0dc0dce870d66072fae233afc38af561cb94a1c97b5de", + "0x5d7dd31237c24c5219a5065e5465fbea6c921adf8c4719b5a7d8b987cc88b351", + "0xf7772a7229f828e7882f9d8ef331c70b3614ebc07f92e9985ac09aa301a183f1", + "0x4cb47a41d06d98e65a66cdb2fd900fc72709c849c8eef69319a9ebef0a98f75b", + "0xd1f7f88e9dce58e32ba3c0b9b3a44ffff8ee1fdc8f2376d60ad32fee056e9242", + "0x64ee7be974c092f728e59ecc498dba15876272684a9ce218bf7154a6fe5ca5b5", + "0x5271e6463783f951f0564668edae7c57cb65272f169b2d39bf531264d1f539a6", + "0xae63cf285d7a72e60d8b5ed3c33c9cd968249f39039ed010467959adc74be1f7", + "0x4b1d6748886e793d3018e0c568e1447b43499fa9b7604ab0666c591d28733745", + "0x804b61a6f141d62c96132980a844cb2876659c9940eb9d8f3e8aa4ffedaca39d", + "0xefb10c75207d2f7b82dc3da8ecaf3bcb3117aeacfe6b8d8d0f53e6bcc3f057c1", + "0x88b973148a9abb778807d44768107b72b328867ca471399faffa8f393ad2cacb", + "0xee7faddf83e0e69848a654c08d790e8540a794d66a78e7c798c75912f7993e06", + "0x87a750275ade22a2a9bb17272213b299937718151a879b7855f2588387934b27", + "0xd998d51cbdcd38735b4e4b584f028b17e2b7c9cf1ff1cb418ab32095ee412303", + "0x43b8230334483c0f9cbea9eb97bb19ee4fe7fe7917253cb88dcb06fbb5b862f4", + "0x1dd918330ea8038c32d066836cb61c473588c76849de8dd6b253db172f1151bc", + "0x754a5e2c2fbd5c1931afda500cabe42ea90125dade4d880ab686f93cc6eb18d1", + "0x7411f00bf1d19b74ee8f534dc5c620cc1aca3f683543bf9c3b4f7f46f009f45a", + "0x71edafb89d884f6a33f4a36bdfee47abaf28576114a8e74f2d8add7d7f281708", + "0xaeab618f7901bf8fc9ab8a034994bf0e6c0f5a03d72a4b94cd3ae71e9e4b2a58", + "0x20d8a5985c95314fcfc58a3aa97e4be2851b052cb6f428993338cf202f2d5b07", + "0x6acb9da69f7d71c4cb55c064ffc38fb69463ba7283da1356a845139f11cbe883", + "0xf045b3a9d30231b715442b9f077f80f0627442af25570c61950f71ff8fcd4d2c", + "0x6bf103559fab3d0da6be90d7371a2494e5751af68a0c40c4c1596ef13cbde6f3", + "0xe21accc924048b79e9db574b115b8f7da8aaa8110f10de7d2cbd2daba35d95ee", + "0xe5237c8fdd6a31e898b1ffc7514bdd00f220497b93a266161916281f0f6f76c4", + "0x290441e4fbc7757942464559a4840c69f23425154a3441264e25059312e46679", + "0x72cf40189393c95fb7f5e055cea77b10300c2fb024e9ea561c31c16e8181cdd3", + "0x4e5da39c53b30f68ed5e12601f00ba591690653389c49dbc110510017da75216", + "0xfa65cc5342d6ef5db34684d096a3f780225cb2bace2ec98bce10c9b9bcea72af", + "0xc47252ad2d44f11b9fcc2f0793acaea3048b4d00217f15d667e459c292a38c28", + "0x87e6cfce84d4170e26bf89da753ec02944402a1cc0eaab366882746b670b63b4", + "0x31c500e215573d6096f9903d303e452f7e54af4f0cfbbe9ac72df236fb947f22", + "0x4dae579a243c8ba12736b0019168637d5972820664867515e6d796e864221d21", + "0xdbb1a00129b234b2e359f7a4d799bd911d6e4ef72721b14ab03e084ff7fbc312", + "0xbac7923acc3024d010016990543f46ee7c45632b8d6206e6628c328a15e581d4", + "0x8e8bd2de12b6ed06f0010bd0eb7c601db70ada39a8a4701ae50aeb7218f4a9b0", + "0xd11e0aadebef3924024682aa1f3fb580ea94e7681543bad803b5ccbfa1986fea", + "0x09a7fbcba72516313a4e9c585cc737ae5d5b6282a9e01b6113ca2faaf6de7062", + "0xd1fdbc38646318444c8ef735edb846bd7c59417539fb5a42b7bb350bc4dd6ea5", + "0x57d5a2539048ebffced22d11fd4498fcb6eab22291507d2267ae5d47bbfce332", + "0x2e31fcfbd686b883011c59948c7fbca5409ac160a1abf737f1c3934629f8fa21", + "0x52d7046c47c7f576db9dce64910aeb894412870bbb61f101fcf110e0eafc1b66", + "0x3a163499091efc45481438e513d7e93e954db5aebd9b696cf001909f63e2a099", + "0x7f1e2f87638b6e251d85095469e9e95174fb8dbdfae164c197bf13d29d25ab48", + "0xcd83cce15959e0433b4084e9d0222d2d2c9041b0a6641a9aa4a6efb7e5d2ffe1", + "0x4ed3ce8f5c279fd6a9b7f0548e77bdc1994cdb88149ce82ee20bb73246480f84", + "0xe95dcf033fe49ef008f095d1286f7fda586dea54c895e2c5c320761c1ba48733", + "0x7f40d717f4e45e76ff571f7977f805473d3f4b46dff6b2780b8da3f2da0b8e53", + "0xfd8077f6a82acc2408d46880415b737c6ebe780e7b6c22a1821f9f4aa688d79b", + "0xee14592cc577288668d17bd3f336bddbaf220949cce20acff318bfa857a0df3b", + "0xb1e964b1bdaeea1be824402b257710f0c3b0e895387d3d7493cd6491cb5916d8", + "0x2111666498cb054ed2faee4847f840fa28aca0ab361e61ae4dad8ef6ae762b3b", + "0xa6e837b6034d62b5e045a81d3f46099e63914b11c15f825f9e41a7ef9ae7053e", + "0x9f0526e5d574302b47bbd1613ca3284c4d9024eed1f71f08525c28f8a67661f9", + "0xe9ec2a404661b3b1bedecdeecb053ba0bd8111a0f8281847827aa7b472521587", + "0x8e254c00d70a5ad3636c33f5703aacd1d441a5722e28f4cf41438e13581f5a47", + "0x77551d8b5c120b73faea13bc580c185ef33457d5aeece57f4124a15eebf426ed", + "0x71ac2697a9c86d9b6597f26a86443a831b67b848c5dd97e7592434b5888e00d3", + "0x33654f598351abd1111d80ae99a66903041c70599a1619fab7e341d6f5641567", + "0x0435869c5e1e10be358a77604e0f878f2953031d61277aae0f381890b4d3f4f2", + "0x289741840e50683d9e3fc30ecb4a136c0f8e35fb13a60d6698f0607fc3b640f0", + "0xca736127d093f90204bf309585d48b04fbb49220d00027abd9b634670894dadc", + "0xc4eff32ebda6c9fc0a55b94d53168bf23592ad0cfbb05a7a806577c2cbc9c881", + "0xc4e127f92d231d9b955d66d03da65a60c55310fcdabcc3628713ed328aef904e", + "0x9e594a657f772763d548e7db00c7eef5b6e005959248df384eb30ec34a4c8c2e", + "0x9aed15647b9591900f7b1529a0ab5c9361e28142fb29e51636fc0b2be82bea0c", + "0x9a8801f74d6831924188aff364c96930ae6e8cd463874344304586b03214f476", + "0xf677c79f1cbe54272462be64c7118ee8be953a8bb7c755ed4c070d9f7c961a20", + "0xfb933e2021a1a4fc1b6b69ff6fe512eaded2cbe163fe747eb504514cbd35fb39", + "0xc59df95a2862ab5b4bf8c2236336d0ec272b3e0aedf81356f368c773e0a61531", + "0xa5b0182b4a29a28ac5c634cee0e5f9ddba9961ebb5ea72b68292de73ac18f01c", + "0xd3911bd94c11233072148b89552bf7a464d3419822ccc124a5889af1b1003884", + "0x24873f45e59355ab140e071c2178c37ae5b3b8af55e1c91a2a50a8d2852cf6ed", + "0x734e3920a9c0161293b22c3deb53e2479d3ea55c6f5e2c664f293eee4871993d", + "0x7be2ced57746fc4d748265ab2c01864c1a94c1807e30565af0253545693f9e12", + "0xda57a443fa304090b16a92275ef5ea3490da8e01f1079b1ed50ad1ef18a232f5", + "0x427db8bb9a3296a8bc0bba066188474c7546cdcf2461052b3d6c5dd2b25f09ca", + "0x0adb8b9602613e11b19f381bee46e17c6da6dcbce5989d5bff2595e87b601c79", + "0x5c279e5e46dfcef1261da5e636024b2dd2e8ab7e1d42429c37ec19a8a505cfd9", + "0x352e672b912b6ac85ad41fb81514985e8e4ebe663e5400fb6fb0bc951f702765", + "0x2ce4588d134a256949ca8a51b3e9102e06cff945ad56afab727893d205677c6c", + "0x46b1fb5a7549992fc7b85e2d55c015209e6ae819d99985f15f5cda60c099ef76", + "0x9b47728fca38f41ffb36bcc51de2fe31bc2e60ddc14f1a3985a1ef926f8e6ceb", + "0x82c989c7bd5108f19fca2c420354ce946f9cf230338d06f9704490a17355ee60", + "0xad60b61d1e577aa7aa560b022ff03bb9ffa6c4b26bc5ee6675ca07d01c6eb8c8", + "0x5669ddfddadfc02b8f7e29c4e54cc697d40de0ebfda7f10f6e53296c3148a75c", + "0xd64b459433fb085bda06ebb3aaf476504cd52e617decd0ddf2a837982f48cfbd", + "0x9af6908d3e40de0acbbc88b0efec5d96d9f1bee926ffd10abe0fba3b6ee73387", + "0x3e4fbe380b854f6536a3b8e1cefda34cf6874f476cb58f2b2ed6a9520e020db4", + "0x31ef80f7e4cbaca1b5fb0aa08d857b62eab974bfdc2566a79296be65fea3e3fd", + "0xdecb955a14b20f34730fc4965e7d50ad52b6ff4a9e4c3f7c6f78db994f47e706", + "0xd1aff3188bcb9ddaa462003b89430e861a7d6c52ab310b163fa17a3a8dfbe342", + "0xac92c03cc6b2d63560e6a5d68a7b9c2cd383db4c121f8df81ab90df1d2b0f262", + "0xb20821bb09449cffb002ffd0fc71d58e7278f200afd6ea78b9d1e9115d4cf365", + "0x815f8c56f3c2bc5d5625e0d76cc159f072b156ff068b3c55bf1f58ca75a10e6e", + "0xc5c0091ffe69b666ef52afcf4e0d3201e81bb6cae4c7a7e0656cbd17b55b78fe", + "0xc638fea5a0cef6d5cdb65491f8e507deeab722cc9065795f7560167814d128b3", + "0xd8d8e2fba1110fc3c28fd2e23e5326745d881671fc626a9fab9a7096b0d17b38", + "0x6e6613ba3f1b1865d42279b89c3c950816f1c582bb25cdc9cf6262ba60f432c3", + "0xda46faf0489997a945dfc270f7d8437bed2dc96d0c70e92a4dbc9e68965a9677", + "0x97ec7f9272d4d51f6631705897eaaec94c08e60d5555fd6705297d72a494e2a4", + "0xeb3b8205aac87377dfc1124865a5cb237fd40df7728b9548018b12f7a9c6b247", + "0x4be868002f02b6da0cb69d9a4f65beb6983eb90d7fe9df391a12fd2eb4e98e10", + "0x4a87d1403f54111c223a0f1bb72851566ad407eca71ba28d965f4df852ee92a4", + "0x80ef65b4d4a971311f0031ba0df9a8fb0514429c8c3a7e9d06c84a45fdbaf7b7", + "0xa1729055d6b870ec5edb78c1a85db86eb99dfbd68d76a10dd56e26404f9e74d7", + "0x38f3aa62e389abfd2285076afbd721d0eab339f556900671093427145295ab35", + "0x081e870f4de92e690815569296302e5478f2df61804e0cd46b23c0cf7ee03ae0", + "0x7b33d967fe0ad4fd52c8921c7d1ee5bf4cdf747673598c56722b978353f02d38", + "0x566ebd2aed6032d524b50495ea2a1fae0d94ede562bd2d55bde1ed178ea3e234", + "0x60c66c40467f31f50efae8b7f0c9ab36af794c4ed10bc141d412625b31abee08", + "0x774cfb575ad0c0c72469fa07ca5ad1c8f9fa0d5d130151713b24d8806db8690a", + "0x15b9747f9896f0be436a4016567560136fce667039e14af5a235a64578c0e348", + "0xa8343dcbb8e2db25488b8abc935db9c03792a51b19c44bb7bb546fe60932b4a6", + "0xb1130b166d735ef14d3ed3fac741c2333f1dc1681bf585e8e0283ac3049c556e", + "0xb04b2417fe4e538500c630a8921be3a7161c31d7def2de6c062ff6290e224951", + "0x497e3e9018a6576cd8dcef41bf69e0aaea27c5c8db8bf41f8d70b9bbfcfd9ead", + "0x4a9cd7092567a31d87f5db6e13bbb4ef59bbe30619a4dc09f79fbe18f17cccdd", + "0x7c3559883a287b9f3f166583fee10fc0e6bd082057b418dff3834f857df4b766", + "0x6de07b6412a3ef103261cc372f3bdec95ac6a56d7280bc4598bc0f630f847ae5", + "0xd3d97de711b3fafa703eac4ff3a9b3b7a977f23d95dfb759d05773950042244c", + "0xa962eb9b866f372b706d89fecf3dc0913abc20bd5cc2022b0c90ace619c62d1d", + "0x94e1946d3cb9f495a78c765984b062ef10a5d0a6796e3b379a9ef4dff22487eb", + "0x004392e0676e1f6a4120ac2df40957a59ecc2cd71c51d768e158e9f8f2212c87", + "0x470e800ef490d8dad7ef3cc3f790edbf17f181555ad894038df37836e2a15cb1", + "0x9ea728c37a030466ab2b2cc568619b1c5fc073ef8603b413909c83ce1c111c44", + "0x7c049acaadb0c8edb94c16528223b2b5badfe887874f99f44363b76c8de2cb6e", + "0x324ed01715074ff71da8b5502bfca76849c622055771861132190c47423ebc36", + "0x324ed01715074ff71da8b5502bfca76849c622055771861132190c47423ebc36", + "0x45f3b00a0267014a397fd544ecbea766ec64fa63cfb9d08ead05f1d0802dc04b", + "0x0b6643d204bba3162600bfe67156f2b8e6fac4fcc005191383d0dc804d0fc837", + "0x6bef1c6b643170237b82035fb47d26f8a39c178cf1fa0a045a59638ee34f7b68", + "0xf21b644a17102785490f9ff3fbfbc6f6b726d093766d11108e4bf0e806b9eb35", + "0xb3e5985f8284b6f3a21c1adf882b19c208cb61ffb300ae9d226513e94ba79906", + "0xd9fe1503bcec3ea15bd76a194e31cfe6de6198e0133457c0bd869baa4c7cb37e", + "0xa332d2d2b23fa9a3200a51df99a59b9b2a4aa7e4578ace3c01cac8462449d263", + "0xc57772f71a79303919f7c8749b0016ee0540b363fdb4e2a15d4fdc502a4805b3", + "0x4d67188ad435240bfa521d84b459641a4c7a4fa3d5977b00c8617c3f35783fa1", + "0x4d67188ad435240bfa521d84b459641a4c7a4fa3d5977b00c8617c3f35783fa1", + "0xb41a1edcb01209a3888d2e38d510c9f5fd138e38a476fc01dd9b6794d6adb0d3", + "0xc789e29a2b6b53a2cdbcf98cb88f52021f2bfda460ddb9e68942a3198d58eb9c", + "0x6e07c923037d8fedf4e11233d6da6bc3f3311e4153e485c2c6c6b824da4c236c", + "0xe029f85fa399425b2aed8302db78c86903e6457b31b2f0a6222b9dc3bd6eb5b6", + "0x6eced43cf6b64b44a5d2f0c1104ae1f2cad3ad2f23a5f269b904bc8ef5bad584", + "0x64dc700f5b124341e6ce978d39c17a280505e62694fe27e226ef2635fe9aad3c", + "0x9c3f2e848c866eca85aa938d743880a3ffdd5c2924039c9e38a72c9bda706cc8", + "0x343d3f5f2f63d5c293827fd3c1906e623a90d5269412f658b5823a92204fbd93", + "0xab50628233650957050b1b7670fbe5848f58b891e9f4553ac3424644018f5853", + "0xabea2e92df63d7e2a42238704c0fb985d91de910417a3523ef8459add75b6018", + "0x84998ced7e4a36ddc0d9ebc526a68c01ced36bc08a96cebbc02a8483dc0b3f3d", + "0xa74d80feda0d6153bad1b23e1bce449f822e2ddbf6993ca6a9a45da5b4689f07", + "0xe21331ec2baebe0584e6610b8e8ff16172960a6a226011eb753a1082d2d2c5fd", + "0xe204b7f542a67fcc66f5e2a373cf27c728e49696ff9e96e1d1f5c66921bc98ad", + "0x48c4478e98f12ff7a9185f6d951142aa71dc7166bab17298852980c00e70edea", + "0x507d05dd02eb0c6913dab228e899d7aa35d7a2f0cf031fb971303b178db8f3c6", + "0xe90a261e01c27b508cdec7ab4988170bb783d100866a57b27a0a46dd293f085e", + "0x9e58985355bba0b989889442214f05c3f7595819056fb7302954fb25394ff4a8", + "0x678fb7226fe2744fddfd3565ea395f5299bf58c8fe8c37375419997f65071a97", + "0xc66202319d128622f58c71f3f3d2e267ae75dc2a1bf887c22ee1072abd0187bc", + "0x297644756c123e65c708a42dfa8df277f2b6e6d9c38019651ed9916086d08b28", + "0xba9bb07761499fb947500bb4fd9b063f0630f9d7068bfc60e062c359339d5bd2", + "0x08f859c1e5841bd9799aab72ddcb40593f46d81d9307dd77e5183f32a629444f", + "0xc4a7ced49dc1a6c9a0733c719d53e09c8618f2948cc41f8ce838ddacff27d546", + "0x5f6bfd76b1106c46061ddf865ec059eb30b9e4dd444d6f358cb9a16589327c33", + "0xdcef14316ee93d6e8c8411c624f704c86dac6b165a6ba0149a1d38459681f860", + "0x53c189235a8cab6c07cd98eb323bfb624d004502db1c84d69e1cc8e9554aa70d", + "0x90f1b403e6d107f0669ad8de4240d4a5682892711e4c206042c89db138f3b99c", + "0xabfcbe9ecd1aa56f14280a8d09f16517858eb2576221f90bd71bbdb30faf44f4", + "0x50b7a851b9d2f188c3679474ccf45fbadb31ef380ba25651349da9835f5595dd", + "0x741e33923d2935f059157ff7c1625999b45bd55d8b66d9d60539c42b5bec846b", + "0x354d602a50202eeb4dfeda6cc78eb09806d6608240b38680c2254a5222574747", + "0x3cbf52a86e7a37580c4b3e115cd931cd6a384f50d9042315e219a8dfae5880a8", + "0x8cd11e4360bcc5d0a47835523059dbdb59fec191ea9d0ff3481b9bfc9cea0da1", + "0xc7a25df8d6a4486838fd98840dd619c89c8e09e4e7b7f6c8c4b590ac7bbb7af6", + "0xd2e85d11bce62c9e9bb90b3e7b4cee56f3ef1690cabb609ce79c1094805d9ce1", + "0x2256f20a8db95c92b4c256d9ae7fbd3abbc5c50aac78c62e1e32820b005de455", + "0xd5aa4c5e753f8734551193376a55e8ef86b5aa9e94770c0213b707d2ffee0f88", + "0xb7b455a37e22b897b303eee1585add54355078ac9fad5a0f8391b1e1d0aaf0aa", + "0x3bd846e1796ebb15e6b18191c5606ecc288287dce06022ed666d5584766258a2", + "0x2bf13a5c0463a30ef3cdc14a838fc8191c3b8b0948d071beb137ce94bf755d96", + "0x55a993ec8977ce267bb0d94a638341e48e92e363ff5a20cca642d0fafea8640c", + "0x6a00104b5f5e199f3428d1d0a4c8ba12bfafdf6ccb2748f9f8bc13060ef155e8", + "0xdc69e971c45e522f092ad653476ac6b3fe9d1fb3927898f77c00a7ac7e66e4aa", + "0x8abd10587a94962c72b44fb27f8582fc7b56623786bdb1b9a3b3279b6b3d6e30", + "0x8b83fbc7cdd409282a6e87303bb3bf545fb85209388b66da92152b7a86c758a6", + "0x54f688b8ac24c5bf1e4297f00a9345c6aab4d317933be9f7cd35a6481aa56cba", + "0x69407a2dc1bdda943f0fd9ed95bc2b10c4f27dad00e5a97f823c6ba00f52159a", + "0x565ec30b70205d4bc12c5df4e0fa363c9c36ceacc6c59c34a6b7ce0d165e14c6", + "0x84a876b4e8da1908f487599bc7dc71c50b4ead0dbad96ee987ff8987e04c7a4a", + "0x0b5309013fd2a75623cc2dd5c7fa16e7234dde9f4908edf2a350264ce6a82ae6", + "0xf4193f13055a72f2e1c94ab76b7821f683a69b3f87307ad1dfcaa4982cfca4e3", + "0x4e22706cff829dcce75d59926be4c4103c05c5d84e889a517a72020bdbf93cb8", + "0x11856473b6026b5d521622cc05d0c6796c9ba56a0b654219a95aaaec4d8b96cc", + "0xbfedd182e4fdcc073a950e3671424617fc59caab42d7ce42002ac8387cdff085", + "0x0e97cf566d0d6002d1262ec6227978ad6391bfba1f713027c9ab07f010034b0d", + "0x6b2e770616b567619adefe4143b7422871e1cb3feada81c4ed49ed4ae9e2361c", + "0xb708eae4cf799ab4398705ef7cb61c4797367869409724caa238636e9ad1ab3a", + "0xaa143bef622b10f660eb4dae9985da75fe3f5c9e1c1e764ca2c8bf90fb40844a", + "0x34c6815ff25aa134e2df6e7d14485577e9af20cb586bac6c7bb716bd1aaf0d79", + "0xb7eb48880feb7268e91368c12f657fa62dfc81616e179e7155a6ea5458d36841", + "0xae61350d7fd405a936a7512eabbe4f3967092e97966a9280a10b9ee409d55784", + "0xb785242d78036740f05d7194711ae8703accdcaf833b2f051b174bb41470e4dd", + "0x4b63061293862a95fb729de641897abac1df0f8825291e436e3bcc317e958db3", + "0x6fba3088b98c95101536e51561a1e17ed10444756b831be71f863f10b48798a4", + "0x30d69bb4b7049761afaf1fd8ce85cc5426ad6933eb38ad8baf7eee6b413706ba", + "0xde46907eb6c50b94b6c07074f9ee2626d8465d65fd59c22034babb93e407e572", + "0xc70a433efdc0816c93f546ba1b103842455eee05ca2a7ce1978dd70a0b533429", + "0xa43b43c894794ebfef437270a540d3966407efabac2a66394522bb59c315b0c5", + "0xe51392acf54c9acbd6bdba4fa46ec4a44c0b5cc5b93e65ddd29ecb6fe4aebe49", + "0x524235dac79a5359186e5b588c7f440c90ebb8f08ed79d98d665498d0f28fe73", + "0x3007c4f4792f18c9a2885c37a60fc11586d6efb6737077ae2a86263c821ffbf3", + "0x63d2f4786ee7fdfd0e68cea92403c911de465d4200735ec1880381be6101b684", + "0xe53116d9ff27d6dbb4584e2a322fc998676f7b56429479c04368b8f5295f1eaa", + "0xb243303b5a0b3157586504eaf94b52b170f0652ea09f20687a36ee0722a2f47d", + "0xfad0efa9d0f8c79266e46ab804dafdca2d6e47a872db6b76d57f61080add191c", + "0x576bd905693c88537b9e97bcb9ee4b17b4b94cc0e87fce9fb1745805c1070799", + "0xbca53bd9a2cf8b615adac75b3aa0f68f3a7791bf85bd18e05f678d5ec23bb520", + "0xa248e3f3697bb0bbaab4f7161e1074a4b866a29f3d2afa2db6e6f99d6994cb90", + "0xde193b795971838d00872c24e2fb24346d30102a4f01e0e9ce900fef207d5f0d", + "0xde379349d80ab390845c23d048932b6e67b7237118317ac9352ed4e97e78ee22", + "0xecb18822bf57bf390d4bc6df5573e409bb9a47f08403e3fef9365ecf1f4af50f", + "0x10b78fcf8fc35ec1af050e3bcc7767bcfc3b5be0799d7b65f26b7e406662e454", + "0xef22f8bf012a981577d8083a8481b624fe9aad66604bb9068ff17bfb0476579b", + "0xf124c49e37df3536cad7f5885ecc100f1a4615ff422041c0dc9c4f1cedf15875", + "0x7c3e8442eed6b2a4ed71ed6580dbec691eae615911ec7e88ff3501ee86873a95", + "0xddf4fc7cc52c917b8318736f1d7dec71af797fba778b7d53e30512ffd229320d", + "0x7608fa94ed393d5d5ba2331e2a393a97ac47db4dff9f230b40315ef59dc93649", + "0x9565c7ee2a90866e61f844847b4159cb7af350183221436adf4a5848c56f7e2d", + "0x179253edc3c20b6c5abecc7be3d031995074b2f58520fc5bbe9e60999705a41d", + "0x134d48d0a73238096fcf1e1603ada38f06103106b1cadc62132bad2d44407143", + "0x121931aa5db5dc80889aecb0783fa1aa38d64fed659355d3ae035511e16d46ad", + "0xcf5318f9c20446c9f49dbaab86ed7f7931d2106c56a50e902ab35b940196e6c2", + "0x3f11651e39349b6a5134271802691d567dd82cb5c9d7cb561b9deca34141f6e9", + "0xa4609297930ee29168278fcd936b72a06f5ace27e13a94f265a614258df42b1b", + "0xc1f84faf18148f883d109ac89342a7b695024730652e99898ea1afcf0931135f", + "0x6eed6ea49b1c13f757b4ee80fc4ede43a3c01f12b07035297ce9f2fb32df1f24", + "0xc4c1aedd42ba4be292e7d8fb5e11d2dc1b4da1e19b6d3e6f6e489198dfb81745", + "0xae08abcfe9d7f27597aed465d416ecf40cf5ee28dcaaadc3bf9ea39e6dac7d76", + "0x104a6e346233bb7cf4c25165162f2861e80e0e21f583ee9067574d7afa3afe23", + "0x81c5be7f9ded6485cb694694b32f83c46f3c0d2c3d4393e4efa8a75588324773", + "0xfa68373c44a94414f83a84d073a419e159f2f6da4e054a8afa0e1791806f3d08", + "0xc7eb211836b25c7e10809669c419685b5bfdd68b557ca9d4ff8ff46418bdd9fa", + "0x895e8fa57d682b8df10b20a766008d5bc8b252af2976c89e2982914a872d3f50", + "0x501ef13acc21dfa922401f5b79937658a8adbc40b687246af528838834af907f", + "0x85a77b51a8ef60bd31a6b705f0fd6608ff343aad7e482d521bd49de10f294f4f", + "0x3ff747c27fcf902d565ae67947edb010da104ef41c3735c3cf32ee156d9df559", + "0x0cd3ae0525fe36d5d2d0a811679fabbdf9bcb15689b67d591d9fbb86cc9e3daa", + "0x545f4a7d8dbcc43c0c233e1e8de6334de7260014ee5251a96e6857b8a31a2790", + "0xac8399d0452d391231500fa031d92cd2cf274bde55233810cd0db86c699764e9", + "0x5805e3617f1a0f92760f6e9c85689f959af49bbfab03d97a7a81bc8a3557a91b", + "0x521e78fd4dfd36483f1c88fe00e712c2f3e6203684b57101cd3f12dcf8fba70d", + "0x2be757482c5058a806b8b92fe899e915a465a093cd1b0bb016921a3c77b11878", + "0x18d5a2926920fc21300d80db67f10d31d8f25047d9593346ce0a382eeb573c4b", + "0x028963574d20543a601b0144bf62c35781a5172ac53436a6cd89bf53df34ce8e", + "0x7632946bb4c1aa36a2592847cf232d178e232cea996695d33957af560a4dc8a9", + "0x2bdcce9b3ad4b659e79283cc43b98c30b0c2d8ed19514e080f90b465a0f7187e", + "0x9ea457d0cc0fdeb167a47b724c60cc6d3edc304ba04beaaee79de542740b82a7", + "0xf9da1c3b3e3e8eb805b71c4462ff64fe7fa2a2d2f0b0b0d3f4c66f32134fd7fc", + "0xc36716b9bda86a9974195d908a85bf5beb29696fbae17cbacb708fc9f4c6150e", + "0x5b7256cf7cb0f42680c3b8e607275576fbf5e05e00b7a805f8ff7ba9d20ee259", + "0xad3a3064c87a9bfb978918688c39ae6a7fe84ce9b1449b076a4022c79f895b12", + "0x759b78e9ff13c570ee47440ba409ce48206ffe6fb98c826ed64206b571666c31", + "0xba97dd9f48a6156a4db32672ba50d4cb6fb0d545d93f1c214b7cd16aec58c42a", + "0xc0ad2eaddc224d955a1ffb5da84753a9695d2972a9730dfecdff8566f6ebb481", + "0x72df15cdffaeba6ffe3fcf16db02c8fef46e26829c78fe59fedd1c03553b92e9", + "0xb983e4a2ecd1f05f3f23da6f8b9af3e54fdaf619fa557d05749d5e95b9b160ca", + "0x05d548bb4f45479e197dce3a91f83741b2ae4f4ae6b3e3ffbb3264989a12afea", + "0xd9b0c19a9ce4177b939f542039d8a25f4adf4ea098ce9c23142e9643bbef3b9e", + "0xefb425bd05fc0b25c1b7bec013b0c1a10a2aacafe9720444455c80e1537e22df", + "0x3a481d5580f2374a63e012a667a35b7c10c96b11cee58a467db186acaa6a5ee1", + "0xa31a1ebe642c5be3926d72c5371edee8502e6a5dee9773b6ba5ec08a821d6c33", + "0x1dda4c3fb7ac256ab073fcfd894c731af3adf35d35858fed15ab09234336bae3", + "0x1a0bb638e01c267977675a6d2df2f0acbd089dd85de7d57594fbcb74652c1894", + "0xbc7f80a08a9960edd266d5c77a7c8dc372c2e01ab3cf78ee4fe1bf27657a7b9b", + "0x5eff7d637116d5330160b96bfab305ff30bb8766156fc9592c14b4d9bf55a944", + "0xfac07d9c3971ae14aa2fb5ede6986ce819644f7a63cf62fce4249c3f68a1d7f2", + "0xcfb0ddf8d89ee5e4d77c80b99e4db40116ced0342df4c7bca8cd5365d968a1c8", + "0xfd2ffec79bc34341392c98e3d3050363b386719c0e21d9b5cdc41a63ed55e61d", + "0xf852c91f2e35e35cc0486f0bf4aafcdd19aedd48086af22a8f84dcb86ac6fc64", + "0xf61e3703dc69bed4264d2146a04149db61ca0d5cba7bd11df4bbf983e8720ea2", + "0xe0fe8ee62b0045ccbed90248334f0fafd25c981d4837e2497ca37e26375a16d8", + "0x1975a8359368ab46defa02dc67b016e587b41baabe0a9fb20089cc4aba5225ba", + "0x59a343094df60c0e0aa40caa5e52f73b3be6e280d96925b74c6e1b1ac8447281", + "0xef516dae231ec7b4ed6e00dba39f1cd30d4671883611e1587ffd91ccd66250cd", + "0x8d74e60ee6c44f74f40e55fe4377a1653a7b5ec62b6b2c9e23e3feb9e01ebef1", + "0x493adc7fb2f0e91a4b580c0e224cc90e99fc3780fee2d3e81624388d7e16f4f7", + "0x9474bd83f4fdf5ad3554b2ae6d9b72659df765e6ac643b672b666b5042289788", + "0x1cd6a3ebb460ac50d2b5ebe8d033622abca653965544e2a75176b3cfbb1abc42", + "0x368d2a6455a33ecf32a5aaf9a8347ca2af45ee77d0737bab01a55a9b7fb1e4d0", + "0xb9da5df050c67c63a633c235ea828ecb83d1c3dd723a2b45da6d3e979d4609ec", + "0x55bb3b0264b8369b6f7fc875983ce6ced3f5a133fe1a3c0d50ae40f96fac4126", + "0xd6399dded03f6efbce8621973493b5ec61cadbc92763060a431414c32f7edc1d", + "0xe1e33ecb633dc1dc3d7d1aae65d5acc77741f91c07555c2c7a1face769e7f625", + "0x93240eba227c162124a62dc929d306aaa4f5d740bae31ab9467096ef2b033461", + "0x35d708eecd532ecc0beb070d188033a6bcb236faaaa5263001ebc341f0339f80", + "0x25a2dd51fdd47f188c926e23eed9f919d90bc30e8f04e7239700055317e1b25a", + "0x4c978710ba6b8abcc9adb0a0ca48e8fc798a971f3ca02945f43df9acf4937853", + "0x4d4a28da291f19c04035f1a8ab64e8c678d5f47fb28ba38b69f965f882b59f1a", + "0x00f2c408f556094fb77bd77e032d5445876a04f3146fbf1e9da1a9d49c663b98", + "0x919c937be634fb3c4257a0b7d0121d688bc9af76a37b3828d98b36543842c468", + "0xe1462fbc1616750590164a56a1edd29c273d1bc674ef83eaf8bb5e681242b4e9", + "0x175d198bb142b9116d033b26f3e355f8fb1026470fb1be4fc9706ec7883334d8", + "0x8adf04a229d56a7a1770e459d288b5f9457606636d03c3ac57e6143b4989c4e6", + "0xefed01c44eb4cf191077b399ecd7e1eb2b79392b1fed72f6483f8f7a3c2e4e06", + "0x6a370bf7cec6420c404bba66a0fe7f321ffdbd4b4d33497dff5af6fb557acaf8", + "0x0dadc9c0cb794ad6e323bc804bacb5346fb64bec70b1a98c9e65d993b7cbabf2", + "0x6e286e16248efb424586c5d9ee30ff099d1f9e14e6270582e6d2c52cde6956f8", + "0x1858e5fe6d2388922d698e3a5af6ec10d1f460b6898d0ec9169eed516be8fd45", + "0xb525a4418af7c71c590ac3f2009872bf08d73b6ddc1f07c3b4ccdb5afe7baaad", + "0xc741ec96b79e3e580c414512e4259e12c152aafc4689280d0af50c25705a49c9", + "0x278359511315572808704ce0e7b9a0d00fde0d43ebc021c589cf4b8b6659a952", + "0x350c3abb16f923db2b1b8e352f9e3397c22cc362f487454e3a8c5922c3945912", + "0x442a9f504dae35c3be2b8bf8034171ea6e61e6fd4f5fa55076f5ba195f7b3e75", + "0x7dc7e9c095ebbdc88f3c1079efdf810565b9763a260263be3355562dc39267a1", + "0x40e9041acd7c98640ab2a72c7ee6df2f9a586c00af754d383680eda82890189f", + "0x2c11c18e8d6d47a681899aeea6683f3b92189d12a5c4f77671d5ed5625e730fe", + "0xa518424300e954e004337716dab428fa9cd2a5a3284f7d1f5e0ae239743563d1", + "0x4c94baffe1f6f2ef3369804fa048b4aa107b90121628a2c08fb4aaa1187a9e01", + "0x53125047ba165546b5bff92de377dc9b609205632c3c14598c144db568d4f0b4", + "0xb7e2c71c713ab5f8f07333feccf0df84d636f4a1c6034cb4d672545202409a08", + "0x48a49becd6fcc9974f345128959d0c7d7e2db0b73bfcc32e5e999b0a39fae2c4", + "0x0fe6ffba3313804bf0cd3d9612587e573ad8b770733468e3ac9ec6c5eecb8b26", + "0xc2056daf232ede39acf1819459ab5729481b7c4d742c937e9b05224660016081", + "0x73960edc862bf916de00b14df814eeba8689c9720bd43f05ef916fcc3acac0f2", + "0xaef03499fa58e96792767d18af28e5b8377d44772885d1bfd793732e1accf9fc", + "0xc735996d9dfd06b72a28f3f1d89591512d8e0bd7014c5a088233c1bb50a65196", + "0xcd1ae8852bce18f310dafb29e724913cd896d2f0e02655ad17f36695c85dcf2b", + "0x340b8599ec008b069c2101dcae5ff2bd2d1f9d404ff67343dc2d409c756b985c", + "0x5ffcb917dc121bebbd2dec6d03a4d022042b13023ab638d02c5039616fe82af2", + "0xaea1eeafffa49cfc9f750375aa44418063860c97b6e93e83ce7ee386198725a7", + "0x9e22db17eb92e5cac51ad98179d459508e05d077fdb47b62fc94a51d8c0760cf", + "0x84ca5d828f0b7c5abd1a28d57cbbb0557d97ef3675d9f05c4e96ff1b571ecc5b", + "0x37d0f44578ccfa428cd7bc8060e794f5e3207aae72cef083f5c1c01c127ca682", + "0xc36adf1915a5d29d5b9bd8dfb1bf68de5890103017ed0036fb580296a9ea5637", + "0xd8c055c8046491ad4d182dbab263de3190d12ba551d0fe18335140962c3e9a73", + "0x61a2106929077a8d2b5a239ae37c4f6847ab6f519582a32d677a5f99b8a494fd", + "0xc3f8f326cf131808a3703ec80d5231657b6c063125925d545a060d9e243dc5cc", + "0xf7e2ff8872be139d879872c01e57debbd79117109ae22ab6274b59c000fa4f91", + "0x3a2362b493477e13255084043590890ae4626a0f6e2a790a9f73ad7eb9bf85cb", + "0x525f50e72bac92741f6825f6143b3cad2d52dc622a61a6b19fa7d20ba417960c", + "0x64b69cc34927d0135a07d5353c56fd0c2ae001684d1558439c0ff6e81be8c531", + "0x42e5a104b65594af2b40c13e00ae80bbfb3a4c4e1d38786a8855065fe7ac06b1", + "0x19a86e769b64c7ca9d45d75e5120c4e6973b27b960d6f69f66115da25ca52ab6", + "0x2683439b9732c6f2bdb418cf4960247cfacd4abe8051dace7c1d2f305caaefc3", + "0xf6735f2badccf41b8342e3f0aa43729a83b4b11274ee086b981ebe44fafc1cce", + "0x6e261d9714d1b846377b0c12f40808ff0ced525d96a5695ae340a57a4d229e2b", + "0xbb806bf576dc3e62b0adfd6812264ca6757a1001a4e41ad59b118fe0a4c89447", + "0xf67af88a314cc66d1e5230c5956dab89eb7b7a9dce925efade800a3da79ca345", + "0x22e7660ec015a958c9712a67cb8fa266317785aa42befe0b34d6205724dbb375", + "0x26533394a255ef3b6f03fd62f03e353e6abae49e07248b5a4a5d2cd459d99c13", + "0xc4e4c41a941797b77f03729cc835a68c0ceb0510650e14b5aba62b313adb4742", + "0x6729b78565db5cb6872e9f2679f5e62bb6d7947fe9208afb5407096371a13e43", + "0x17518a32372c219501000cd692802feaffe0ee19511b2a055d5c609c5aacf5e6", + "0x608559b7c1a00c0829630185e6c69c77dfcabd311fc037f930c8182ab88fd5c0", + "0xa4772c2c891a31d372ab50d03e01477f48507c9140d064fba1407786cb252ec3", + "0x6fd3aa25115318851c5a2f24645c88c588c87eb9f412c3ace615bd759dc59672", + "0x663820a79988300e097004868f4f6fa20910d6e49628f0100df68d26e06a3624", + "0xf2f15f149cfcb98aff7439b22711188f2220dc01175771d2e9208c31cefef04a", + "0x75b9984846615bdb4eeeb590f61a0e56af9297d5a86f68befd5e39781c9759fa", + "0x4ff1191ddbffa935d0652baab22c8f602e601ed07feb35a5436284dec5f109db", + "0xca3394c42c31a1af27a9abbda604e94805269f4cfbf0fe97dec388c5d82c2d0d", + "0x19fa808f1db8ab7a6a283ac93f8fad6a787649f1ad071692eb470754686366b4", + "0x71dbcba59c2722c2ebfbbe9ff721478cbea053abec05811e4c7492864b9e904f", + "0x81d402957576926372adac6d1076f0c10bfda6e2d118dd5d0a24995d53020ff7", + "0x1b2256211346bfb1c6c102c4f1ca5ab1ff691183574e0a1ec8a40f0be38e45b4", + "0x7dd44c329cd83790668866282c945dd7683041506f5bd96f363f71417e0039b7", + "0xd8641b81e7e594cd6dcf603a6e2980f69914551c285383a0461265f409758d2b", + "0xe00647e1afe507d04400d00e9dd3a3a2efcd216284925db6d0d12adba5d713a4", + "0xa607eae6501fd64a7c564da80bb04e5f2ce2bfae376462cb7a191e73eec04020", + "0xb562294f66f90c6a6a2b6fb3f3bcbfedbc1abdee8f12607ed2374997522c41b8", + "0x37833d751817726cfe8c9a6e9b0f7ee1d752de15b7479f4f59b8242672bb9121", + "0xdd6590ef552aa75e103af4332bad976fa915c87e7a71cb0f48395ed1e27f4d9d", + "0x783424ae2f6f33989563ec471d2fa83e8384f01ad424aa9ef16c51ce56756d9f", + "0x31a132d1eeba74f67be9df2463b412f46f07c5ace772f89e20d1b4208d2152e4", + "0x6b7e95c15918064e2e848d5b6ac5df3d78af6ad24daf641da7ca07c54e82f08f", + "0x397d780f736879b3a5c53049184f0d712b1b1634998c10f49e5a7df7c3b1c66e", + "0xe8770315589927cb0b7b47629c3192038f308eeda8a2a07931c4cf4891d13c2f", + "0x9e7baa852b017c7f02107dc7bb876eec747af96ea60fccf0eadda1b0e1d9249d", + "0x8d3f109cdc2627d6ea9f5fb7f00c105f59e38da27a56509fec6276107529208c", + "0xa22988b43953e39da59fb97091d2c726a582ea14c7f9618093434675be46c400", + "0xfcd87bfc0f541a00b92dac8d43fc05c17d8dd0f9379056110c4b99eb2c83b5f2", + "0x7f52025c6ee49517bc89af121881ccea044f35883ca9ca597bc8eb2895bdbe21", + "0x6cd83914a227b66157365f5f81f47d31782ffcf3bb737c33ad3127d843140b00", + "0xc37cf5ed4beb260d3114e4b36bac459e61cb96b537bf14f82f32a7655cd78421", + "0x097e681b72319afb32e2f8cfbe00e3cd2431e4d2661975d2a4fea57080aeadfe", + "0x2008cd21f1e407c3e65769b88c1432b728d46ca3fc75413825991eb9f3eb80ff", + "0xc8e1db51a4c8263ebef085c9eecbf8830cf1fa4067f35fd4a47c4c7d1117b5cf", + "0xc5a20bfd2f08d4d82eda07455bdd3353ee330791ab9d50c11aef83edf34d5181", + "0xa57aaca3bc7a68e82084dd4bd159354d53a6edb6bb86d6957661d20d48e525d2", + "0xdf4302935a67843850d286bb74037f661dae5436a8b02c4d4aa486e69dedf7ac", + "0x28eca6f79076f5252cb46154bde6448b00b2cc1e8b86cc274ec360006ac5224a", + "0xe07539d36968d47d04292f08b6e5486935f93ca5f3653d58d6671c4c280a53da", + "0xd085226ffac1bf9e895498afcdb879d6983008f17e7b41d831454432728db504", + "0x96530b1e73294d79785e56479f9f5098ca5ec93863c9c4f5ecafad60a459f401", + "0x36ed0e3518d9955d4e3ad7da895f080a6d1775ef73146b35c19e8589fab5ddc9", + "0x190d8328a05a79ce1f6273f63008fac16cf065dbca15ba97c584ce1d34cfb516", + "0xc3aae99c088198415bf4d37e1666c37193c896339bf9d238aa5f2d74ff274f9b", + "0x8f87f36f4ca737ecb5fa68784cf1825fe60042210bc0dfe289dcbcd89353bb87", + "0x6d11d3e4d94188eeb8640e5150ba3358e85245a9254042fa35bc593f786e2694", + "0x8663772484478e3518821efb61f2b87fc6093b959f3b54ab5b36738d61227ac0", + "0x550eec4ad3adaddcecc73b0ac561139152f69470adf676b610440af7ae775a1b", + "0x908a85e546f3bae9e3ddaef997b3376a6e053cdefbf8f4df2af746e5a5c1a3cf", + "0x59b5d3d1f2847575275934954cacf5ca9ea81193bd28598828aa58beb9987042", + "0x43d290761a8bd0f5af5729895da3f6f2440d155078708fa0bf43c7fee76d8ecb", + "0x0fcc3b2489c9bd2f55429e67c069ea533acea6b1195461f1721d52136377d199", + "0xac4f710b03407667e2aaaef91212d3cfbe22ade3bfebc71c651fb15aac0f2514", + "0x18ef35f13c937f07ab87da4a78a847e8f5e3642910b03b23b89eb265b6489548", + "0x4b77d1a5a78d5420692ac8d8266db79a7a23983909b3c0f45bb18e9634994772", + "0x67393171cb025799139906adc0f64c6195c6a3e94fe729e54964018bb968b0b5", + "0x47461100b007dfdcbdeeee6ce39c35d3d443a88040d21887fcf99936fda1f404", + "0x2efdb4f4e559d8bc64e5921e976fd4a3bc6c63e9d5460028c3c8b3a58803562d", + "0x1a7b07090e31f34e3d4a618aa6bb4d65cf177431f7bb2dc8c57acce8ca04e008", + "0xecc1f1374e619176b828c5254f19410a2f1a26895ae4196bdd137d3ae2591733", + "0x484c4cad7a99c460a1b0ed8342728d839dba9b2913cdf5c89b96454513246cdb", + "0x75ec7d7be28845fa31ee4d234c61a15f12516401f270a7cc2ddef341b46da8a8", + "0xceea6abb319544e98168ce93e58018e14e26d8ea21e7c8e6709aa99d63d89c21", + "0x17757021f34eae1c7198c4ceff3a42e89dabb31fd2b5f03a3aacec08e9eb5c34", + "0xeac16f298295741c8823dc93f9fc75bb662e22da0aaabd70efcb2897ad24e406", + "0x447c24f4a8fef8f0ba9f8cda98020c94e7d785eaa6a00926d8fdb509509b6e1c", + "0x8ecc1d8be2dc1327b382b309cf12bd21cbed5621e25b3714393a440d0da0a645", + "0x1c2b14885e1fc83c0bf2cf0a8822496ee79839b68712a6e730f07c5178bfe438", + "0x8642d5f3c55d356a3e594ad6204e18027f1a914cf623fc2ca6ed78f47cc8f46f", + "0x0f1ed1259fea3859fb4630b20dafdef7856be79d20920f31e253578eebecaa7b", + "0x68a418c742f0a1a4a4a9e99d02d40d721447fb37bf521e51446cd7a1b2d093a2", + "0xb6013279ee06dda72bac6218fff9b84ee7d90f2061686934ee59dcae11e4ad26", + "0xd420163378471d8d5a0a3f827f8c4e15a2702908f67d9be24b9a752448b619ea", + "0x4688fa6abd5a8c3e7f5042d22fce83a94843bf77f0d17f973f98e7e80ce6274a", + "0xfd8838d86307800b721ee603edb2900976f0a283cb0d0c42a0b4a1ca51af0f69", + "0xeaee7d97b3b3badb2304ad3b142217b30edc33aac472649ebb45275e4f8e8979", + "0x3cab7ab1b345df6f183c6a6489fa0a573250e8f69c9337700d0ebf373c0a0a91", + "0xf439450e8d4fda85f4930a01ac96979fc99560c7ff2177540a2e62c0c6d1943d", + "0x07bb98f27d8a9b1d24e1bf86b58060ee558d5d349ac1824064c46b18e3afbdf2", + "0x7ee9c68a3ceb977f62548c33a60b8f2b4adf71c15973cf5e2fb59d2d33ee72eb", + "0xb355a0b983cbe0f210e086c3bdbb9de30c21d2e2e869fe560bc646d9a39907b4", + "0x9e3bfaaf4d25d99d3793d5c04a77e3ffa3cdfebe209999a6d6685ce8ef2ab4f8", + "0xa8b1ab6d1905f3b560827acce70d06ebf2b558878e0a700426da80e2a48ccddc", + "0x271d2813e84f174a1740b33ba06db991512bfbfc3ab48aafcb945658c786a657", + "0xf70efd04258b206d4629b7e654fbc344c45fd9fb000c2ac959847f18bff3dcde", + "0x7f213e097ea83d13a94d6407fde93ef1e3e6803caa774bd1dee5c60e7e6abe25", + "0x3edc5aba271a2d8f805cb712ef85c64f2e73cb52f5126f78580e495bfa25b705", + "0x3d75dabaa0a7ce2f69735f167fa0263086273a083cef46dca2271914218aef1c", + "0x3df55894d4a92b2c61df47542eb2c6bb50e37c05cdb1630ff6fb9eba7c1d2352", + "0xb315c5166ca6846a3f3cf516207eb8b23cab7ee6a02dc1692e6904d907d67311", + "0xaa1b93c9975c396818140c971fe47ea7ca4547ef7f268374f76b0ac766768d3a", + "0x4b25f2216e2ef4e6410881daef5542b3f3cf5cd39e52c29ff1e465db8f6b9e77", + "0x7660bd80593c2e580ff16c0dfa8e12a47b5fdfa03b78c331717503c452a19669", + "0xcb43e9159b858181696413ac9e92972a5e22b91b3d638245b55e1a4c47321c6b", + "0xdc6d9286113895c82482824596f131686f4903169c2ff4be9cb506c3094dc009", + "0x7a8f27020af0b46c6e1c380d19da56e52161e7527e52d9312c1a245c70110948", + "0x751f2382ff8bfd71ea046973e3a8096530d69ef3065609f54d8d436ce7a4e916", + "0x94a18fa3acaf6080239752b9d69bda3ab75d8c883f7e18f1febc5f39e63d03e3", + "0xfc1c6e41d8b1cfb09fb02ce01d124614625a67c6d25a1a33c550984085d25acb", + "0x6121ef02f5e24d8d6ab750aa9d7f5b947c4127493682389473f8c38b545805a4", + "0xa894e9b6ce9f6df011bb79ecb68fd3ecd841f0ce5b2de24b767c9b9a7a50ec32", + "0x7aa32fece43560370544043b46f6b5da06790f85d4b373b696c9cbe137969e40", + "0x761716f646ea04a36022abc36cdcd26dc8fd14f4629fd9a5f918c7b514a6f791", + "0x8486732132cb658f3cc60adea8a04f9cc6ac1d5be9e4e6183102201549c1efb6", + "0x7bf195b422a7ea33370e216783de37c77a6d1fb83df85f03991ab2d9e8a82e8f", + "0xd4cdb032ed8257b72b93f48cdbccb360f54a911cc39af990784bde57aaf48715", + "0x4cef9f94b85dedae772279746d1d3b36fb678fa2e6e6cda1daaee3db987e6b14", + "0x61d0b8a77f1264d83383f5e046e0b5b47827b56498d9797db0de1d441a6749b4", + "0xf5ce2cca38dbf5329db87fda5ecdf89fada7347c8f1929ad1ff25bded8dafbd3", + "0xee6888f262c4028e1ab6dd4a045bdb4fac2802c3774af7f2637e4887ca95fe00", + "0x0a586a88c47d4397be9d6cbba41396171f155f4cd87dc95ae024127a791e3ec3", + "0xb716f8a5f2503ce289da1dfc4eca1f15feab7802df296ea30b5f6314d31a71df", + "0x4f3bb41efe5088e72d94726a6607d017cca7277f089cd8fcf06d9bdb568829a2", + "0xc8fc51e1d5787831f5a7c1546aa3a734123942e7b9786c9de583e13d8306d134", + "0x6de3fe9fe006e8e43c81eedcbfd2b60bc448d3fe6a824f9361e96c8880b4289c", + "0xd9745c66a2a505b3f943e800b336d1aea8ba06569e8bf1213ce57616c6d9445e", + "0xc365effe642eb4c7df630703e3aecd63b6de6d9bd323e67ba7b93a4a8051d9f9", + "0x21c5287c524c66307ecc9303617976cfcc2823fca64d56844aedb4b5372f368c", + "0x923ed834691cadab877b37885b7df5445fff6180938b0d0b9152a580ffa6c919", + "0x0a2cc40c9c4cc09ebdda2257764222381514ccb1fd72080adeefe3a4ac5d06ee", + "0x89bf80a9fb5d554d174005ed50ea2d41a3770dd6fe50176628dd9eee3133977b", + "0xd77682680b416567beecdea396f0bc2207d7bfce04f1cea872ecad9a96b53aa5", + "0xb05846ceaa3548eda3f1bb90fafe21788dfc239a699c1aaa4720680810b06a0b", + "0x1083854c8fbea0dbb9472b30bf36a43a900a90b4a2a3ac8b31f24b243e7a5400", + "0x463dde1187a5a2a9ec0e57bd2c214875c8050666d80859944060c47b4a4fcee5", + "0xc92fab3509a4fe195385c8e84cd43621a953407b30593c164f22c26f0e7b9ec8", + "0x36917af90bf6a4db131fe6bf6ec22724f2e821da67cba249848d5abada57807f", + "0x0f41b7b99a441ca6ed2cce0916b40df995de054d775216b0b847231b0c2ed182", + "0xfaba4f79a6e2de817a86015e8550e0d0a8b6c93aab410333a643229ec67f83fe", + "0x3ce590f144fc33fd92c72f7120de33f63aeaca4acf8b5769a342004cc2a05578", + "0x799a9fb7fc9569de23b0efe5f387da912b0400570c8d415270faa2f056fde45c", + "0x93aaa0c313f4794385da8f808958083198c14b64f3f645c5e7305d009e28f0ae", + "0x4e2157ed6507e52be473f2adf7f478cc779017fd3506184bbe2a38c0ffbf5988", + "0x9bf676fa108bd68e0f57d3f5410e908986d0222b1977b62c87b61dd137a07ace", + "0xe30a741c1d1f861617c6b61672fb2b1d4edcc67c99daeeb6e44e86fd5c0f2209", + "0x140c5a530db82022081f177b51ceca28aacf3d5f59c5f6782b217447417a8c30", + "0x2a279e23bb0c34ee14a8531f5d45f9f8ae1148f2b00a73b041e7dd4246bd8b9e", + "0xe83693cf186380be1afe6fcb87d8e06305262342c2b819b4b5dfca3991fc1d01", + "0x26dd3ccd3f767c9321df9a8d407b8fca960571837c40b7560578bc5c03bd7d08", + "0x4d51ecb3ab53c4a6179f8ace3bb06aa318f9f4a16e4174eb543955276a1048dc", + "0x792ab90195f82908117f140370bab5bdebe43fa911c3adcfe25b935f22044513", + "0xe89c377f0e72c8a033a13894bba71af1e4dda5f6e928cab7fa8571dd69117462", + "0xbb259534424d234962fdc00931486d423e54d102e482e9835925a05e612a82de", + "0x382830d1612ff70c5efc6ad29981139af9f263157c6d2f100e81715c889c3e27", + "0x2e902d862493442bdb9b4e8e259ea013cdfef23552d963c08f4245389ebafc18", + "0x6ec3056b95cfe2ca471efca5251dc28dcc93e673bbf27b7fb9d313d6db22084e", + "0xb88864d6b9708c29dab3a6d7894efca6c6df825e2d904a704b94af7c96938edf", + "0x5be1ac4c597702f0f35f44f111030697c3881d3084aa61cfbafb17581d2b403b", + "0xfc7c6203c77ef132e31de6a46c9ceb9f03437ba4ddf26d4cd22393b1a73e1947", + "0xf7cc33a3e756ab7b929fe27306d4f7d6871e3d871fe9617faf7682be846f0ef8", + "0x7c0758a2e7c4d77ce552e89296c6b1c9ef4fc467cd5ab9ec77a4c9bc8cdcc78a", + "0xf89c6764694fccf062250ea74594f36aa4a36eef1212933e5ff2f4159470456d", + "0x4c81757c0651715991a05991768b30fa25b73188abd46a446dc2a8819b047f79", + "0x8a3ef84829d2db51c787a18bc824a8f32ae6f3f1cb70e084c641f389179bcf67", + "0x7fd599ad4c211c04e34c1aefd483e70d34aacb865093b91908766135bcf72ce4", + "0x2aab7840a9b07af86438e7e2a31fdf65fcba9bfaa17cbe805a489a0ce14cf6d5", + "0x5b9c528cecc185c41b79543eadff8d4c50888cfe794f1856098eb8c49a216123", + "0xf97383245eabf40967c51b65361b1cbfbe550d65711c66bdb05a0dfc10b6aa36", + "0x82ac8892d530a6d4a6d964e6cff13db6dfba260cd9e14adc362368cbf8ca176f", + "0x5e56a77e0b825e89baf85168a7646f26d4e22c0795c4d7760fcbcbdba0f967e7", + "0x38071a9dd2f910780fa76dbfd311348efabb6fbbd47b738f7c0796a8c341353f", + "0x7151d72a185a81ef3e8843afe4331fa5d3f7f5dc9a7e6afb8a6c132aa14ed8ed", + "0x0dc6a91a96f10a891cff008dbf1c0a31534c88582284d242bcf4a845ded5956e", + "0xa18562fdaf28baa008fbfb55afc2d333db8ded12f5fa2eae9f300a511ded6717", + "0x8c4780b696e3e38a4562966f8b160cdee5bbd07f626f7ed5bd4b1a4258d2851e", + "0x955e5d665b6bc6c8ceb122849795064a758d9bd0f963f39a6e444f1ebb2811e5", + "0x1e20c55c2c1e557bb789bb8c02af53d0d21a8e91cc0bb061e921f9adc1d9d052", + "0x7645332bb8de8f03d17cba99541ad0b333e71d0df80a50c1f7a11ba7b56b29aa", + "0xe826b46b23cb62ba1d841b0447cb618398e992212ebe1d0c268a871d928d588b", + "0x40196f7636bcffe35c5e8e44a78e4c8e592ffe727921debc7aedbfe8fa8639f4", + "0x219cfba07695789f9a455e4342385b79afeb8b70ab6dd9ed809c5754af3ea6e9", + "0xfb68fdf681293342ba646b084059d7c4e1d52dfe14dabbf36411f33d712874c4", + "0x261ba631f523612b2c9e4ba0a8a86ee94787f05f32d5433640dbcadccee01190", + "0x9a141f533e2e0880c70446969240e7af332853e613c355c140de926bf34f233f", + "0xe0359d032dc7cbbc16ddeee4bb97b23af6fc1c2b9d8166f56b63d47da1aeb316", + "0xe05b507934f4ebd6dc6cb4bee44ac3dccc680f123d88a3c67bde035ec4323859", + "0x68e9e42d60f33ff345b448dc26022f54945cc2076b12206c2b5e4598a97ae59a", + "0x4c81998869820076b22a0eafe7128b56f9357da70776a821655465cc56b7bd23", + "0x771b284cf5f962d1f5cebcf48a01db725de236df3d7b4dfcc1d1e9634f1e6d69", + "0xaa070de4a4302f3f8866fbcb31e9d35b9c1b404fc8fe6b2735387367962cc4b5", + "0x8e22180084c328d869af6a9bab6a36799647cf7cf72b221137f442d1250409ee", + "0xb18def68022668eb87676e2e7dddd44865fac914c452512d1c7ae08b4729f083", + "0x1064e0cd886d55838769f4c89a7b3ac0ea09f49a303c6455d7b14e165e728af2", + "0x42c52c9829ff0796200cb9e37f96b8688271cc2d6bee8642332f5a2de4fc1afb", + "0xdd0f3916517394232bddeceba46e73c09e824a59ed598cf13ea6ce5e1eed9b63", + "0x3bd113c56ea2fa770b0599cf290f57e8666c6eaf9c2c4eff5477090c233ff6f2", + "0x3c596b1409bd4ce761faf0256fa308dcec86bb3e5756f6af1cfbd720929907bc", + "0xd112c30575ad66e3345deaff453af2f74a1abda1ecc23efdd6115ec917c3545e", + "0xa4b7d1e86f79915e28c06e64909cb6a706908ad0122e7faf9bfbf530f1fcc52c", + "0xc71eaa6a22e1839835a56e8a9bc9b8e1eec99f7d2fa21722930643319f1758e2", + "0x632f2badac3464d598eafb496f94af625849934d551d2247c4df2d614069a75e", + "0x96754068ac88c6537b6152c949926a7223431a977740aa52a15d85229de286e2", + "0xff87ce8260854266d1155b7e786c496d8058059fe168d89d09742827c8e03994", + "0xfc3ef4fe7019d571816bc0dc78176951cef84e133dbbbf8c2e6832ac1e9b72c4", + "0x2295fbd04243d21f3dcf71c082f8a170085dffa49cec38c5a6cc5f1dc8bfc41f", + "0xe31f834d095dc68a3fce39a6ba88b721693d34268d5758573a818b7c3f165e6f", + "0x8aeb0cab3cb5aea1611d1c808c968f30fd2d1b15ba48eb29badf520c2eaee5dc", + "0xbc12ea5f9039d7872844c409dd331a0eb10a3f0357646268059b1fc9034bc8aa", + "0xaa4e9b57a2afcc3f467393ca731f86fe4a58824c62ff6e7fce17c2ab58751af3", + "0x209e106807bf643741ee21b00c59c558036fde0c1af3bc9ce65c95ab3d996b9c", + "0x82766af25738db24df3eb0904c8cf0ab0b90f3d0d50d14ee5d7ada3a9f3ab803", + "0x768c1b933b483539c7478507e8a03eb51db4773f7d183d0b0e899eab38ec786e", + "0xbcda125f9e463bcf55ca7f24ac3a36b858e2be64113536fc4e31e699a7e85d96", + "0xf0c77fc7d5ac34d88eff6ee2b96bbd97d7f3847917748666a1704e3043b71994", + "0xc30c1c7034665d3b9512a2f8fa91d1453fa0612207bc4ab5e63a5e2206c447e7", + "0x9cd82588c016d9ec301d6fe7610b3bce775d4fcbbb2ee261698234b3824809a5", + "0x2b32d0e5d566700ef2abf0069c487befabd40636b616a000596c120647c15b6c", + "0xf6137046ab4a5099b32acb25be8590e9cb58e305a0d4d4ec22e1afb71d08ac01", + "0xfc967e8f1e781f896eea0254f06b62de95d480aa15253411e4738f615f32cf60", + "0xd5e5bb885d29ee6e6606ec333d1e61d03959fd9fec0b61b61d9c79f14d74fb1d", + "0x3e60221b058a77c9d255ff090592fa174bdf1c2858c5f524932ab4e0836a1502", + "0x415f63d742ebf5f03d60dcbef18016581587afbdfeb36d8e4cfabc60dc588b3a", + "0x3a4b81a2b2668244b59efef72cdb4d532c00728ba8d97d361da18aac7819214a", + "0x8326fbe8171ce07489788c85d76b2762bd388e602c6f24fb8da733809c223f72", + "0x417b712e9d1802b3ef43deb81b4a7d8952f57d9a7da518a685a5567aecb57e1f", + "0xce93fc490fd81093c2692c208388b6a252d4f17341f1840ff49229f533203395", + "0xf51c46f0a8e70e4372e1d2173e7cae44c24ea0324b308485fe04c838b4876bb5", + "0x0e42f8f01d6dbb13ccac3a4943abee739ae7091f1e090cbb5bddfc162bb6f60f", + "0x3e0c60d1a77fe40ce143164575844daffb997af9321d8f59ebfad5a01e53e44c", + "0x26a4df604e39c81b046233df0465fd75f8225ae958feceba155e44cd992db7f1", + "0x5b516109fcaf88a5810ca80a538f58744374ecf96fed5447c30bc64cdbf0dd41", + "0xcd5d87d521806dc6c8eec69eae186d1c35fa096876e00d5c6634360d429bb624", + "0x06a3b23ae298cd8f670a624690005453baff96346a71ada32a734018b6dfacf8", + "0x4836049aa728f5ef3e155619c11933a10ba00b0539e913fc9bfd6cb788f9bb64", + "0x33a0fc54dce44558d4a671ba8fa9bf4f2208c4a5c7221b6068af95a04a5726cd", + "0x618639c2252f3047c553fcb846a410d77c2cc61b30e8a1d61cd997869cfeb274", + "0xfea99d2e45e8fe5965c56d2eb24032028c3f85079ac8eb25b7c0b22d3c525b8f", + "0x1e847b6c963f7c7466510b0438417255cc311de6d865721a3a26373f7642de78", + "0x85c1bf5959c21ed32b1542feb505c09092f74298ac67e5a735ad051078181d23", + "0x23a2368e13b0c41d659acd6a8310f61bcb551e707a30229c2091dd0d297f7eee", + "0xe8bef5e03e5b55d080af7c5b654e6a68964a6e97a0043f990b1c7bad78b7b784", + "0xa60035d288b1e4df6b7048e0804795ee6d1477e0c030bfce374842abfc138cb8", + "0x97808545a79843954712248a74642da46ee2f11b226a5246bac69ffec8e5fa7c", + "0x021f080c07af35cb2c41d35722b724eb12d7f1e5d08a9fedf31f08e9c720e0e4", + "0xd75d67551640b3d18d8f7942fd08e8219a42940abf1411863998bc18b51b1300", + "0x1ad7b861305684cb0504e4ad024d145207ff24584139124bcaae0300d78da665", + "0x74639117cdd4cdb30978fe572288433a5327cb49942cde220c3a71f18b7a278f", + "0x7cc227b517d7735c35f4a284ff558b2b646c948a187fe833c7e4ba550b07f95a", + "0xf4a4b6098065ba9632294cf0316c7f8a7c01cdea176a1c25afb53872df86b4b3", + "0x08c87c51f35859d5b3d29a0a71f7e4186b2c63b7684d16ab79e9952a3935fdb4", + "0x5396484df7ff23519e205f32ff11f34e0a9a031dddcd9966535500b1cdfdd143", + "0x8f5b523031e325ef60685376da552c762166d6666ac29199580a1bcb47261615", + "0x612cfc00b4238378241afc76349aaa91b4a6d142fcf22df2f7511db9d9a8c56d", + "0xc79d3a63ea0568ed42668b9b2dfdb644b73d425d6e42250130e44ee8ce37ec1c", + "0x0112fc06bb97780e5a810f78d274dc05c5829594d0f69eaa8c3579a0b9b04309", + "0x2a8bd1724d25a63569232ad96ffaef660d59d41fcf34a5d2a35c541e57ba1cfd", + "0x685680f616a8b931d6819cb67a69c70af4dbdbf49e77a3ad55ea0bbbdfb0a824", + "0x07a64353094b66582eb864fc58743722be93b5d5671184cb0297755d667dd529", + "0xff8bc4b2d3dc603df48ec6b761428bd93de76036eceb80245596f96107141132", + "0xfc29dc0186f06c37ae62b9ab7b0126dd5f46dd9d2e8538e9dd9452d7e9637881", + "0xa923416ba3ade87c7aa18df5987a299730e299cd7544a45b3bf553890077469e", + "0x04faa40881a2c859387b64c7a13e76e9e29d87777ee196c73885b8d0a5a4a430", + "0x8f5ed314530fcf2dfef7ba4188ff2a77af57e992e61b555b5d2f7896633e654e", + "0x327a5025299e10a6e784726a42f09f04bfbb09fa9852ca9487ed88b48de08929", + "0x4e7c37c49b307ac665037d49849d53874081a2ffa58785723031433ae427484a", + "0x4e1cccbe77043095cb6d5b1055cef6ebce2730bf12fc3f2ef976e6d1a7755ca8", + "0xf1e2cf42ec44d2eefcc1fb71607b02a3c577f75b2d31745217e33ce386d76f70", + "0x1510402d513f5a49c337354cb00eb9fb0b925f73f1ba6a1889ce04b04a96bbcd", + "0xa51a465bbd0b45dbf9d2c89076701dbef3d65369e1347ab71b40a24a8cab7c4b", + "0xeff220aaa8be47d19793c5f15b86c86725b9d26fd1f301c6d9fe2a5ff8176a60", + "0x7b47e77c993ef8bde1360e06b991aa415a679f112a5ad68e44e72e1d8a5b4092", + "0xd3d6ac12af2c6e9533c5e08b723a0b50efd7822bd3afb7cb44da32853ad55313", + "0x31290804386cd9f4c7732712fe9c480f01f825afdda4f71ba76c13c7273318b8", + "0x3d4b4d8e3befc882c49f0c76d212403886cd945864c9aef0e78e93ad731bf042", + "0x6c78d6319f970b55e5b28cbe1f75126059037deebd40fbc8098fe30b941a9424", + "0xacef4e6fa91c2486069dbbcc4387d6b2564b044d809ab35bd2f8a1eee62c9d11", + "0x6af08268580e898f7444be57386cea5bf061ed3192c0615e79a55b8e0fb2c619", + "0xbad8eb690cfb6e4941c0e0d0fd0c001154c8c763b496759ca09bf7cbacd1e183", + "0xb777b569e9ed159e3c42022c527df579fe92b398def81d784a0bf78fde54cff9", + "0x48f51d98b1cb05babc8a8716df865c241e3b6b242c385c01609bbb2f4957c09c", + "0x1738102392f9cf64b32e52493f1a3bc8479ee2a2aa50082dffe7d9c8d7d262b7", + "0x368dc670fe652cb871a8583c04073003321f8d5277bf391e11b3bbadeebd26e1", + "0x1450f6bc8cc4592de843b9960cd165d11479772c54da431010a08c3ef3e62244", + "0x51483f9de218c0299d1894030de3d20b61a243d18c8b0e66574f9e77bb83b7bf", + "0x8389754c77e0217f9f3dbf644929c5c705238643ff84bf0ce147395c479e1466", + "0x54f05ff133e2853e0fff387da68da8838d7506a8b6d52b443435923699de7a3a", + "0x7b0f67bb1faa8b019c62a0f5e35fe302a0dbe9db11a255bc8c0c1bafd7c1496a", + "0x870869f29a2fbb203f38c4ea3a51247d730ad2eb1e3bc421d1da3fc651f5de48", + "0x939dfbe79102433ee5e981fc72947c9c5bcee643aa43c4c97a6ed80ceafd2aaf", + "0x0feafc9cf6c5723f6b0947feeace1ae81cc3906a88ead64b59c11ee418a50d88", + "0x85c752176a7cb4e7a07e60b73a1514dd5099ec2e5b9cedbe2b3ada3c37691489", + "0x0721a7d68235aa5ea8f10f273aeb57d2017762e68aa220fc98cad62a9bf1e64c", + "0xcc0d4fd395466e80620c29b67ba0d6e2ed6290deed4babaea36802de88830b6c", + "0x34e994badd18a921bf6309c40ef3a51ce1d26d2cc1a75cae7326c91b3f2fc39b", + "0xdc2915d492e915c09ea6819c1ab3ed8bab30d826ac72fe4f03df98431e82155c", + "0x38e9d94b4d6630d2a169f9a6fcd8af95c7ed453b09106044858cd31a4fd80afa", + "0x2b2e0d244cec4524595aa475e72d45cf23ba53808c3828484a50b8d6f344653e", + "0x6cae1ad7ea4e66fedc950728d289c88d765cd3484edff5bd73e05986df3cdeff", + "0xbfed9aad797effaf26b9613636c898fe99d3323f0f52c3405d14b7d732e00e80", + "0xa6ce1f3a454f1195d23c30ba185c89005413e30fc5b5735aa87617d0fb7ebaef", + "0x9c95fc3b15d78cd99d5b0aa5925f34a8603294822283174cc04568e2de9777c5", + "0x427c6bcc21f932692074a9511b6f0d3bdf31d1ab7ecd4b2496faf1abd28050ce", + "0xec9fef6c918e72c558959a83772d0d69e54503d388710fdb0ad5fbb37a2ef552", + "0xab849f77ee0816a5c79d7dea571f70527dae2114ac68cc610fdd898082be75df", + "0xf604acf1bfd5729de8a06f5f856600cc8c9ea4bd1a3286320a5c736f21064a7c", + "0x8c840073c3c0356223679a699f62b458fb4b3922c8acc1a9cd372daba01ac9cc", + "0xab05221ac89b4c4a23c5eb7fc38c8497b5b74e6cb3265c9e4d80185d83b8f6e0", + "0x4904bc4e322b87bd131b327a2b605336e4588a843e0ad2c213c3d01446cd4959", + "0x20e91e94b0706e8cab4345d0355812ab143c1d7ad8a4bed1522131392da315e8", + "0x0b725f2eb5975961429e7ec2dd3bdb6256fe0fe4b8586f4148b7ca084e2536a7", + "0xf63352e23f397d9566697b83a3ec0ffe3ff45c24cf35de24187a0bf0cecca920", + "0x2fadcf11f91a2f925c9e05b1f720d903d7977d369106678181dac216f3f6962d", + "0x2ee2029a3c34ec3658e15baf5bed6668ea41ee66a362b7f7a3460731094e6e24", + "0xf3145419d56694ee72337e6175438f24ff608e95a6ac39f7be82defac428af99", + "0x5e97d6217bd3a9e4ac0eaa2cb3693d296602eea3a0b05a3acfcc2fb6d954eb73", + "0x790abe5058a1a586dd1693e3af3b53da1da459eca188cbfb61caba6e78af520d", + "0xfbc555f2235fa4f33a8598295c3dfd3de7c4aad845bdd07f565dd27c61115fcb", + "0x05091b80279ee1d52118745e56a9c2a8b752d2f1cd60bd9e54de9ff0dbd770e4", + "0x465a6b13c7da1f79c6d49a30efa1d69fe13eaeffc22b0eaed2b65c882be8b511", + "0xbc124a0d9f54c88fee078dda21c6f7fb92acc74413259c08bb8d152747f6a60a", + "0x353b3649ac1bb6a563d4c83108bea87bec777826f4621e77d97dabca3d97d7a1", + "0x9e02d419fe5e8af12fe8d0d5e0801cb749a6cd850048d82dca7e2d8b4b8bf935", + "0x795572eb0aef56b300c67e0d14315bdf978b88592e96cd3331f601155dcb76cf", + "0xb78430b4a0ca13e17314565951504fdcf4d6c8cc33ee31eee9e65064360e124d", + "0x269bf8dda3af32541b1f01d6037c6875eda02843edb9c2059301adf9f32bf482", + "0x01251d57768e802aede265fe777f947885f31650050b8ac2a6ba5cbc54349d4f", + "0xe53d57792320bca570085b117c2172aaf897f0370df81e2b3f18732ffdd22496", + "0x3db8dc736a9f6c66fe60189e03210f48227061fa6fb5eb208ea68b79730f5e23", + "0xeade41f730311a6a6594323167a6064d6d88d283eafd8adc2fe0f2f3ba988008", + "0x0e1e413a61821f41cdf1ae0da1818e66880c692874e976506f2609f6837f7238", + "0xf53cb4b17a240b1c5437e734ff45fafd41b0ccbc5849a89204a1ccf44cdb0a61", + "0xe30436f389e8c2eceb00fd6cf2bb529a79647f00c53207957ea6b9cf87b23463", + "0x917a770dd6765d1a7ca32588d4752617da97b456cf363d6928812cb5704f9996", + "0x6d1b5136c63e6d904305b0cc8fbfce42fac08ffca85967da145cd79ee48d5485", + "0x3170463fbb7b938eb4114fc7b33abb9b67ca4a53f2a764ed3722f9129876be98", + "0xf1cafaaa14f1bc7203547b0b2eabc4c9a34002d4ef694f2f7ba4967c893e7ad7", + "0x7b4e58f9e0bd83f1ce3003eeadd51dc1217534ba190ecb85b56f2f1d6320a0a7", + "0x23149880cb8bdb6407556492a90c36924b03ed339336155981f1e2aed88cfa81", + "0xa6d60d942c65aac2f434234f5fe072bd7c3f84b129e04ee2a8e535774df7da38", + "0xdfc676a30352251d7733e8d97b340b0c0c9f59c27285597f3bf2c6312f68b435", + "0x5bcaeb9888173c9742cfe545f5a95d4bd5cd888b1a432e5f76e1aa5e4a2daeb4", + "0xfcc266d9f433e688105e1fc0e3f3aa8cfd384b29a41c27bf79d9fa4ea25f9567", + "0x67ffabcebbcce2fce95c8b5178f669cbd89a0624b6e7cf6117d7c682074951e0", + "0xb605d371900bc65383747018a46dc5b391e1919616c6f295afc9a0848ea88803", + "0xcb21fae1d94876ba4be9639b02788ce50ff1a3479bcff825b4727e852d11d230", + "0x422efa41f50a5c8ec08f19c70a8ca0ed65698e8553b18b96e26100ea5eba63c0", + "0x5fbe21a7d47a5d60506fc00a4831ccbb86a6620b4f4eb0a2abd02d246239dc3d", + "0xcce700ea0d890afc6ede8cff5626dd821c15d79b5d752ff9e37a4c01fdd2afb7", + "0x9da67a3a21b7f6862a8c94429bf713bdabbe238b061ed50ae591c812160120a2", + "0xfe9179f6ed68974c5d96d50460a937433a77a19c088448e8c82883990360396e", + "0x9b200a7415a3989d3b31a99bbf5da5b344b74674c472056e759bd8d497eeb784", + "0x707c6967105401641c8793003c612af3efb405a2d991ff3419e89703d7da2737", + "0x8e30e3c1b785c7e4f7ea3fec763fe6517f2ab47f52ef4d2918b1e2665a92c314", + "0xcb4355914c2c19c6dec8c67391ae430b5f9f099175f3adb46c4a1dd0d14ecac8", + "0xc92bffe0833bb71ae411605426d651d7f53cae4a8b7f5caf540c6d3df757ac6f", + "0xdcc9853bd5d7ecd543b03c84ad8c60b0a10074736977e1b38a2849a56f91bf16", + "0x470840ebd82baff5cec0f0ac592522f7eba8faa33e44a76648e9446d8e6ca8d6", + "0x25c3a56004c5264b6ca42aa4fc14c745c99faa30fd8758fef0a00437a6682b0c", + "0xe25836d7218635efa50f8dc31dc92ec3a10d47a5dd1cd2714d5f3821d86f1284", + "0x296cbebf47aa3baa0c4a0edce9a33755d529e6433aed5f163a09c41294a9cb79", + "0x972da234aa21872abf3825ca492e8e6e9558b5444f853bd4c177d6e7252343f2", + "0x3b083aacac312dd44b0b5e15075dae8d293bf7579da5bcca3af8331c1485f3a8", + "0xc9f0ff414a9b17077c7c47991ed1356859318840de964958e2a1836ff802a2f5", + "0x6a3e9dbde6d68b56381321f34ff03432a790d76bafd0083d771a58854f1171aa", + "0xa3c0394b8ef58ed4813638291a7953199d8ab93b0b178f4b40c23f15747ef1ad", + "0x6c72551fb06b2ac2760d1b02d37eb94a6b803dab42b410ea66b01a2596b89699", + "0xae8217451af7e5c57ef92fa15f735a03aa9d389601bc7694c770c98b77848ef4", + "0xc43f402858f2521bbbed08aac7ce4ce9f67ec75e9e105abfaa715bca9f55e5f0", + "0xb7e507e166bbbb17eb1084ce0678fdf29b3cc1b1d3ce5fbe33f574834eb865eb", + "0x3d08ae27b8c2981d47055bb0c2256810d7094ec3cbaf0e06f5deb0a1443e943a", + "0xdde323fa6ad983328f841f3042749eb9173ef07c7d1fa9ed4eb4703fd17b3494", + "0x0b5e71a5ba4277d6e58816a1fc796454b05f815b7a9c0916e7888ad473c4d71f", + "0x1f8ba6115a1063918ce42501c54640ce5e75400709e9a4216f845cfd2b5c6ac1", + "0x84c2455e58397683391b48fb663e471927053876c47810f9bfa46498a6a558e3", + "0xcb3a1e071f637472167e3aeb55c272ef3dff03cce7c79dc1497909432567a51c", + "0x67b2c82f6abb64dedf4b6beca2462824098ce2cace46d0ad9bc7f6d1d1f5be4d", + "0x006c1bf5b3dd9605723e2e6e5da9e9bc3caccd4f2e8fbe3d82a11f100a6cfe58", + "0x803ee368b70b8f2236eccb217835991be4e1d74a77c01e0a5616d0dcecd2ec44", + "0x2886a8873fbd4bc33567c96b738a07181b66e5c696fcb35b574353f1b7da70b8", + "0xf7a3258f4e91c33594287f2c2e503bbd0ea5a627ee6bc44df66d126041468d19", + "0x466cd486d03b22453af0d3d652f76b6e31e4481d359ccbf474ac8923b7f2b036", + "0x0ab3f4407599c61e59d5901b338df1f3b55ad7b01369fcbeefc4fcf2e5b09c9b", + "0xc0630b4d0898c1ef2441f9efd52b6e238fdb19a0d4be7523d65cc2bac495d21e", + "0xef86133a4dda999521abeff6907a5dca49e6217b4f391328f312c589f33150b0", + "0xec77ea4ce6e942f87000410c405d735969fec1af185db832bbac688a88bf1d01", + "0x23d5b7dc38c9ac85475f52a7cd7aa2a62a7eb6b77229fd1e7d220243eb8e71a8", + "0x909f9761b5cf1ed3c7e2e9867a2440cf369725e1c1c389f45be1dee868235e02", + "0xd9b95a269a5450ab6308cfcc9263de25b7a638cfe9ac76ec2ed105f4ca3bd705", + "0xa3f5f5d0e50636252a168de7d06168744d3357b02292e3e3b242ecc207086b17", + "0x3cce30658536454438d3cd0a7a246c2a6c7e725a1f2a855ba2b1cc24a85fae7e", + "0x256556b62d17b1d3d414d3514612d4e2d67dcc494c45d3ad68c00fe57f36d3a0", + "0x5023c730446b68e9b1bb25ae5f7f82b7e47f2a638bf7de240ed1eba5fabf98ce", + "0xc811a63f12a3b6eaf4ef44d09782449aaa649b643b9b3a6814b7bf177037f97b", + "0x215911d46adf0a411b12223107d5711af4bc406bcb01ad43f0345558927c8f3f", + "0x83c418cdb1c58f5192bd44711e1062495d2e9953596a64155d1bc7d28d40d45d", + "0x60ad4c885349e2fc765c48c57fbc485c7b592f155e75b439192efbc8071250ef", + "0x2f6131aa4d4d290da7cbd730f2810aa66940978a5c4e459c2a2793dea0989a56", + "0x6dcb75f9cd862c15d1ce6fd3fd17aabac8c5589b1c0c7c73fedd6edf9a376b0c", + "0x72d3242583cbbb9708caf0d53b3d28a3f6486535ecb1077f188b11e314a37908", + "0xbf3339ee5d2d41f0f46f5b2c6e5bc6d9087e5b141a10bef8c74fa39d0b725d0c", + "0xdc88f03018f5da415b873780bea145e74d4e9e69f262e1ce0efdb59194bd9810", + "0x923e6a9bc5dd64e4d41570d38b3840d2922cd92d04d3138c73916e8ee4bbb5c5", + "0xe6b43c6789e11796d3bf61f6a77db86264ee1e59d9baba9cfaf0947242095449", + "0xbcb67e97b66f7a333bbab533f4d6049a094bc6c90252178a6c4a0b250bf808fa", + "0xf70db8a011fbb9c1a44fc68abd9b7e77b64270cd1a53e88c430b55feecbb165b", + "0xb5d21bc6b15db62e4933ba8af93c4730fbcefcdf113731ebc4539aff2885dad4", + "0x219f9ee046f06949a7c0b5ed7bfe0528d8be0a685c75681e5535c001ef98aa32", + "0x1d3b54c8e75b31823610d73296145d9e810b89f9589decd6b11c9d1c67ff082c", + "0x379ea7b0262da11e0168c06b35da2923d67c04a3946bb78d41f51792dc146772", + "0xb59e7d8d1889e4528f8fec068f05da5805a3423afeb0133fd0f30e4b3161bdff", + "0xb653d3d723cb07fd0642a1a97477355226ba5256b41e342972351c3b52e8fd5f", + "0xac559c7f6c01a91534f6646800cc6dc94363be12b23b0030b06ff35affa6b3e0", + "0x17e4dfbff6b6f0d0df0649558d496873d8c9ceee27f2dcb2a3c254b4ae89bc95", + "0xc738ddf0c1db3b230903f8ffd9a6c12dc68522b34c483902f033848275d9fe9e", + "0xe5a80cb3f58d862a9478fb8aa056182f5ea76de86bc174812c2f66d99cb7b232", + "0xe8def67f9944f62c5d5e64e5703f14ed7e982f593d1418ebed1c193b5ebe328b", + "0x9397d45a579ccb13d70b6b800143fb001a2f02c820c2b83b4785d65adbbdc6c4", + "0x96278c28175b4c4e42531aa130ff5b024be23460b7ec37cf03b6bac0911fee14", + "0x951002fa2af5375d475ab00eaf176c2e740391cbe0bf5a04e4eefda13428253a", + "0x8bb9b392a169863cbcc6efbd0c9761a19c1e0bed86857e6694bebb7b1ec56092", + "0xe1fb9b88a362b8d363737d1a6605fc527366ef878fb666c7efe70f709c665114", + "0x580c510a653115c6159d8392baf459acec690d3ad3c7249459ca5b3f2d097a6b", + "0x666e57a3758f477d140616faeabbe32d9acddfd912d99ca372d08ac12cfd7bed", + "0x35ba444899ae5a12804bea45a109117afc8391b8c8710798903937e23f03b0fd", + "0xca6ccb08409b2fdb397d4e37828812e132c9b54d06159a5be0cfb8c584e385b4", + "0xac0d50751365fa1269367d6b72fbd96bd5e7b60cfa35c003f6c62695c77a408f", + "0xc2d85307856c419d1613f346e421689432c73cd2c48e58efe82ed0ef2e6b92f6", + "0xd2e95725b9e2b525829113ac478553960727d1d89c7baa73090424510d967631", + "0xf412fd8d3d266bfb85ab31b418e427fccd73a79c8b205baad90db557651ff751", + "0x6e27c8d753cb6c0e93b884c1785cd7467864c866d4b5ae6513f6a3069c6f4d98", + "0xac0ea6539f6fe9b255ccb3d4906b6a84abe05853fcb65f663b74ee271c0a9c3d", + "0xcb47feb00cc175e1728a985931e1d6a3c43deb93625d2db2dc344ac7d3606ec9", + "0xc5efe5007cdbaa3705a45edddf078025acd5f6730f88424541638d5a45e14ed8", + "0x5f80bfa1a77405ff0c739806e9f1773e857b356af432295377b0a6bb664e07d7", + "0xf3aa0ab28bee4261e494d2e16eda34cc4b7d15070b5132c06bb1c3969486578e", + "0x3352f283d5bb2c09f4cc087a1d9ab8ca016c8e0d4e50baa2e38965b8b2e3ba2f", + "0xa7f7d408fb46c9ff7376c0d8f078c9163d32155b9cc13fdcd9aff594cb5c83b0", + "0x7f96c90b367f83052f1bd724bf8a9da4b254dd33b9621b90b28f1410cdc29f28", + "0xf5fe34032d012dd41dd1532aa610dc48d76140164cc7a97a0ff17f0b16b1f878", + "0xcbb937b7cfb22e02ae9278d3a3dd0afbf1722c4de5c14e058341ee1bb1311446", + "0xa512f4de2e539cbe9937a60b9a5446b3e0520a266aaa34e3d1519712d5f3ee7d", + "0x2af4f902fc202851c835dcf9a49c9dd574ec0390f3007446570a56f5a563b7bb", + "0x87f42c2b3fbfdcfb2a6e85ef62628ebd6ba5016cc69cee44034c0113a0a6e779", + "0x7aa70903add0394c526abce63cb0aa74153cc13ddca42f551008f4d1249886cb", + "0x28f757f0b9f2a1dd7b223b921de5fa5370a2cc6d23824f178f89eae49f5d3907", + "0x7928485e0e6841e9abc341bf97b8383b751e5b36ce6d88c13f73d19aba447dc1", + "0xd3e50fd177a5bbd2fdc29273d85af2b287f101c22e72cf91908cff486238262f", + "0x5e62d4bade9f65cf9efbeb94fd98f817b3f3b92c11411886aa9d4e1d5b128986", + "0xa3560d725603bced41d9d12dfafce0d55ea18d9c40870f59ae4f2eec5ed84605", + "0x0e117035feb824706eb54cb387ccff8dad79769a58912e519bdffbfa13c64308", + "0xf2199a1f9ce42bcf8ce17071118697dd760fbb9c4a1a059cb7c5b5949bcff028", + "0x7e58fe005b5d839f228a6c277f75bad2466cb251f8f95ad2ebf2704f9b43104f", + "0x9adef0a5b4aa3d7a0fc1f3b0a8573ece6c8911e2ab2763fde8f2d2e7a5bb46d3", + "0x40988ccf616dadf1de4d14d751190285b24f84fb18540fa0e53e231929a99e9e", + "0xbf5598f9f6244c484280f45480cb94a14c616e4821481bf76aea4507c662345b", + "0x2fbaa82dc91263f38877b5625ced97d21d1c48c0df122e3d24549052a682c019", + "0xe64d675ea788cad0d5717c518c276bfac1f6168d3e7800717f022aa3bd485cb2", + "0xc971820b4feac4127df9f6c3f3f891602793cd1036a2d455129987f08b1ed682", + "0x08e681f6cf6de04f043fc81c595318247283f72062e91149ab9601a7b36291c7", + "0x4191c8cc3fd3b57539bb0b306ae01b9a2b851114e01bd63e887c7e364b9de7b0", + "0x0bd54268b509684b9c98f6066d65aa6f3d00a382b45ba9e9494caad546bac8d1", + "0xd3a2584df7220a6d088a5af6c5fb59d98e143e9fe6b6ba76acf66a7953d096f1", + "0x7d7b53b7057d2fc76dff28c147e344d8ed41cc11d2240e67f514d225d9336307", + "0x9523535e9cefec69db4c84f40a829e5b1c6097f5a41e90506ccd1cd8de7f3267", + "0x40176714add9162c0efb1db9a2288d76368ef54f765aa705455d3872fefcd785", + "0x734c6d5132835bd0903f10b808d5100a7be35e6f415c8bb4695d4e9c9ae31b34", + "0x0cbb3cfa53a8fcad61beb35ba4acca2f788b1bb8909dc55188193803e0822023", + "0xddf6bb35fbeb9143cf9ab16dd469e639a7bf1c3a9e999875e4585f2b66009c39", + "0xb3b1183398aa3cf726156e1ecbb905c69bf12c7e04ba6a7ae94624308683c0a6", + "0x42a97d8e6a33d675448a2be3ae34cf7e6ce7743f62fa00607df17b069db28e17", + "0xb5ff9cd9b563cbb9a23af67b3b25ad28c7f046a427ceee49fc097da374d69a82", + "0x3eb64c95242b04ffe2db4bbfdd7d7735ae751e2ec7553eefec9feeece2655c75", + "0xaa5b13cfb1e717a00517e58acb319f851201338df347613d40fb6b42d21b8255", + "0x2f830a57cf19fb76d3353e85dbf600160171ac8426ad2ec0bba1d619a91cd0bb", + "0x2ff5876e5fc11047236078a6c395533df10f4f9e625202dbb77e3b3580ef4105", + "0x5ace44047a8a7d3f6fcbd99fbea6eaea2b8002475114c68d87b52ee92e0e0dda", + "0x9e40dbc2e083654723073e82353d04eeb881394bc99a846f36e65a6e1f6fa33f", + "0xdf240f7db1a3ae25ce62f2c5de2343c4ca10b91b81c5c98b6761e09cb6508bd7", + "0x2016603eb3e5a8d4af0ed6ac284ca5725ed78db3552990169a54a6f923139ce5", + "0x93688d41a6589244f9abfad7aa8b0874a16232cb2742290034b5e706fee1978c", + "0x11b48dbca3211ded989dddc13ecc4af4af07461f66972e5ae32df8e171b9ea47", + "0x2559b274a0d943c0a2adb30d5c135b0a3660c2c26f4da719bf9eeb17543fbd70", + "0x072751f2da3381e2d7930c2c0510cc224e231ba91334d566f8cfad78a2b5c910", + "0x8c320b7ae9703653e40d29814b2e8487bb368f7b04dbdd0f3164ddaccf859a6e", + "0xee54b2b7db23eb00f815500847394d2c53bc92e8d99d45d40de2465cc33b6778", + "0x1c7edfd862963d94666fa854d7bcbba1ae9888f41d8c7a6ab0822904c29f4a2c", + "0xa2793d7ef0397adfde6b7ab2c12429633c7082971751622fb52a820adb142486", + "0x9fa9b71aeda0ce009584dcba04259206a54c787b9c19bef7a82a8a74c2cdd581", + "0x28930f137eb87d51b9ecdbf5f08ec7a2722bc4705a17eb746707fd4eb5c2bb97", + "0xad6985d44bc16ad68cd2a2879d63646385bc6d04aef17e1955ce0eb22df42a01", + "0xeebecc619bf6f8fe887ad1d2732e22114b3926f33f7ee04d8862cc2e81352214", + "0xc24cf1b8ec53c2774f4c05efa9e0dcf19bb0187e4ab0ae3f3575868f1d7641fa", + "0x3128318ad24a8d5c904e207e8b0779083fc80e56b477e3d552e6d992442331d3", + "0xc51a617b0112c38a146285e2f72479a9df2eb00a080b85eb8b428f4aea9acda1", + "0x68f2736b108f9ee3f1bf080bdb27e19d5dbd909b0254ba1489539403df80c4bd", + "0x110f2da8bf188075209cbe74b65bfdfd1e0c557a4d8e930fb06d921802f44315", + "0xd0d7fb9ce1785ac826160129f1068a5b4d70cb80bbf733dac1a6b8bb401f5c19", + "0x4c4afd186452e5974905c0465853dae4491b10de1daff0209c7b34d2c1ed1734", + "0x22db7aa1fca1da1e48ff1d1237183417d4fde82e18bc91f68c85d4af6c1937eb", + "0xdb1b59222fa21e551ed37049b3f18575ac85c614efa3b9356c6425e267ba1796", + "0xb9add062103a8ff8c6965cacfe4db777d6802bf2d58928dbf4c4360ed0632013", + "0x5c09de27b05b869e4b5aed028c6a320e8acf16d2711ee34b410a670f971a3a2f", + "0x44f6388d9b3611addffab2bd2be1616d7316e399e1660bd3ef334416beda47bb", + "0xb44b9e2bf1c347316ea0abecebac22e2c2180f5ab74b2e1d682d326be7726df8", + "0x71b186d8abecc3bb03c12c5c8d50410cebbc3cc5f7313e0d6ccc5abca506cd9d", + "0x0dc7739dc6220fedb149bfb7f65f4233758f2da88baf676de378afa3b7c39f48", + "0x34e6029650f4ab1a54f016e8e4e4816745bb68ecfe851de3d4131e5c35221577", + "0xb25084a74f95c06d80dced7f4212ab1b1cffb774d3de50841ec1f55222d2f13f", + "0x47253d73e45e015984711863c639e09b79f1530d6a08cc0ccee793445e04b107", + "0x05ea91871f7c37c1e24b7b89e444f9ab4c040d5d961f4f311b07ef8d62a6ecf9", + "0xf5b5b4ca11d928a2ca40bfd9cb85c353e72b043b2e38cc06eba95c14e24c9125", + "0x20dd19b4f651d80a92e702bbffb767ee581c7984df42014158ecbc816bdec6ab", + "0x8d1e102c12627eddc4ad76454225048e7ac1b2b82675355c25edde73ff0c0f28", + "0x5686f6cf9e909af1090ae1cc7a95ae9d30ca8faf2295bd54560fd5ae028a673b", + "0x83151a84e5ed33c2498a7c047a2267c220a9dbbebdec97cb34a9e9ab46622213", + "0x8938bedf686f9d5be1a8d239dd4f58b75dc76a04ccbdb764b311a673a72de271", + "0x83c2373f396352f1b71240a17256e40a0f99c3ade40c1652cf38a8a049b80c57", + "0xc1e123dff66565b7e94a61a3ff85688fd8ebbde1f8e3dac455324de307ccc419", + "0xc2f0c1a7b263c7739d7d7c0dc999ea8c681afa43e329c116f18fce4cc73fb2be", + "0xf68a7d3c248830ae312be4aa23f3906c4f42fd964d016f8702d05b6a99989c6a", + "0xfa125752dae7ed59ca799c296de954286ab06981524f5a178c350aabeec30212", + "0xc82e6438963d9e598d25e7a0d380e023488b690036e6808bce32abfbf99ea264", + "0xcf5d796558bcde946a3e14dc8cc1b342ea83a360cd16e9b4ce9132bb51ae06f6", + "0x8b0d694976f33dd3d6ab6c26aa016cf856c6eda0138972666e292fde462a3dbc", + "0x0e150cb6b58c43fa8eaeb051cacd536455699aa0dd07f6d98c5dbaab1872b2c5", + "0x9043fd41c741426c1e10d6325b1db408975b1ce79e860486840b660d6db95292", + "0x3d935340dc3ffadf17c17dad6da8cde592d929f4e21cce0305220dc3b2325508", + "0x5a46b98543d295e7c37684818974909fb85536a6baf3d44c5a44f9d6992f504d", + "0xc45d84f3bcc7256d18a5b2f0214011cfb0f6908fccf3c55c57012f9fcb8598cf", + "0x6e783d6fc3213334b934d553d777ca68375707206c9eafd221c2d1887de3740e", + "0x26781d2a08305eee07493b6ba42e6be8b8c8238192338567b00df96489d206d2", + "0x66f15d876f47c906a30750ab9b6f4d2c144e3208c8071f3a9b56250089bcd2af", + "0x62f8926e7ad6aee13a98249b55593747db78d8e8bb59f67e9958236794bcd43c", + "0x712fabb82d86f6bf23e4c6883526a5cc0e11defe6833418deb0279ca73e4ea24", + "0xc31f5e5a45aa61b747da8e58e154a9d9a541e54702e74c068921ee0769193166", + "0x0e087899ac6570fce7ffa0cd96a69a9a51e6dddf0e59c3ab24f7e237d55dc67d", + "0xd5c6a6f5989c47999f61f7e31c478d0f6d11330808ea2e0a196e0bc8d4645a57", + "0x9559a67052f170f8aab9c14d276c33b88b4333dd9a8af59e3fca877e0e3350ec", + "0x2e9d370c75d0c2215d5fd326db616453d869795541b6f53cfc9fff41c6ef8c63", + "0x73de46f9b565024b52687948aa82d4601d1e96e1d94008c390b3145b7127a735", + "0x9852ab9efe7219e4733405aedcca38e0b247d6debc9ea0a6c18a489fb705f2be", + "0xadaea780090168af78c316637a0ebf1a798ed01d01c449b62d6bdbd51c278f7e", + "0xe86515f345fe5b9f0fa278fb5049976c7ce78b70798374c10ddab35765f07670", + "0xfba107700c9e06560d112ee1b52f9ef7634d832cbadeb7dfde901349ff7365c4", + "0xbc549dcd7297f7381c64fdd278244848e38f03fae9368a8e61575c4c61881825", + "0x72ae633be20ccb00ed7b12604ec960365e8db02f2e8783ca44dcad7bb6e4ed02", + "0xd91c4f4e02e491eef0ee58b02f139e6198f8e413f469b204d2b38faf1c84b07c", + "0xf79d7f6e3404e9097afda750c69b7970e058bd197883b1aae6db145fa0c1cf52", + "0xd3bb1807d0672db2fa4f9049a2afaa9f4dfc684db85bd5ad5aee04ec2b168d72", + "0xa6cc463e56684ad92c1f1b39cfcae44d2dc1bd860c52dbcedc440a766569a4ab", + "0xd5580caee93c1bf3a7a2c368e7dda04477104b03247a38ac0c02ff893a3176a5", + "0xd32bca2ff8a70e5bf373ed14971c925636e3d29f886285b9ef8970b63b333987", + "0x616c2124a8b2de88d29e15d12cd94b4209039eb08f64cd04e29b09e793572679", + "0x2e223045475050ffd872f80a0fe9c54564bd380479f4b5cd02bbcd87b864d2de", + "0x8699fb3d158a73df9b3a1639fb9ce3f534182b17b308b9e4cc0313d6ac93f10b", + "0xe834b1485bb9fb74f291f00542b542bc67052ed3b0e7c96d2e11360861d49b53", + "0x4246ce559155b303c6a91c2f319efd01e89ca4467a198c9744c5107908661e65", + "0x90308bf939466f3b123cffd44bf4a842569b7964488bccf3d8140d0a4239311b", + "0xd93af0155b9aebcddfdbc9d9b720a9dfb871c26de4c4f0f67b4eca88bded7dc8", + "0x6425dd2951f3397b63485880099e37d05736fb5d8d80d2509c846cc5a05479f9", + "0xc3597d4097c6e1e58705507914b67c4c48ab04b916e6d214f5f80a4cee16ffc8", + "0xcb0c2ecfdcfd148a00a0c06fe377b1c1e6580f3d2a1fe55089120d94ec1e9908", + "0x99769eb964eea7835ea98be032b9c62b42c5761cd30d8a4618bd11c865719fff", + "0x5d174c149c57b4eb7f0da8fb354da9bb72e1f7dac72067caa3720edabedab8bb", + "0x74ae0d2a8029d4246f8a03ad4233550a7c95d1b4b0fa7a80dab85992295b56f7", + "0x1390b1a4700dd13bb57249bafd476d46837438ac6936dc040f3245fa76662a80", + "0x2c9af3c93f834f6fa3fc665f2b925368105ae057a57d9a5589f0c48c3d9ee0f7", + "0x4ea3e69ce84a081e8359ceaedaafdb2119674a5228107e2d631287e50de91745", + "0xb71c8dcdd17bac9a605c9efe8e8d81518171456599d5bc30fe63ea4b4512993f", + "0xe29edd38e201d3388d08a6ade231b5170447af4d1fcf456cbcb7d62dff87a2a7", + "0xa6c7d4651d082490c85d6d326d260c6fdbb0a136211ad0d45b7c39cf5aaf5fd6", + "0x1edacdd3d1fb057fca283e595923ea6d1dd8a7195d997b2e00cd6a27a6b889bf", + "0x6ab867c4ff5eb363f248c22fa8b2ccc9b11c7ca83513ddcdcf3a4f5409dc4e68", + "0xcd49b05db3b0b727b6ecca54320b910cbf818518c21dadaee24bbd5ba519b478", + "0x26252683533299b7c7cd8dd4f3d7c907616a25c9508276bee4b1df782cd36d64", + "0x3c82d7f4a6f7d8baf505748c6eb2fac3152ebdc8eacb8f2d26c6a1c2d12fca9b", + "0x8c19e63355a2249517614003bbf248faf7abef11f2c8cd9aed4a012428ed4eb3", + "0x00707c183d95894e707d5130b99cb79499d532203049bf57949a6e91637cd8bd", + "0x5e597db2cedc9256a1090f85fe0c8c09d74eca93bcaeb9a820403aa66910e15d", + "0x28f136143a6e44463a4e7844d59b86088ddafe29ecbaf6b2845db06ea63e1d33", + "0x98f9d5b918f8bbc3989a6823c21323c0685fbe83269a9cf5718ba4aa15088e61", + "0xdd1d9b00e5c50b3a3239a4d9fc1c446982e8eb917301fa73098262ce63cc9e6c", + "0xd7b99f10043852195e313496edb1f1c99daed1a4d1446c5db0ad97054e5f79c9", + "0x7547c4670c94690b72a5aeda6390f14a5689d0bc91dffeb7099d1ef5a32d2ac1", + "0x5879b170ffa4faf3e2b25de334d7df5e13a8100807f014aa2ffe51fa21312dc3", + "0x7577eb963bdd8f07f705df927f85fad489ab4382fa878782bf39e90efdb2b4ec", + "0x637b64ab97fe9bca75a3a3370867d058c2cd5a5ff0778a3981cc3b3815cfe317", + "0xecc76a9b749bae5141d78f294a5a9d91ad596161132b11e7688cbee70551087a", + "0x4cd31c3e30cf967ad3b694ca35aa779f9dec021e0de7632c8391d3dc63258c54", + "0xafd7afca66b6d6f8cebc5e51b6c6759e0472bbc19a36fab63947d5e96112b7b8", + "0xcfc3e1f6fb45f625b483502c59b50ab39072cba8919e094c6e90672c17e2ff18", + "0x51c5b912521cc2cdaa43eebf3286d4ee6d088b68dc0dc50a009c071ece578a35", + "0x369573bc4315884e5491d26535de42dfdce63cc968147ccd6db5753d7f4f2c6a", + "0x3aa168ab4de13043f004c68bb8fb5e2d3133254462c547dc162dc5d53bb9bff9", + "0xf4c18912eec21625d5391b192ca70e400172eaddf5f90194c1f0c0f221a92b07", + "0xfd459600111ee208660af206aa29090b50110dccd16bbfda9b901a110ec7dc59", + "0x53fbd2621850d1a8b382ef1d969976a4e49e6d86aaaddd61360c8b1c087121fb", + "0x65b9b8a70bcd8ec0c60eaf22a7ca930667350c96441e8b1f3be027f026ae3eb3", + "0x042955426df71192657f96e5c43b5f25b50f324b10c4b3e9af4b67f0faf20cd7", + "0x7bc737fcf481504b9dd06251bd2a4371348fb7d9d34bef831ea47f9f1dfbc97a", + "0x03347085209e10f3e6c0e6beb547b10aa34095055826946a79d2fc8b5a50c6dd", + "0x724276a633c4b1125f56d9ae780ac6ff7d349a61ec243ced6d1adbbff721878a", + "0xa1674ace5a21f9da01f159adff3bbc47a5d979f1f3ed24b5002cba8b635c6ebb", + "0x20acedd90a9909415c4080ba7f533803848f0ae5b0f85c1faee5aa2663267cd6", + "0x30bcd6f7b6be20bc415923bb0ba7172d89411709c2b384ccd2e0e17297b28fb4", + "0x33d1f9a4d9a181e406269327d52c678887c98e72cc04afb51c90a5996e5802ed", + "0x4eb9d1a852def57f1a229220ae6195f3eb6fe63be54bbdf051ce2355a7701a5a", + "0xaa34c65a754925b90c24e99a39104f72af2697d46599df63990cd86255fd8b00", + "0xae4d584c1a94c421638e5e911e52ed8caee3e14fa542e539d54c16cd8a4ba2fa", + "0xc21072d05f80b72855b829c707a9b7608159981bd76aa602add56ddc942dc2db", + "0xde2fa2c048cf97e20f301c904e084eb66d56dc9b5819ad2d2cf5375274b4f366", + "0x9dc448d55b0075b7a32a15002b13524974d01fcdaa3a618c881e50b188487684", + "0x9e03c99cad4a429e905780166901fb33ba7b71299242b494115b522376a83ff1", + "0xd06427badd4c14ba4b19b68da863ceb6326e569a2f6194df78ace4b49fa7e114", + "0x178836221a4ba66b313746c7388528f85c5fa2c351bbe4c9731e1d7efd4259e0", + "0xdd3b704767e8b23ff37de3328f669ad8239482cd80ebdc4a781e9468a34feefc", + "0x943d946fc3ffab26f191e94ac272823a4a5eb9a3e2945dec4a6f2af066934824", + "0x314decea5bdb836470bc8ef86c2777eb670a0a54f879d4630d42a1e6646c84a6", + "0x916ae5578c623479c355e0cc73b5eac721c3d2c9a11c05cf3e2f17b6513cd27c", + "0x8f35bd869b8bf429c3914315cf00307058f69fac70a434828ea969c9be190cd6", + "0x3a53b1c616cede6f46159032976a07c7c0324c045e4bf750c43fec8c94b86f05", + "0x0dcc140d81a48811d31a51da5f5612fb53f59db3e479b0a879e37c30701c0df2", + "0xe299c53242ade31e18d552753af5fa0c7dd92cc48606efa7c16be21b3bc0e76d", + "0x76fcc8690af76f806cf073d2c02cc2595b79dd174d44ccee40872068c8714fc9", + "0x80f6e3fbb6a5d7b0a48192385326683884e60b528b66ec93b62903390a453325", + "0xc25cbbc6c6e7756a7bcf3c82d7d703c6b11ba794785c84961ceb99d7a6e87f9a", + "0x2aeec7658af9718b2dd3b7964adf5f1357dee31ed315ec15c7d0a816dc0fcbcf", + "0x4d566dc28c978117fb80a1d1ee7af3e71da7ec0d8194e065e6568f4684a1bdb8", + "0xa9e1f1a6e12c56877aa976d452aafaa70b92f301f52fd5b52b9df1180cb527f8", + "0x4f3843a74df70381ade712e3fb33e9aa6ba12592a7a98d3f5d7735935e76bbef", + "0x1b0c88f665941a769129b745048dc4a31a80f084b3a258c4d78cdac57bb5a10b", + "0xde816d761a019d294a49a345b98620889621cc36a5e08b2d22fdb87028e63e85", + "0x99c21210c4f91fa2e9de5580816f43a0a8943c86515320a5ba4e3ca67b5d88f4", + "0x3072969a007b9d88c3f5c581a428810de09ed3be050d176893f51602e286fc9c", + "0x674856f43abc2c3663ff4798b12e07d785a1fc47d671eeb73a381f42aba8ed44", + "0x8df8277a82cd9463e6ff6c8458fb00ee0bc883961fa24527db0f5fa4963f996b", + "0x1c5a003228bb53fdb0b02ba0fc8a97b588addd577d86df8642a7471d345d1e66", + "0x68b7d9a626ad2cd170d7a4807d7d53114da72a879a666255c0b591f56b4b564f", + "0x6b7a5e5e1c072bf792578d26016050dd3fc1d06ebadfc8e31842faecca975b2a", + "0x269ada5cab3e5e4d1758a5deb1eb4682af345528ee0e7cc4e20853759a0dbb75", + "0x8c622924a3e5d610cb3db4469d3ec895d43cc85541680c78ec818a929fea141b", + "0x27c29883813caff3401cdf697cb78bfb42c213fcd55ac811dcf8e2e8fde9a0c6", + "0xa38e0e90ed1ce6a814dd63893dde0d803b070e05c4f947dcaa969b44a1bf00f4", + "0x3235538a2f0f78341ab4c8c8c97d0318505c5ead2ee0a57db2dcce0108f82ca6", + "0x4a43a500a6c2dc21554eb6b96db64a9bc7943d3179c8f6fd3053af51bbe0ea1e", + "0xaffb48790ff8618212d55b6fcb57b19f82881488011acee58be955624c455d01", + "0xaa027a249f4cbed08018c314f457a5458e4675a02713bc9dbfce3140ed4d2f3d", + "0x94e1269802178dfdf96d5d6b59fa515c34eb78d2c720a6fe610d3dc062b2e2bf", + "0x9526766f43a20d9289cb0e1fc6d0c77167d7764e86ef9a966247c3ed8c35aa5e", + "0x5f52eaa91695c4c40ffa71b50dbe2f9037a34f09b107737504bffd0cbdf2bcce", + "0x1ea68ea907ac5d437a7c06c1f6d0bdf04bc5feda609d19312af32dbe00ac825b", + "0xf15071863c22c2761b5ed237402f977e5e9f1f4b6b87c136c89954312364fa2a", + "0x6f149a4fa83fc942113b7716dd2034765f60f1f392ee8cf18d1ff2e883fc389b", + "0x3754f899b9828ca27f279ef8f377e68399cf5ea4d0a8d6e91ba190530ee6ade7", + "0x308bb185ca249d724f820ce333c61f00af6c6129b23efe72d0810ff1935d44d2", + "0x159e023481d1262f127b080f1bccc7eabb727776eac7f72543f0eeed1c822ebe", + "0xcdb77ce3f9bd734910df46f01e481380ea4c93477f2ec7769613134a5b707a8c", + "0x98a5e7041762650d217ac3d16f5be689f047fac6c86f67e03737e2146d155300", + "0x09d8c640eb3d2a91a506e40a5fd000b3ee9bd1715bb5030e1f263d68487d4022", + "0xdc573694e627ecd0c14260700de834311278642e1345ad32a5f2ccb3194a2138", + "0xe4adc5643384788ef29ef39992f6cc3adb9676f46118a4896dced9371eacb100", + "0x513abe3327bf144b4b96bb7a8db2924f09227bc80df7fa06a17035a73c3f507d", + "0xba45b2b75ffcf9e67976059bc115b3e91e4aed6ebccf6f6d1885dfac426cc8f5", + "0xb4f39e619d45f0add5d314bac10fdeb86e56a8bdeae05f329562f894a83be72b", + "0xd30bb7ac5088066ef45a5ffd31b2b866591351c938f9ec3ac291f68822ffe7f4", + "0x43a534d007e4af354a7ecef0c4bff743e3f108e6da423fc82daef4b3e3eda3d8", + "0xb984213a24c621662263254ef407f292e684d61481e1cc93f63f7cdb19defb82", + "0xaf03ab6b6d34441db67abfc7a4b18fafb41ca6efcdb9696adb64a52c3c71ad3b", + "0x4396a22b18b422c83899cf8150fb0cfb949985b97f46960784e94eeb08cfa174", + "0x3175818f93b0d65b73419e893c4c384ae7aa416be44005e00eb8d4986917d0ed", + "0xd4ae4ebd320f697d7ee9d7427465ee5bc0d9b3af54a17c9ef1751f97e6468fd9", + "0xee8164a0618acbc1e0e176080d8699d3603267a6213213ac9504289e1f5fa511", + "0x009ba696b77ccbe7bc6a305d980927c41077164b5d9cec82b1dcd8e38a892e28", + "0xf4bd410dc01d6419adbbb5bdc3c5596e1a24aaa1d811a457725f52b195c516ed", + "0x3ca978f24b2113149685c3b51d3f0409e5d3ade674bf7b28974acde2f151e53f", + "0x59bdb6fe5b9583f56890c43f23ae90dcd42122a6090922ee0d96ac862e05a7ff", + "0x74b8be377d4b3b0b33c884f8cc0fffc3724d81344cec37ae26294d1c3fcc55c5", + "0x770bdf64605f64b599bdfb8ada89b0da9275a7519ecbf5c352ac5e84dd6e9c5c", + "0x795924afc1d84c3efd432c37afafcaf25c6efd1b179f6096772a94d7dee07579", + "0x6d47b8b3136562bba482b11d40cb9aa5874534aaf604281de9611bd8b725ec74", + "0x624f12373b731ace6fafbfdbc8650d2049d8f412ffa17b150a7826db1791e013", + "0x957b3007b67262197c18e6d77f64fd1623135f0e7c089418d6a6a32f0f50e395", + "0xd3c9010de661f6b5235f15ea575f92a777071d7ac954ae639158f0ff7c9560c8", + "0x73977a08544b31d31dd4f2080b16b4312c1cd16007841ad953c6df4bb3dd6097", + "0x51b72fbeecb15e35dfd6c4d29a8bf473de4e7b9ae9da4f44f73ce8a0cb125358", + "0x9f99690d431ba4a867add05071a958874a95ad046e4c341d7d1885a7a9f7dbf4", + "0xa3006dcf618b241a301169ac8bb4f4d4bf63075d6113b2f7ae41c7cd3e0d700f", + "0x3fa97bc986022d457c735868a1f890440c3e04ce1e40066048c04fde69a4b150", + "0xff64aa5360308a3262d93164e36c1166dab2e1aa14a105a48f37d5219ca24daf", + "0x62d026a4a834316c37e594bfd3935e2d589911073da1b003afcb21a9ce33e3e6", + "0xd1c9d96090d9f4d61c4d280f12eb3045104a050d71cf7e006df39648b001f67b", + "0x7b5dce5c58d33b0087390b1258461daacf00e29b83ac5adec1a9d0f798032ec9", + "0x4459f724cbdf5984f16d24597e7cb84d2c16e51916207b0a7dba7923942f7597", + "0xd0fc61f9aadaacad3e7e3426c0f60efafb4adf96276ae5ae2134f2c71e236c4a", + "0x4c108153801afb48bce30d24e2ab8c1f518e368cb6c08f050698be48069be47e", + "0x4cd77c865e38e19a413bd94ada41e280cf33ab9aa2195e36f26ab223b94c789c", + "0x53efbe86d949faa29c8b418844c22f2ef71b2e3f1ee2581bec31e59e9854eb14", + "0x05d6ade931f120bf9e1bc5a8e99bf20dcfc1f509614ef4f0192df2d183672c53", + "0x57b3751bef9456a93bd1c020d9c4e86108a191e4279eeafb35c49ec361c5790b", + "0xebf55678f81fcd6d3fd481572efd55b3c8152ac57040c64d42575513c0f4d14e", + "0x5f0cd6a0b46d906280faeaa2fa5afe90a70755efd1b9e90ff108199a3742538b", + "0x74fd0cae6c801fcddf6ec8ab20376b2ad26c6e3f044b4c022b596a630642ff62", + "0x1111166decfef28753d173656e77b78c34f03c70b1cb46be53d5c632f787323f", + "0x9a12fe5d88eea308d3df7c67efddcaf2e57a4e8a45f687d4657334847f7a6fd7", + "0x02bdc2d7a252db3da8b14424fa5d07781bac2a9d537f316ef9b00bffc1a6217c", + "0x457329e8214226ee83492cdad7b0338ebf3cc67db5f731c11a5d34df0b598e60", + "0x67b3b88905a3c87dca05e481379fb238ff483e60f95e3ee61acadb3038fb5acd", + "0xd79325575991cbe758509ee5fc5fe3087cc8187878355af687e1e1d2d84af006", + "0xd5238d2d02d66f522498e4bfe9565180165b6df5cd01ac83d339ce0ec7b33aaa", + "0xd970af80b676a553afa639d515d75ce23abce0987ed096ed2fd72f60887b6216", + "0xa7d977e7ae32d940ff548a0d29b1fb9047a32506339d24fac73199919c6a70f8", + "0xb43c39101d201efd36ba5e68c63e4bb9d936777a70cd78bfc85e2594853f028b", + "0x897370e13fe7f572111daea9cc7371c723988e7c2821d5f6f37bacb787c55092", + "0x28a5f97d9d5523dc3df14cf6f75efa29c312168aca38c9ce3ed028a0f5fff00a", + "0x89a0ded75d6f1e26ba28526c4150d7744241455a988b8bd5e2442c1904446485", + "0x41b871d468a3c5773d3b5e2fb9c225f45273a1369bd8b7e6965385984101054e", + "0x20ce8065bee6ad915596b6defc756a889fc9cfbff31c42739fcaa44a122b3704", + "0xa878bacd2c9f0f2b3a315d5969cfcf6abf23d0b75282f1bf97a366cf78d9a7e8", + "0xc2159b9848d52491d5e650b0fd916aa67cada03890c9cc3f0e04c55542025aa0", + "0xc61cac30501962fe8a6678bc67df575835c8035476109f166eefa9566822e66c", + "0x18ecf3f25886405670db1beec3a2230b62bcf3138d85425b760a4bfdc1f6ece6", + "0xaa8d47e83e732229b479914d68d22e96aff29fe239531f01b5c7044138421453", + "0xfcd8ea36762694ec4770c7bdfd6667c1536781d365d6e749ce9fdebfd6b08405", + "0x8e6c658e16b362c0e7690050d5044f7ffde556aa3462122f9c7aa0da799ca809", + "0x170b4d3f3d1b910bc29a77b3b619f9cb4b495b2448d74c03af4b882fd76c7aa1", + "0x59e30f11a043d96e32c2cfdcd806fdcdb82fd19922287f1c2a58a6e4af22cf22", + "0x2246e92d86b4f35dbd12ba9f19b132e784c0491ed4dea8aefa965a1474a8b87c", + "0x001f5aab685c991b025b6ecb437133bd3d8ca2828c8908613134ff0f09f57bbc", + "0xd4d84af4f2a99545cd9a230dc9de5865770c6dedc11d8b893210d02979abd597", + "0xedaffcca0cecbd4504b6ae66173aeee21370d1a7f3287c6be6712f1ebafcc09d", + "0x6cffcfeab6931eed79f00126fb5226b9c5aea5cba76c433b4fbd6d0ece129682", + "0xd4d6fec1c38cc957be8543ec382614ebbf164904ade5361fa3fa216586535248", + "0x8f144e0582378b50836c57753f780d67833d4d5a1c8116d5e726e903e7aa23be", + "0x9010a8ff836456b25f598e7bb6d256ee73bca0d593f016bdca6d85fdb08f0cab", + "0x1b71732704614823ccc8cc8f9e384477e540432641e6844f38e7ac1929a5032a", + "0x3ea6b7c797728f3079dcfe267c8323c07171d839df03a0b802e5f4ce7da1f003", + "0xae0d92a26268aa5a7b6236811c24b9e24a32f720b0f92f15713dd1b80b05dc61", + "0x7619e6beeacfd6264f95edd3d98e3d3705119c07ecdbda41707d75003c075742", + "0x6ce87db909ab6bbd3d72c0381de101346f805ef2e677256d9808865e13a0c781", + "0x46ae1af8768b430b38131093c0cbe017e08d1fe84ab4f72d4a153d0420923eea", + "0x191d4e8b69e5996bb8052664566d042f17ead2d1c7c8aec626a4c3aecce896aa", + "0x58524cd2b4b42cb1d3f27b7bac010996ade2370f0c013516b5bd8d86c426890c", + "0x79bb3f37e941aa1e4c07b72d53b04d912473a5d11d922b01f2c7b6161a2ed4e4", + "0xb2da0d1b8f58e5a1d85ac6c69020a280740f3fbc3fc7ae13635949c204a6b190", + "0x90fc472a0081d52d208b46d7a8940058aebd79327690658ed2c28a52b6fb2886", + "0x2c4d74d20350bc26d4c868b47c9eb48f42b1ac463891e91bc52ba8fa4694b52d", + "0x59fe2e9b566a27b8daa37d19099806edb07ea2e898c4ae9725d452821e653199", + "0xaae2de855c441b5f23065955511a97b2ad5490434baded1fbe46c931d852e60b", + "0xfe55dcf1cb0952e7e6ee3f94fab19331c04e910f49c95b4d75275b4b4642f4f9", + "0x916b7e1403695dcff11cebdf38ad2d462d8648bc87160bc75ba4cc2b2db78155", + "0x78049c629c7a7ac4064aa308f26b4176ea84849a94f49b8e467e1aabba0fbeee", + "0xcbb2c1807adf2974a0993edb14ed41c71d6126f737d6de2c646c1a40cc2fdde1", + "0x92e631bbd19fe7229ffc24f91f099610668ff9ec189b7be4176edfd9243aaf3c", + "0xd0409b3e58240211a68040e78bf0f91de1abd909537d632ba2bf57ebddc40853", + "0x9c0a6eebb9ce88a368ba838ca370254970927534556cd647e6e0c922f84649bd", + "0x9c0a6eebb9ce88a368ba838ca370254970927534556cd647e6e0c922f84649bd", + "0xc224cca8cee02182012523f656037a4e9fb74cf856ca4a12f5493967210df3db", + "0x7ab386a331c2693e52f3e71e1551b305d99e38af04789cc5f87287fcc122d481", + "0xb50171b00e42be87d54c199ae428225d6075e01c028021c65e85c3f6a68dacb2", + "0x37ce70e01088f293225fcd3e2c44af137ed8e34852be55817cc69a38012adbb7", + "0xbf18b4fbeffa9f62fc38221cd395a2f65a22181ddfe351491e2bf046caf56868", + "0x7bd20b940872320e32de3843a68cf758cf50c609fc85fe63ed986f8985c0b18e", + "0x33e3b39a3bc13586f863f0d63fb5d1568d908e0891e0740ad185d4f73694fcd1", + "0xeba9ca617a1737c9d0c1606e939e7c02b72770d5c73ee1abac761781ab7a5a65", + "0xea22cbadc36b70d2e2eef4d9615bf5fcf8ad308fb903e2ac2fa3a0860ec74e61", + "0x0fbe1ff269c6fee1b2bd4de75cd83424d981836463d7bfe503a81a3fe670ba95", + "0x92024beb2e63db753bde7aba52e0d823e4b527ff7a98ccd78748334780dbf891", + "0x218b983bc40a13ed887333337d7c7e2117da25bb0c0f36b68d67bd61f1a2d381", + "0xa83870d3637fa0509e698d940c0d8fe354757954367e411f9fcf0d336e690870", + "0x1a40110c993686b64789871c83bb2bf51de09a61a014d58dfd06e538ff170d06", + "0x0332bcefeb6b69b9366ea18a19a7afa07f027d7a8e96fd562ed65b6aae0dcda2", + "0xdf04267aeef0c033d154173b995aaf6e0e721fae1deef7d1e350d51d805cc286", + "0x68740dedd01474fbef41f5d9df60c0b9be95af9af6f2595bd4a6a651d4f468df", + "0xe6801eb48abdc5db4b78a14692af94ab5b465a6a881af43c4c3dd7819e32e3c8", + "0x9cb479a37f0beca186327db4bb824fe5e5de44afaeb33085d8558c1fc30f30ff", + "0x2656e01e49d0232b69038549ffa471639a88d8707e28b086e2ef139445a6a8b0", + "0x2e2eb7f81e4a3935a1e0f326a0aefc1aeef7f6d40ef58bdb1842c3355ea1aa6d", + "0xfc5e544c831cad690cecc93edf5486320900f842a5e83fea72b1754bad8da696", + "0x3d4fe639b0e31e3b207284934c200e70656ca2d78caeac8243a42be52de5ebf2", + "0x6dcdfc1fbc7f097a8b1f2426fe575a92636dad98bad00a52af4cdeabe06c5eea", + "0xfc117a5b49b8bdf3e834fbda5b98bc5c04a0ba25ed2539e615c58a6637bdd41a", + "0xb219706539b08dfd3697929e8d4b79742d9065b19559d1e992d895e377d079c9", + "0x08d1cc32cb7aad2d17d1fc74557cbf3c44ec7541fa24659cadc8a2fda488d514", + "0x80b1fb1cf123139e95718210b74dd795a561bb04484d55a9494c8aded4b056a5", + "0x1c081518ad76deb5dee43061f758bff3f7fa77812b9a588848d69900a10b14ad", + "0x40a063d7fd5b339472bc7053345ab8c302ed1a2334542cb9587d66697cdb4b88", + "0x61c3cdb908e4fcad127c927950ad7ad3f701d6cdd6fd8d31e304c8f89ba95ff2", + "0x05374dce35bbee349671a4fb10da5d75d5fbb9b878d364e7315df14f9c2a45bb", + "0xae82e47e66647e5b9c929d367b94ddc46c275b0022b64a8ee2a95109b2a8116c", + "0xed4133e52483d3ad83059283b4ee999c151c3eb077fea4340991d813843c97df", + "0xb30dea186fc19eaa4ff092932b3b4130f64893416b159cc283d41150371995e5", + "0xe993b4b9ab4a334ec05fcc1641d5072fab8dfe892bcfea9456d4a15ed82c752e", + "0xc408a372d46b7feab9003bca62720524a6746a16f39de48b2ff659cdbeb707f2", + "0x7c84c2060c8cad0a2e880010355dfc9be2b20144bc672e2cdca67937c7c9b2f5", + "0x1568fc531d95edebbb8348f0c95936b23a3b7826f5e6a61cf7232473a8f100e4", + "0x96baff53bbbed237e24a1f529ffedbe403abffa992fc6a857b55c13b03df0dc9", + "0x8222682c3a3c0f8c31f7a0b0bfc971c89a950769ec90ea25d5fa5fea9f866c9b", + "0x95b09b2f28656589c72e4d7e6f8a52eed3ac57aa5157606357000cc232abfaa8", + "0x6320f05880b7a4fbabaaf86824978fc7e3e570e9cab3dee4020a4923c21a3f06", + "0x8fd60fbf2233df2acbee079ae074add0fc7aa3b928431301bb479c659e3d71e7", + "0x0e8d8f1e4c90b6030fd80da3b4c89b4c1608bcc4954b4bdde231fb3cefaa9ce3", + "0xade7ad9f19054f07a64eea56c8531ebff943c44974363d1d2d31ad533717e291", + "0x74033f0df4d2452c21e0758c4e25f94920a1e724ca77c1bb3ed57cf9d5494889", + "0x17b4848ab053341898e4aff84964dfa23b6a66f23f29afe9b4c2fe16d4d12d21", + "0x04381ee16c2abc7a24919212498d49f70e852d32a4de319d443839ca4c351caa", + "0xa379099fd21970771f71fa22754a02381d63f186ada4d9e899bde8825567accb", + "0x36c6c8b6d2dd4f961b905ca54fad5e12066ccb461707b6e199f9d4014d70a9f4", + "0xa73c1b4ce1872c3997efe792929d806eda4a6ae70b48892a07475f44938a729f", + "0x41bef50ece36b25fb00b67a13070ee835be00f6b44eb2479f286f24d4294db97", + "0x83cd53aa1de289a1281244c2515498032063d59fbc6b5f8e5c44d9d338a9d05c", + "0x80d0fe53588ac57badde5fa0486b6812eeb78e9ad7582cc244e69a112aea88a9", + "0x0149bed5848379bc406697cf1bb7313cf1bbf4cda4714df14a97b7b6042383d0", + "0xa67ba16dfdae25992ee70ee0a38fba576d4575bcaf671073fdb4883571e8ee39", + "0x8d3bb0eda231ad5f514a021131a38b54d42fb82adabc5ab944fd95058b74efa7", + "0x566fea409a9398d3dc0d02e23022cee6535a4152d71822a12e87f89e6fc54e0e", + "0xfed86a5194cc992ffbaecab4c6db6458facedc117f2273228e44b3dd1f1813b5", + "0x715d58409b185b8f3954662301e2305d5a2b7c66b8e75a319e953f34877a08fc", + "0x54f7c7a20f8577766c3d0e487a772c09edc422313067d22f0a4b484320cacd5b", + "0xf45af799b104b42d7a04da1a4283af9ee6d6724cf13b84f958e8c5fdf079ac0b", + "0x2aec20dadcaddd1e1498d1a54117fe94634ac8b16f630266e81d5e55174f1495", + "0x36c15ad37ba7fdde4c3a7d4c2dd564e5fabbde37b6c0973f63b631efedb6f5a3", + "0x55b19a27f1a5b27eb314fb00d434b683c4fd9a0025129060bd4b4a054a77405d", + "0x834eb9168b20d4d7b0a37fa27c4c391543d8de4b2d2ae586507ea290fd6d5d52", + "0xdef8607619acfb863c16189db66892bc1cf6942373d3dd57589c798048493c25", + "0xee4a1adecd6110b9399b0c1c6c6eb5a5adf16aabb1ccc3f1591cea4229fbcc3c", + "0xfc93f5e1fd8bd70fe15056acf2f75f144a453f5828b0bb6571416f8cfa8b2ab4", + "0xac4fccb535b46e6e247229567b34e9c88d25a0eb6ebadadd5373d07e1ed4f64e", + "0x5a6845f2d670ca6c86bcece83b16cd8da3df1aadae3f75ae87316cc8894667a5", + "0x1f7b044996bbd7344bc29d6cbc18e498c8603468979698df2594d02adeb732e2", + "0x4987c46fbfcf7806a1940c0c46e4f613688d7e93a75a4f7c72305b949746d453", + "0x4447aeba4a46c1a881af349e5da973e05018e2ef931e42054b6fae693f233f66", + "0xb32102b165138eb620e1517139b7927908a333e8f9f9fb6e71a2425a4f33e030", + "0x283297c24bb2e19ed5b49117592c1e46c4dec09638cdd1c0129fe36028c03012", + "0x9a2b265734817bc6d0af44b86594ab3da3fe8c5ba9fd82830cfed1fe3b8c41ce", + "0x855cacc8ff910742d6effd070f757837533c0a46f197d8bba4cca74818c400d9", + "0x32ded7966b48b427481d9587a1d227f79b929652cbec0314bfb8f50feeb806ea", + "0x0949f0bf3c7bb9ca498812d9c12054da39db90a60b58d7a1e123d05fc945f6d5", + "0x65fef197452627a8bc62bc199f944cdfb72aee2d53f9261fa84a30167d063b5b", + "0x3ffbba63dd2cf8e0f36ef23e199961c99e470e6695ad181bb591ad83bd9fdf63", + "0xd5a512485a929b68dd7afc6a65d95f9c8c8b6e53e88d8671ec8a60ae4c8790a5", + "0x1570e930a5e81e18ea9174c93efa4d975834f90d8118b9b267e9a78b56437236", + "0xbc405f2bb3585e21660791dd997429a3be715a2251c8339c6763f6c10e0df8cb", + "0xa8c1b45976b8cfe4115439b023df6a1b0ec9a62c72dd486f663d8352fb9e4ae4", + "0xac31952b5337330d3befcc50a3341f62a0b66701c302d73f68d338c8f881757c", + "0x35ad1d8bf739a577df4153c869b18d3fe7c59efa8226de496a32b9c04763fcaa", + "0xb8a1c97a29512be7c5735d246845137e8adc7ba92d5025d66c6a272758bc740e", + "0xc59ee2622d61a5a9bac65c31a066a830b874b92b67339516432b310f37fd9c3e", + "0x6184c1963fdde7c7bb026e5fc70d9500fa0f74f55a9e57ecfe6fc36dc863017e", + "0x5bca035235d48a93239bb717d681075a82cea3adc4559feeba6b0a7aef1c346a", + "0x6fac05d9146ad6357f4d1bda7ef1dbf2f1b9877f3e53932587a6d44675e8bdf4", + "0xb1bfb9b72d3fcaf5f86b97e351bccc858abf7a0892dffc668672483b77c0cd52", + "0x5e20b73f9625fb3778d18872791e30a2fed9777ccbdfaa4a490c3c71b70c5311", + "0xe1b438c804986b1f00ba852f7ae76435f3bceaa156ab65af6a7fa2a8dc382253", + "0x460306f297de9065ed637079cd780bf790276418bcb0af88f46e3ca8b7d43a59", + "0x97c3edb64d06e3320533bb540a35e1a8caa6096899faecf787998925aa9784bb", + "0x10b01627b2a79f77e86ef071b6149bce020e39c1c22c2ffd890eea9e0344cb4a", + "0x6453805b22c6db371c4452404794a466910c1096c145d55e55663a3b9fa26be3", + "0x6de3a30ec551cb9f614ea65668063c52666fc2272b0f19e7e3ac8a676dcc2797", + "0x303520ac1536ddf9aca16176c418a85295b37f5faf7eceb68501d8e384db1065", + "0x5b4d450f3fb6db7977429c50ab731c7c364936ce00b7d423238773e501bcb6be", + "0xcc9bc6ff82bab1b18da634dc0a675b067cf1b51a05c0c19c6fe21325e1412934", + "0x7d6d3f69390923ad698577a52c93a41010b118aebeef95af2d0df3fe4a67693c", + "0x12184355ed9661b8f98dd5f81040215b6c55cbd43a0aaa3bca0f6a0a61c22db9", + "0x70eac1b022c562b2dc111b2997284fe2147a04d22f219eea1bb17ce992ce8456", + "0xbb27defe7b9fd38b29cb4cbb80e2273faa8ea4e01d428f51fe40081005fe58da", + "0xecb939b98a74252c2759e95de9c611e86df358beedfce83b6fd9a20abc8e2cab", + "0xefeb771c499b6cd0c361373ada032d80f9566ecc0915169833aca3a64d27d5d2", + "0x7bb20c7396fdf21fca07d37cd981da79dc273abe83723794a5110ccbe0c3d882", + "0x27472dd00377bf669762bb1b9074878b8ef31a1de315a73a9fe82f84bc25ceec", + "0xa97f8636d159f5e3828160d33af3200ec8f97e118c241440e6d48c633efced31", + "0x0da41c448f1bfca8a6d9acce6b576db9d845d9898cbd4450b291a0b4f4249ea2", + "0x0996d4896476d9a4b164c0dba9c394614faf39360228a82dbda76e88ed5bf122", + "0xd144f154026203f43c2e7071cd7e828b6bf39005808112057917238f21ee066b", + "0xe7b176921efdb765b150aa6da3f0d73a371650e3b27eb7256f27d0a406a18cee", + "0x4290c1e39378383573e006148402966aa92f2f0382d791c39cebaffb06aad08a", + "0x4d13e1c2bc878c7b31344203a5288a9a1e0b11dfb5879c71c9eb812b6bd16fa6", + "0xddd16fb9c4f71cea24e2bcbb57c5aa100d873a8fd62a0a3cb6d13e874959e995", + "0x4e3bc1e97f93680b8e9e9982ffbbd3bf0aff782613d43c8e4a58855f8395a505", + "0xc2d429426c1b980ed1327574d2570ace9f7b58cb9997dd88fa2c7c721a8b03fc", + "0x8a9d7ca41f31eab7ddb043c585b6e313ba602cea74cc68dd007abc5c03932ed9", + "0x5e0af7e970b0869937dba01a08ec6cd1d7db420b0fc048a98bc3a4a18fa2853b", + "0x661a52b529d9fe9f15424dfb4eecf4c01350ada05baddd0c3f0ec51d233d7e9f", + "0x465bcf3cb4cc3545a8f6762a1045e44642fcf1441e8e58f595e2fcd2e8f346fd", + "0x98ff3a5be53f791cf9cafc07c49ca735439cec96e8bed1917c0922bc46711dd0", + "0xb1de131581f7b19ef24bedd818e0bd072beb762341dda805d098b3c48595194d", + "0xf33a1a2a3bef1505fb75c386456175553965b7c778ee8ed83fa6645c7cc563ac", + "0x58921cb767379de6055a2e2e0c17229df6e831abb84df15cd7f3fb59e778eeeb", + "0xa08bd885afd40633e8874ee412f258f2c2281ba21692e7e7fffb759a42f85fc7", + "0x67231fd4a6c97c4e42268787c3ac2b4d6fa955049174ad59e39aa1c1905d86f5", + "0x9d3681a8aa467d28823161c0f62596b51f18cc53931da5f22441ca932a68c737", + "0xcd455f6affaef09a241016c11bae2a134467fd1eb9beab9983eaa8df4c38a54c", + "0x60855cc86735a029a2c00223f8446af5e775f1ed7d729db877fbd5269646af12", + "0x246492f36f881008de145ba7f04162ecd05f173dbfbce0ac984f7897a502c9ce", + "0xa944ee78581d27e751a9152e6de48abd74899de156a10fe5f368e77d696dfecb", + "0xcb10ad4ad358581e5de1444c5d9d054e84f38b0a38d9a2aee7498e272dbce661", + "0xccf589cec8641ff9746c56778aeab71279894abd88e120d8541809f3bf6473fa", + "0x680071ab4e87a134fd0cb764b215165dbf1239d87f2671f403fb470aec7e9d91", + "0x65fe39ff60ce27263a84d86adb30e9157b78b69f0b8467c57c6b3fa8f5c24058", + "0x9cb6b925e042be67ccb3a0e90b52d43172e793b61fe1e9664b61ddcd3faf740c", + "0x4d9026b95ffb7922e119e934b736840a22c8df826d8765196c7ecf4280a2608e", + "0x342d9d8803b5fde9b2ed9c942f97ce7a8f81ed9191b71ba6d76f4ecf55d1a0d0", + "0xa0f84683c986e2790fda920041dfd153bbb6d798a740749cc91b8f03d9c8702c", + "0xcb24ba8b93b2fae090c2374e37776fd652de2ced4232b6af95164bdeb114db05", + "0x3bcd1754ae8d9f5618f8e4210e741f833c54e63e967f1db3d82ab3b2e3842372", + "0xc4eebec023cf964c875a7f7c9c7448be8d16e59658eac4a3056520265ae320a2", + "0x110b266f6e971ad93007bee70617777fb1d959f4aa9c1ed65cd74849e918a9c3", + "0xf8c15c44d185b2531d12936e92577a52276a4d2e787bc0b69d362c51f30418f4", + "0x65a8487f6a48a0d65df273cbac5c053848c8e704c67497334ccd4592987702e4", + "0x7afb464acdf2e50cf3f0b061325ac62da76ec1a209ba8d26050392bfb9ec856d", + "0xe8cae559e603d93f60660872b260d2bafc37050861cbbc99ba0841695c99c40c", + "0x03ea4e1e31d261293b1f5b43bcd8f85da15a0b0ca5a453a6b5b716e0a032ec7c", + "0x0bafc876d47fd2096837451b52dd708444bd82288afe13956afb4a323f87b7bc", + "0xf80ac42597b4a9f232b001e29b0b071a5f481a08ec15d8dcc5b4901e530da5ac", + "0xaad434aafccea5d1b30e65ff411fa22afe51e29cfe0bcfca382f3b26a0a519f1", + "0xb726df25001765ba4103355eb29c390f184198b6ca3a2e73c2c455e97e2fb470", + "0xa81345bdf214b1be2533e9143d316f07f433ba2353f64155972cda6bc4dbf6b6", + "0x54550c62af3655d9ce760b73d944b59c36ce149fc9ebf736c09b1d460be72322", + "0x319141846464daa5119563b63dd76f068d38271249bac5b100aea29aee11c201", + "0x98c0bf63f8673f5aea521a7d1ff371feaf79d6454daef28ff554905abadef6a7", + "0x0fd1a839c34b9acdaf86a16b70470c9ea182fc397a2de5f4c05c4ae28ccd92e0", + "0xe7f32a6ca8b0de5e5f87cb41034fbb85ced3ce31ed7d23bce7d620a98748d02b", + "0x5240780b922a280adb40c8490aedbc287f9e88951e0d430656cbf4a3c703fc4c", + "0xa8af151099cf258ef9a2691506127131735dfb07e223c212af11031833b73675", + "0x46c689dac23d982338535450f2f30396f51364f941a5c648357693ba600c4aed", + "0xa72f72f9f8fc0879da6230d5891a650c01fb32b97f9ed8eee3594f9c18ff2fcc", + "0xd208e95a6910aa4008f002ec609747256c96ba08056cd75de24c70a17f305d84", + "0xdb5228180263eb1a887e0922a56d31babc6f1742dccf81427cf74d4eadba4076", + "0xaba51a3e2315275e795001f01aa4dfe7e9dddb877428d5d65a9df32a20106c46", + "0x7ab71d7774d22e5527792ebd0443ac0ea25bfb91663e27e56281e2bc9c9426cb", + "0x4ef34b30d6fe690accc4d7cbf314559c8090044b053e2d9d339d716275a2a989", + "0x689725c53109a6a729d8014dc5785eb7549215f04e089fe6f54ca6b85cab6a14", + "0x689725c53109a6a729d8014dc5785eb7549215f04e089fe6f54ca6b85cab6a14", + "0x476fef91d165adcb85129f10465d25988526d99f73b313cb503539e052a911f1", + "0x6adae7bd45572202a7c20bc0edfb91609a1fcae4f1bad04b6aa228f781a1f3e0", + "0x92ee2d6ff929e69b533102c439027add2279c1a6898ce85105ccf0dcb99e2bc4", + "0x914d5dcc2dd80c0fe05ace51392d0bc23bac27102eba0007161c06ce6f76f863", + "0x48769df68bfbef531d62586087c1a41f9dbbb0458940bc713083f47bc2c661f2", + "0xbdd60106491e9a0e34b032919eb8fbf7c1146775da2e7dabd6cbcc403746b623", + "0xa587e680a48005ada97e622af151cc55ce5ff4252ab98bce756c0f7f42eaa1bd", + "0x7f7361ce125beb90ab59155a5d768394ad5fcde84a0d183b3868ec7215053c58", + "0x407be6a232f8bd754f69d8976a152e6eb8358d0646ca3b19879f88086161bf0e", + "0xcd0447578a94386314a48e36cd18d364cb45309d94264e072e190e43cda937c2", + "0x815dc8f242c74646d2ae87c73a823213c7e3d22f2cb1a96ccb79b768df29f793", + "0x042d164976339fb848951dbe7b2264b103f8a5304d95de03fbca7cd6ce8c4da4", + "0x0218b58d6a069d024e7d1566d59a1b1782134e00cae580f3e00a262b23eb124c", + "0x845251587bd37797a8ffa5ef194f1cd49432a5f696f85c6f1b8d8de966ede7a1", + "0x399604675ee545e21d85584bb0f22250cdab6a1374f198b9f3642e07031b1b36", + "0x6579296b96c7af1e6da12bdb02e15aa90a052a260402edde96bfe78ccb06e742", + "0x22f070a0f9bfaab14166ca36d2ceed0fd4915e1c6526283b1b5cf050a56e8d83", + "0x1043f3eb583ff5b8c8a8105478f5a4626ae1b028bb62822730bbdf74873d500a", + "0xd90004d445f10414e94e8304817ecb7bce392d6b1b3ac788a80238adc12daf24", + "0x0d94faa2b03d445b0d204c9aba5820ee63d34b118164b48acffda53afb39c1e8", + "0xd2a2d8d534f75f70aad2fa93151d277811906affa8c1e28e96a0f3a36aa81f17", + "0x4fc9d173ca6d7c7c9804b82fc95bf1dae3167fcb6f644c5da36cc023b42def9f", + "0x1af1fc18737527fc64262b0ca7dc1eb94de91a774d1af4dede1414ee267e81d8", + "0x8cb64237a06d5d0cb03272a4da76bb2e25085c137ed7b2e2238cab6aed062339", + "0xe666c853b7882292e92b4b391e9e5995049e0ec91c178e9945c6f03e23cf3c99", + "0x33d442398cefc1414ae5d82ccfff58f1475eddbea8b300280720f7d32f219510", + "0x66583b272144b4864aed37554ab36a2e26586407dd7cf12c7d6656c1e14ea7b9", + "0xbe9788c38102b397a4349ba83f4affa5d25adf756b75ca60c301850f7cb2a4fe", + "0xf95489cc90bf9dbf3bedbddcbc86655aba3f97e80deeba2dd6ff4fa9b235b441", + "0x6c1b182c8d07e7e9de7729c207fbb3b9693582d828870c7cbb4e6a67386918ab", + "0xa588fa41f3dd4d2b6b8d866bafa77437a74c9da1d2a579457fb775292b2fbf14", + "0x3763c7fb29246dd52579c378e2167d6b4ca72e50be222f4a150ea7bbb386a95c", + "0xbb9eda257f7d30da6cd9f0e5e672927feed6634a1fdba5f1b285448979222348", + "0x85dc5257dc7dbb03c70a8a0e99cd7304a88ba0f84de85fcc1ff566d6bbdadc2e", + "0x804a4b754014bd760e545151f18065289fd1d00e79ef3761803eb5709a57757e", + "0x105f4d3655cbe7c52fd90ac9322de90b3536301360d33ba07a33c921bf3f46f0", + "0xab91c6fae6b46790b30309176b3944d174e907a561e47194a12ff7a56ec2f4a1", + "0x71da343c6d493c7559f773d1e6bc54babf42cfb584f82f26b25110c37c42993f", + "0xade799bda0bd6566d496dfe6d27b647695c167e65a21d724eed4e77cfbca5ead", + "0x69dea03ad469752445089d755aa80091788428b56d58a92af9fd6d1b2cf59bc9", + "0xdfe96540c353b128de023e8900ad5ba3cdbe4ca5837aaf43b41339a9cc1393d4", + "0x32ad908101197f8ab6dbd9c601b5e9e20d1f24f6188ae0fd9c634f910005261c", + "0x6b239ee9f368b671dff41eac10faaf1d82066b5d66d020d4aca3d5ed9c61cd57", + "0x801a96b8251197525e32076b8efe0063bdfea09483b0e9592aa21c329c7913b2", + "0x1a6b8b63ee4000f071db6bcdff6d5aa90e6328c5b08b51703eee5853b2af3a19", + "0xe6739110dd81a15e5654b225f16bb89dc9f94c97060abbd4c2c6efea4e9f820b", + "0xa119c653e2b4a397688a3f9ace2d538078318a6bb47a77c6895302661574da7a", + "0x421d32c17494a04003971651ae02150dee2a1c7330f38e5298a2a94145b43963", + "0x710d7530ac6c1555bc4022a7876d50ae60d781806ce07962256aa2a2d8b680dc", + "0xa3e3d4d1939871cb40aaf355d36163f7ed467835278d6b9d722adb6f54e4456f", + "0xf27bf53a9ab0adf75b988c9feed508b0613e3cc13b761a5ccf92846a9c7e6a67", + "0xd9d49606a90958b9f8a26154cb710f3cd9365e020775cdd848b4ca870084356e", + "0x928588bd2caf0713d41c32660510e1bff365f1a0a477deec13577c987b165f16", + "0xf5a28f2e4ece63bdae1a5751c30980969f70f416ad86258c10dfb74d5c6602ed", + "0x8bfedc5ec235f154861c31560e8f3b1363f09b8818d3f8336b4c8f153dc84000", + "0xcd52719916e73157cf50d3e6ceaa49a0b39d6413e487e92736b47093a728e33e", + "0x698cabe16a318671f8ffa3c504537b9a84a58c06ccf0eb1c12c20863a5abd785", + "0x57c06b6c0de7cb6fabb05795bcadac0eb700bdf0d9fe14c237e3c579a406b56b", + "0xb8d1b1ab62e09873468c1535b59ee448b28bf552536ece6b525fd139b21480a3", + "0xa1ffe180af83a4f9c7e5f054c8b7b770e8d835cc80fb5802670439865cdefb11", + "0x4d331e695d07cbbd6fa209e439185cbdc9798afeb92a72bbaa8f24d21220e9cc", + "0x5cb949f85549d2797587877f2d96e06119959763ef7a726da2d5bce231465c46", + "0x0a87518ec447fc9c039a00b17437334108e83d48ef494f5c25ac1d48f9d7abc3", + "0xcf0540a99393d68019d53e484efd98f01654cc894c41e1d556c4243e3ce82994", + "0xca369f64dea81d2a8668020ec050366685ffed6e3cfd366c5f0f42e103d8a78d", + "0xb20c35ec8c8f1d106fbbb27f95d6008f67012523b10976b05dcad30812bc10c4", + "0x231760cd7d1aa66238981793fc6f40a8251106e7fa48ee7d857c668d1141a75c", + "0xa100ca555a63153b4bc230ac84eef194528196d15d74b22af27c72d1317c9fa3", + "0x884d4108199353e5049997a380398450a3c1e44d75e39081688c046e2d1080f5", + "0x3a1a25e1132e29a61d8edf91f51309eb0c8b322a65010da9fa9fb5f25bfe79ac", + "0x880a4daea7d20559149742295ccb92426ba5666273de3dba5ece23b1aabf3014", + "0xa794ba438a183b24e38f7f3fcc0f3135bc4535af577467561f48804ab562e666", + "0x03db9d3480e5b0d235799bed18ccce91beddaa6feb67da81bb1f9af0b29b23a2", + "0xb0abad260052d385fdca33b9c7b98af465db9932a9fa7c65882de55b0c11a90e", + "0x33d2aac6fed20b2566a5fc31afa8901763c6017500b25ab8f109cf64657e4b7f", + "0xfaed0faa34b24b1aeca7b2eb941eab706cfbf53e050f87dfede9f4172c5ffd61", + "0xb12ac32bc917e8429b5e6af0fae5ab542011d2bae3d2702102db72e6b98a80b7", + "0x630a06f41f226bf8a88e5ccafcab6a3fd275ac619d89b40909ddd4d55f681d46", + "0xf45571939d8713f2e444bbb16c228d33b8bf072cc60e65fa20477eae55ebad3f", + "0x305fffc5d88b6fb43da382dd144ff8e99990c675ba6cbcf126e7e1d4145a035b", + "0xd68deb3c79d9773ca82193d621a90db945dde07bd8ccff51b87000daa506e5d8", + "0xfa4085b0a5a88b2c9cc59ca145612fb8ef28eeb96c0dd34518c2964b554b0b9b", + "0x7074d590d4f2ea2884cd2ba0bf19f887ef84f00d50951e2ffad5b4013baa4a90", + "0x8dd2675c73281b4b49115d03bf7abe485bb699f0ee4b50ad2e4f181e6f57483a", + "0xad23ad3e3447639ba6a267124932d85ae6e72f11f6886e74c7e6fb548660cd73", + "0xc7ebb33b55cad0309c66628584f8e5cd9c1f3db2693eef8fe6346028444a30ec", + "0x90589bd18d0087286ca3e91e2c668369cd75e3bd1c8e86fb908340ef65f2a488", + "0xa6efd0c13f565d78877bbccc290b2242762ca08e8ef00ef0cc684b7d4ed95ea3", + "0xdd71d1990d0ffd44be30effa78eae7760478c1903f47c880d12299534944683a", + "0xa084ade7881b7475d6ff337323e40104f20f412e79cc629199c2f7dab2056a29", + "0x6dffc4dca4b3cda10696e2a10d5f149c5776304c97cbb926b4a969a1a9520684", + "0xe540eb89024cb164af482496faa0121bece85009c21fe1fb34b1641372b52421", + "0xb71a899094cba94dfaa6183ffa14abb6232c40c7e347e86ea473d894b8b0629c", + "0x09cfee2f5bdf0afd5ed76353726bd764cb64c300e6581fd448baa352f34a3a57", + "0x24661109e5f08ea330c8452dedf5fd018fb84e7848974a56564a9b57acd395b5", + "0xc476085f0208de0f23d24e48b63d47689e0c399717b0d0679888286d9674915e", + "0xbaf7fd6176d005d5c7d3d7084de169ab4bd513731b2aaa92fc93a613caa0a58f", + "0xe30b23d6adafe87e6326e92a9a57fdf3299abf2c785ea9630c075ca9775ea04f", + "0x1e32c1acf194af8670bb930636bac1880dbd23d0c1137d886cf275bdff266bcf", + "0xcbbf5f42c20448cfe93fdaa68336c14451f87fc26f39431efb2ff067d98eeb65", + "0x235a9e686162cab9f54ed01934da90e796b94b485ffa8d5b17834a49a9e98e31", + "0x53c73272e44f9435d5ab641bc0ed1e4b6107909f0bed3c102005b2d8f3d020b1", + "0x2be0af8351c809dc7015a54145bced11f32689362fb475abeec41d8cfad37f2b", + "0xfe3ebbefec5e0a2ceef5f5586c7c3b70008b53f9ff1af00cb67d438c0a5b9d0b", + "0xa3ea7d1fe641d844028fa5544bd911b4c0bd13977f0355e39a61e3bfe89990d2", + "0xb1f86d222b9b125036625da989c8e1caecac1c72b2d274c58409b7d1815453f3", + "0x2bb6778b8fd4f93ffbf40789720942589070d493223aa127d0e6cca99768ae47", + "0x42f61692ad365969b0f9fa149542726edc37e20dfe947ebdc372fabb8331d20a", + "0x4d1949a15bb32ed5b9f927e567e26b769f49cc9a3b11f42ea5cc9d53ab2ef81c", + "0xab2a6ded1fb27dc453ba14f974511e6a55953ca8b37b6dfff8bda6c6ed400fe7", + "0x491e0f58ee6432c8a6072f477fed1b7878f6b8381c394c618a48fdcd61f98f5d", + "0x71ea53e2e222b058296050aeef499740793aab3435cecaba91756ac2289be525", + "0x670c6379ca5e005116d03c0d940f434a39088948cd057de2f4c112d0b0213e16", + "0x2847d88deab94d9051b9bc9e407b5b44c95b41d182991d03018dd7a68c66573c", + "0x1f773ee6631a3020c01a66674e76c9c19ff7cb00b065e54b7793c93fd0eb8ba9", + "0x0797e31c11c59c79cd25651cb1426e8aaa5cb757ae4e98312349b38e703cd4a0", + "0x98f5d53332baa8470e294e3162d2981780772c2f857560556645c4a360c9190b", + "0x7e506b50ed6e8464c0af45e27f2d1d5e24f067f92af535e126f97d877dd48422", + "0xdbfb63b95f650e0491995cd5d5ac9c875eab48c5d9cb1a61601257a567bde5fb", + "0x345cbb129d79e9397637e0fe5a975d4ab2d2a79b3b0412aca12abbab9c421046", + "0x4d9db77a939b7b50c3a28096513ac4e8534b2e6e3860c852a927d26d6bda8b13", + "0x7615f25fe1103a96137d8d24f8ecc9349ba1e9a294c7e1b8414b6ea05799722f", + "0x61ce483b1fa3971a6e54ed7c5655ab4826f1bfe325ef936796bc470b46ce06d4", + "0x96c91901ba374c06f5e73154430929cc5db2ea81ee1ff67ac061387d621622f4", + "0xd413386d09ebe2af405325dcbc0a2304299da84d76d275371e1b6097e364acd7", + "0x028fdc5462080cc9355f828cc5868279b566e6902161958aa9d30ea14551200e", + "0x0f4076dce5353df06833df89b94996a11316ca17e6dc3ab71fbfa6c51e02378b", + "0x68dd1db1622403cbec3d842987a780a6bfff805bee4aca7b46e67358318b5e43", + "0x37212686385376abee96e9aaa82bc9f566c5bbfc790e5a50fe55f1ec5c132a8c", + "0xf350847afb0e286fad32c604d9f1a3ead94909aca8866cf0debbdedfc09a4f65", + "0x5ebb698cb1e6e8cdd0fc5969633233d13c0f248720e1333af5396a97f61cc211", + "0x28c33e154492ad875c3556dd7b506352a7c86637f749af8597a9a6c22ff9694a", + "0x27b2705c5db92fa5bc4812379a423a9eb9f4dc892dc6e2b55373c6dc3dc7deb7", + "0xdffffe55aadbc6050ad2b202b91cd8846f396cefde271f7ca4cd8b28c950e30f", + "0x474557e8abb950143eb4f0c4aa8986258225dbc3a6cfd23ea0d31118de31bf8e", + "0x0240524a7f8b50610e4446e9d8b92f1e7391484743317cae3a14c20d55b13cf0", + "0xd0501cbacc8c3f847d9f028784e0f9dc0ea3afac39b327b97ed2846239579726", + "0xff8f1c11a47ccfc315a24abcf727a026ca9fedb9c30dd9e5b0e59e2a54d9dc89", + "0xb6dd24f100916fbb4f288569f636d16652e863d3047cfb3a0f4d9de8b308abfd", + "0x0fa13f409a1377a222e5292003871d48a556a074a4fd677c383f63530e5c03b3", + "0x0541edf6450e7d6406cd651b64245d5b2e820fb13fa3be92c81d1d61713f8c6f", + "0x928c9626231371615bd71a7f176e75aa358075977fbc731036b953b54f504a33", + "0xe6d92e7b5b2b0ceeb6ed45b875441094304eb3aa5523f8c587cbe9ff82103491", + "0xb70b478bebc85f7c4e946471758a1e3ceb594fbcdb1eea35860f15f83f541f94", + "0xc47fd6d37c1cc2b6c4b0a67936eebec434882d528d30ac97daf08fe591f93a4e", + "0x4b26cbdfeaf459921a757df68f87711a4e7e8238120a7df3a9b33836bab45786", + "0x7dc7a91b2726683d187be90ddac7b595e7dd0ab82f353f8a33c26e91354e8ecf", + "0xe947f0fdde5d9aa8610edb289817ba2e6e3e65cbbdb1621c5cdd2f2e48659d59", + "0xc1ad5044cb02afc2dd89dccf46cf30ff0927896ad3278e9f2eecb2b07cc90020", + "0xb6333962e0e61b485eac3761858699211ccd2ba843b4c362aa4a85eda7cc445b", + "0xe25aff46b5d3170bf67118286cd65dc6acfaac3b0b1c60f81d5d86a62da21fea", + "0x47f8556484eb2d27584c07d1128675da27b0cb08a4c264298839a7e0d82e12db", + "0x84ae111c7569b62cd4326ae2393cf6ae26c75d3c2af4d26c95ccc42f92f9405f", + "0x1d4f72fbb29a8bbc41094f8b576cf4fc60604a2d4afbcefc104959a006cf493b", + "0xe92a4cd56b82d00b349ce98d142f4b0403f3cf019c2c6612c38626eaeab99942", + "0x07762de131fbc07bf80c87e398e1cd852f302b0d90340796dec72835d1bad063", + "0x7321bcf957a2996d8f8c3ca5044cd603d439c3b4fa702cc8dda82bcd425d6a41", + "0x13837f4c3a18190c03d79ddd80bdee2d05f22512cff2851706991651d87449ae", + "0xf6ffd4982103668256e30556ec257c2186fa77f2751005b678c52271d907b3a2", + "0x1346d8a19f8c1fef1fa59036b2206cd53c93be13e2559d9061bb921dc508f2ed", + "0x54a83ae6e74a48234acd73c69e44947bbe120d84edac5f2488b348a300ddd5b3", + "0x74de491ee94f425d28e733295fee8550457666d656d6f9a1e4544e91093c695b", + "0xddfef8c6d300856a80451d0f02b507e40ffbdf3156fbc235416ea4d1d1861f80", + "0xd8bf2bee450cf91bf4d8fbee13bff407f4853b1e1e4fb01f9504011454f66f0d", + "0x1b04dcdd9fb0c742be56a785f5bc00b22e545aae1ae37cc18b7009b43ad78136", + "0x46c60340eb7c2a40ec1d991e8c59e729f63b31f8827d11c7a4cd884116d29ae1", + "0xee3f511767061b0a7458006ae4c0bbfbeb7ef86a2aac58e6a04d76711b7d1841", + "0xe6138e0732614deda1dcf68c0dcac9986f7b53d0743fbf0359f59657bd46e95a", + "0x151b7e4ecbbd44bd9c84bda564969aa03e6d5269533c47bec349dddfc0ded8da", + "0x0cae6476eedf30c7ee581f19f8a10fffb3890c65a6b8a8bacc08f2e981f12a70", + "0x254924d739a895caefb8848b802523bd83036039ab314314b59035c53257941c", + "0x69f6d4fee7210857233be6df00c401e0c4031bc2aa16be9f0f1fefba66519dbb", + "0x799c27b8a85170b233217f59029cbdc16f4eefca24d75dcfcd5b06543f9b7b0a", + "0x98c202d63f5e3310d3ca07a2b2a157b61952bcd314fc4e7dd018587db5f8cef3", + "0x7738fc75421c16b0a7cf7cd7abfade8f9b071a3aeb343677920af20af023045c", + "0xd0fa4b7f748c3400878e2ab7bf77a517d1d348e51c466f28fa9dd80dc042f216", + "0x253de4662c712401431acc7ee47ae6b1342cbff4ea7793699d96260432603b12", + "0xdff3a34bc917030dbe2e84c1a92054e067a1a1854f2d14d4fd7d3c93f3c39657", + "0xd3ee4c8196f2bd21d54838866f0f21772b465e8f99931ea7888192a5555d9ad1", + "0x86a3445ffd1b8311b3a115ec4f0adc2a49b90c6b9b836c18918986ca56ab8cac", + "0xbe86e5ad415af8dc272f636ea4b3f66703b0234f72c1851065e07d6c9db3048e", + "0xc2ba4db4f10cb46bb5b997607de331626ad59919436246aace734bae2ef77d6a", + "0x96bc4175afcabbe4311aee796bf28e8cad7011adc57f807965480073c0784a4d", + "0x21a964964857a6d1db5afd015dbd02a96c9ffd24ae0ea8810ddc0a98ca65174b", + "0xdec40f389bc737f415303f3c293ce305549a522ed645fde7da3458fa1a57c206", + "0xfe92a2172bfb0a7659e32e43e2e224f903ef51535d75853e8c419ffbc5769ece", + "0xe9dc8e5cc7acd8a27b49cf0e49c748de2fe96186c2e5193b878a348f522de8bb", + "0x07701b250564361a6074507c222cef83d1be7b4c03dad6a5a724fcc157437f54", + "0x1a06b9f645faf5008d5ccf3a3c7c2f1d3c1a61692a0ae14354f20c1aa9fa0e4c", + "0x2aa0cd755eb688a0efe5053a40c640179a09c9439808d733103df1581eb09ee9", + "0x6e953b58534a8eea0cf3aa96345cc5b0c8edc3b30a415f2057b3563538b9500c", + "0x608368832c37945348c3e6dea31bd6181d30203a81808d03396d8f871864b384", + "0xef6cccf9e6ef708f2acb6f55c6dee77be03092db6844f96b14d57c342437a907", + "0xdd4f98b64bc9a9c1a3b03a4c674a15dda7c4593b08edf67016443b48549bffba", + "0x4216a9782a46c3cc4539fa1066b7bf4a68bacd25a63e6a6027d15fe224c15daa", + "0x3de70cf16a3640133abc8b5483c7c4780a3ec65b3e0e4051b4bd4c5349051ae1", + "0x1037f75165972251b1dceff970c69262e91433376de7f66b1015cd8b02279654", + "0x44b91509ed91cb075d52cbef33024ef24c3f437905b7f81d40efb1488f631d86", + "0xfd1a481eb9906693b61574d6fb7510dbcc263c2d8cccfe59bdfc7591ddae5faa", + "0x04d27b43260157a0b061d6b810dfb972b8c8d9cd003b4ab6ccf3cb986d3a66eb", + "0x3f1ab46de4ba8a31f187c752b32f5f1fbc982a1aa899d69dec1c773bc79f4a17", + "0xb15307fe0cac7d96b1cf221cfa83cc6beddf9117f91eb3086cd7f61fac05b7b9", + "0xe98276a48c2ffbc2272bdbfd3428f99336501a6abce18668368085824d1e32cd", + "0x314eb8582043d305498d1b32e229c1369ed4d2e6bee851134a0b66f9aff5afd3", + "0x51be3fd23a1fc31286b1cbacbe1baee4f91ccbfd6a8c4c7d6dbd4dae4d05fc60", + "0x18ad81071b1d443eeda15a5ab925487053617f065f75c1c8911a4c5401dd0a1d", + "0x2dbf4c150433dc6fecebb4c479aae0342536b9b84f744d5a961f2e8c3c81c031", + "0x3c94ab9e6ffc681ce77a6a0da0711385bb65eaa0b7a82d8c506de05786ab77a1", + "0xcb35cafee507dcd4593fa624cfbd9c24f1ba781c7e95772ec943a93737d556eb", + "0xeccfa09fc5ffdbac0a9f4e17becde9939d861c7eec8d6208f1b37078e5b25707", + "0xee69ba4ac46ba807db684271a37d70fb3274dde66187d93c59b1f6c463d6aac1", + "0xcab8368c3337a428c60f4541dd97b5e475b580c2f121aae5c596862e572f770a", + "0x3399ee580db19f09e5def1b52aaacbe3eacbb09bc490d50c28d05eeffa2ae462", + "0x246fa31f5d7ab3c98e1fd5a71e57661dd4e69fffa46de0e0d9ba1a9d9b057fab", + "0x036d7f3a8c7762a0cfb27f828aa2632d1b2a224721dc63d8cec3b3fb3d63a6b1", + "0x871086ba0f5835b4169e04275fdbba4916d1a0a4e6bb0e110e64443980096250", + "0xf0d676b3a26715b009784872dfeb34ed84ffcb4f2b6d9b2a434eebcd68b8d0d1", + "0xff9f019b72a4a2517722af8714645b179f88526f75bb6f65e3fbf90403c9408c", + "0x7a41ace23d3b398e36ee90acf845cc864a2b3c608cb50e698999b99f317b943a", + "0x07f4286220cb24a7e81c090cfec68e25f93b016a4554bba423d836d950a3a968", + "0xa106b092e564064e1c2468de60fb3c28aa14f39af8d14dad1bca8dc005f0d482", + "0xdcd94ddb53fd811f34dc016964fe1a44a40ac3be54732fd10cfab2744b0a6e54", + "0x1b9ad989751d1d47fd9f15a95edda425589d9a765052dd72572c516e58fbba14", + "0x9f6dc5eb6e9c66fc25be2e793f2671a0765e1be3aaadfe63df43517a0202ce8f", + "0xdc2baae514f996976aeaf7d0c0ad87d935320da30e4a061bc712b556667c0b9e", + "0x5f911a3031233956cebae2d23dc71322f4502b62461320ef89d79b2dde0890f5", + "0x0cf58cab1cdd4a0534ea33f82f3c589a8357b6dcea1d77aabdc30658812c0cb9", + "0x2b4f22870ca6aa02be983c5d91a84af798f141e48753e00cc314e0e775b7bc43", + "0xaf334eb52bfef4b6c215564149eb1888a2546f14c7bd61c81ad2cc0dc2f49c0b", + "0xca7e5741244cceea4a2f7bb8eceeaba0a60f4445d176888c6389856baceb8519", + "0xcf87b97cb01af0bc90fae600d27e9345e6f002ef171d8e71bf925e8c23e31920", + "0x92bde967d1a24e97b7128df706d62a24a23dbe0f075b4a3856c37e4baea5ab07", + "0x83c299e8979e0b3dbba9d51ab61ac65d5f6fac273e75d7a64baca678481767ea", + "0x4e831fdd2adc9d454e17594c63582ffeda7322c0c5445945005b3bcf3f22ff36", + "0x8c4ca83644c496b278fbed5abbaa29c74f866b31d68c35f37d9333485f7e7223", + "0xbd03e93b8c88aa38114ebdbef5615b01e43f973df451788b48cac89ed4842869", + "0xced4e7f78b0168da7e80e93822db8894d1634b352d8140aca1208cfc3b5494d1", + "0xc0f3dbc11135dca5a8ad43ad9c1bff4783e2f7be2dba040a11b397ca0d346970", + "0x09c26861123b1e0bde40d081d4d8ace64265bba803e9c83dfabac22c3cf66f15", + "0x942e7171acd9463610c469782ec8b29594ab9a73849ba3878b5bcd57d1d8651b", + "0x212cb18710f5382c9891eee2a14ed07e1f15624e884d59df9e5c324759b68650", + "0x49343d641588e5b6c54e3a12aef04b0baa6c78737f876a4ddab97f5554a796a4", + "0x10faac2707a1790789b03624004ae3db818b930d0d8b059194efc8f184ceb3cb", + "0xa0121ce06925686a3cb28b2fd932a7e2a9978d8fbd6411d4046bb3f0305aa5bc", + "0xce46c63e54fb0ef227e127eaf88b6ac5b2843f9399227d0370a727d0925bf338", + "0x6bed9b74ceb5b07860af19f75f53d4b219d3a8df2135f854662d6e8f44700286", + "0x5579fa3b9065c2ac3a8c0fa1f9513f3b5d5d8d22336f15c5b10e75aec3bfe45e", + "0x85c0f9981bb934fd4b87734481c150b2ee48d69d91bf30fff2e6f15b060f04be", + "0x3fb11a366eee029baadd3e2000946481590bd402b603b907211d5752c524a124", + "0x8cff765acb54417de499917d00a9a029bd15f279e2e7d4b303c4cb2dc09439c0", + "0x32c98c71f2a56ffb10818f005ff4760dcb9666c06137cfd12f9517fb087ae6cd", + "0x0a61294a9827cd066607f3977b6e95152f83ad9032d92b8eb40b5913082d19c9", + "0xefdb7e9f7e71eeed6a0c31dda1494ae3e70c377fbb0ccef97c6ebf6fb6b73c2c", + "0xc12f1d72e3f9c3c1872fd14b3deca09d5329c87a6e01a071dd312f4e80991a0c", + "0x69eab8cbc231e8170e1b78f73e5213f3b9da4bbe0c88c16a42afaaefc2dbe6e1", + "0x13a7b00b102fd0ede261748bb563b48c2cf0b2be8fd57122a98918ddba50bde6", + "0x8bbc5db1972575e25d18d63de9c95dec6ae5398c43836f020cbc2ec52c9dd917", + "0xb196e39b6e11d07ba9eaa9620bcb7891131af0e05858333fe7a3dfd501b46551", + "0xd5cf2639bae2fdd31f3e42ef31e64c5533326a4b1bc3508e726d786aaa55bcf2", + "0x4ef389ad6f2259d53e73163a6da07745d42c5972a1673d1b3385ab3eef42e115", + "0xfcd04605a105de58eb0f5a474fba4fba270fc4072d4147110fd9744fbfe85637", + "0xce3a3417db936f1b1664e0e25a40e3d161c2ef68946fff44e9519844f1aab4a7", + "0x029fe3e9046d3ff5b9ce043155426fdf5ab87e696aee88bd7833e8d9ad262aad", + "0x35620e24945248d89b2f67f50698ddc0fee5c7a6952c9c4b31abd5dc050c78db", + "0xb89f83aa4205e73a93769e833ce61f51633d6df3c744946314602a210fcf3c7c", + "0x9c80e1f5984b9af43332cfc185a6c83556ecfbd098e58af9ede6b50888f4bc0f", + "0xc1f097d998d50668e9472bab5c47e9fc76c1c7c2975d27511ad5ef687ce7b3a4", + "0x5b06d03360984e753f07c28fd3799d4473bf8f1293e9589e171625ae804bdfe7", + "0xfdff7efe3050302d910bedae052419e944436ee4691c94ecd295fe0e40cb0906", + "0xce797bb3daa9792f2f9902f8443b508c5492c68bd859fc13b1f2423017576701", + "0x53bbfbf3be8c185d214f829f9d4362e6a70a068227d2734ca7bd9ccbb5436f8a", + "0x72f3766fab28b8aeaf8ed7fbb6c2be919d6de81815ee634192240c16ae0d9386", + "0x3ffc46bb0d8f1530d90ae623203d79fc633020d169be34433c88b2f7d087bc2f", + "0x99080cb172d21406ff902e146286d03f9894235b7b08e6402d03bad76fb50ef0", + "0x9896b2e34f8e765e3e8ff43bc29925b0fd88992b2ff8fe7c55cf10bd69e3c6f2", + "0xadb64f362377f7196a30306aac7cdf2da948c808d518b2c3006a16996a402e9f", + "0x914b3ec4c8910d7f5a2de92cf4029d89d9d5b8fee6da967154afe923c270366b", + "0xcd2b2ce633b50d6f91016a2cf4a3227384a4910f2977e8bb730990b53683a1af", + "0xce8e7bb32e274b55bccfddd7312903b42e7e6820bb941d579f88817a4fff34eb", + "0xd866df49975c650c1ecd269c0b2dd27653d1e3f7618bdc22cf6ff92d04092031", + "0xc4ae3816eaf50bf7a48195cc542dccc9a265910c5fcf5b24698d0bfd05d9fbdb", + "0x2b007867b431e5ce791d21e548d9dc255f58cf207ab1341f4f3833e46fc3103f", + "0xdc7ecf61d408ff86b56ad939b6cd8cf9a2c75e6bd0f5934a0d869f42652ad2d9", + "0x53d99d7677f637c0d1cb21410bdb8ba089d02b3f147d902b377a2f7d2ddd483e", + "0xe3da7149f526777f624b6e7181d1d6c1e1d9a0fa6d9e16c77bfe999563c400b0", + "0x37573045866d19261d96b083500b57a40b1e57a0f2f5183128f5bc510bbec5a9", + "0x90e1f682afbc9d89eb937580adebeb0aafb2efdffc8a953144cb59f86e1fce55", + "0xe4daf4c9ae9eeffc407356dd752623db4b239c77514ec302623ad8d88cf8c1b3", + "0x2ddfdc374b265a7f5a11a08f2970f704261c9270332843fc6ea20b01825d53ec", + "0xa01242133086197250530a0b151e5b95b410034497ecc03172d608c4e6445ef4", + "0x9d9a8744ac5e942fd117f3b379f5b1fb778bee6a7b063031383e3c2b0eb5c511", + "0x3a7da1014d539bf61b3f0d6c6b935856e5a319a40b6b5201eb8e57d067bb35ec", + "0xeb9dcd93f1492443b99c34ee66d225227ee85905efc8b38f436f48bb53a223e5", + "0x546682376d2507b93442a2fe76c681138e551c90350af79274c4ab77329f32c1", + "0xde456bae85380ccfa450f187d288ad0f5f1809f5e449f766f57ad3127be3dfc7", + "0xda1a8a08fad543f590b608cf5ec26760a1ee12e756851a9d612f8119469f4bae", + "0xb34fcb29e0d84678c2d97afea6e7337c1812bb52261bf301ba0bf694fbb37a71", + "0xadcce5cc21bee177fc04b37497e93ead88b1fe89e6e173088239e4e95532176b", + "0xe8f5e2fdab4fc0b4f3fd8e3e79c04b7f50d31096afe9ebe4a8c483efca5e7d4e", + "0xb0c87ac3ce56dec46c02b45aff36098fcc6385c6de6f5634bc5cd55011e0f944", + "0x0fab91109d9c35bf9682f90378303de34a28abe0ffc30715940eec942e80d65e", + "0x86dea4a1063fb9fa5368d4b32f2dfba33de65791ddffa62ee37938938a895b1a", + "0x9ddf582126e731da0c4e529c89d3199a1816658f6774dd874618f193ec6905de", + "0xd7e9a7815b56e7df064aba528773830ac946dd185f37392ba22ed3b6bf79992f", + "0x0fc498c921ae2234559acb68ad129fc2cd0b521b2bc9569b51ca6d0936b24153", + "0x63317282aac538a373c3da613dd43f3678e92ad73d06ada0f2459800e53bad0c", + "0x4c2fb4aafca0d38db29c9e0c0752669ba697c2338671377666a4c5218b362b21", + "0x993ea9bd856b10d0b4bf0d4acfffd7e69ea76cce62ea9c5ffc614eead4178bbb", + "0x1585b44c91534216fb53abd0ba153e162f2d7f11ea4ec47f8238b278b027a22a", + "0x863348f9d9e42deef0f77bb4a6e282f3b21071e1682d65ca94e6e81c93e8fbfd", + "0x84e85c6e3000c922e2c9aace18f38f489dac0299484b39713d35151b2ab69fe9", + "0xbd9b9b71a651da9eff6be2ee538dea9619aa24060e7741979922e1323859366d", + "0x6ada20cfcd0ac759f3a5fb40b5accb36e818f2c556a0b6bd43ca553a3628190b", + "0xbd37807e8e4386525baa2fff60850908669d23100849a21048cff7e81205036f", + "0xfef1e9547a8a9b3c2aef3fe61c0e076c84a92d0c6ce76bd0f6339b6ec0146757", + "0x8c636efef5ff8740130cea171897d72158faf87dc9d4d86c80778f1fda8c4fbc", + "0xf2cecb675a1e77db143e583f404c5be6fae4b669316b21c449086c4d6244b717", + "0x1d134890f5a336bf14c5c43976798fd128c09d60760b124936a4c5d434cef69b", + "0xb4cc574eecc8d0c62769e3c59fcce391830be184ff798a9bda62bf1fc6220f02", + "0x2bae478e8dbde86107bcaa12175e2b152fba18481772fe0537ffd611517a2959", + "0x052e4f225af80ea1be7802284b6957f9732f7732d5ac2f5d77ec9fb149b025d1", + "0x5776e4d6c37d4bcc61d74ecd6c2410894cf01a654cc0a698abfd1b3cfa566fc6", + "0x06a6775525612ea24ce3e7915ad4beb1c8a68436dc6b5792c38c04d9fe0fbebd", + "0x090ec2f6a1c79475f924a537f76dc506b091a27343dffb20a5f2e5536c4961fa", + "0x76d90a07522866668bdaa2a0a6ffc4e74c2902d4a44466eb10507bc69e3356f0", + "0x04da6706c3e427d283e83aac45ed622e6a84a7af3f6489a99627f27af77804f0", + "0xae2a4c241f51ec06711a498a65a5667512b84c3f78ba7d46b2442b12847e01ed", + "0x232a0201afd08afad1cf78272b06e8a0dae54d8140858b13806d112d955bc5a9", + "0x232a0201afd08afad1cf78272b06e8a0dae54d8140858b13806d112d955bc5a9", + "0xa55083d094866b20def6b929603f72bc99b55e209e77bfd646ab78e4bf92029f", + "0x322a1523e670390d6924d63d6906d9de99b040f03db9f0cd953e6cfb48e4d6b1", + "0xb14aa59aae074b4777b2a59a91843367551f3b2760687b53baeead24fef7e6d3", + "0xfcd00232945ebeb9d6b36015057eca2996e50c7a603740efd3b041335c996882", + "0x005418c9fb87ae5d951921ad5a4d57e821fb35f22c50c4950b193d6c36b1c5e6", + "0xa550976c7833fa726f005d04462d3dd2afb651360a67f506d6e32426d16edf2f", + "0x99be755d7e9e0ea717db0505371cc480244588f63b52414a48a939dda21474f4", + "0x620edeed425bfc83d01f862a3d043673181df1ccd863dc65377280ee422d6d89", + "0x3fbdeaff19c66a6f289810cceb517ad9726ee33d022a9301fa9c7cb4d46fcf8b", + "0x45e046eda1a372ea9bcf43d501eeadd010a165ad6a7d68c075be80d310d142b1", + "0xc45190232e964a32d97f1768b03ef4cbc9272a45c00c8b83bfd27cfe33a6d635", + "0xcf9f1cb6b1b0780049d47c623b47a5ff8051d81e68eeb04e4af065c50446b004", + "0xae25a506f6ba167e496cf210207445d6a2341e846a5fe983eb5b2625b1f526c1", + "0xf4f7970a3a21a97317faeee00caa42fed43e2015819043f40a1b15468323adcc", + "0x722c60080df2f22b8b84fdf5ead1e89baf9d1fdbdc6a81ce6bf87faaf806f546", + "0x8b06639402a7a82557a6639ff79009eba89d7fe62935bcacdd392530acf8aac4", + "0xbb89abd2bf68c9accdc1413f76b19f734602b3975996984a93ca81e69cb74968", + "0xc2bb7d65410e1f04292002fcb033c07d7f6d20ed9681a4c060d2aff243f24086", + "0x3a4f281caf741fc778deca3c7635a24ff04e6b27659ee7ef3e13497a35864c51", + "0x9d625b6620d36f93b21a7c7c11d3ec111c53e57c93e2ab2abb600770ab789138", + "0xa0c2a8c4221035032510ada953191cf36484bd2071cc245de4a391f0d8c154c2", + "0x9e84f0f26ec0555716b9351186a2fd83c6d9964279a0951f4f347393f285393f", + "0x745aed04b3efc16c0ee60ff339fce4802db21d588a3b2f415c37bee85de63dce", + "0x4c142c9a4b27502ce0a9d8c08be3f99aff3369960ba81109282a6890cea039b0", + "0x91517dc2fd3248d5a0168b4daf12609858bfe05eb13cb962188582dd543a504d", + "0x003fae1627e1cb0edd68b730f4545406ea1e418c5a2ac66dbc765eb4de83cb0b", + "0x2818be8c31e5378e18cedd5d12028605e1bc814db1b0a7c5b707977565a168b8", + "0xe70e63ce34d13acc5ec68d17a6255a59b248345c33f64a2343a2963fd447eaaf", + "0x7706fd99c3d5a8e707698890729baab59125ad135c89b7c5a1c3851ae6d3d854", + "0x4106cdf10eba4ff6b4276287e1ae7b4ccf013ec3879f1b87fac667de8e1c1f3e", + "0x3ccb7b086b670c04cf80542989167f3dff84f1d81b1c2df3e4769181f718f059", + "0xb53282788401a267e3e234f629a18441c5388d6922ecff637781c567f617d456", + "0x92bf7e776561130299ecf2d64fe69e0c08e4a168ac6534267596327d8a292c75", + "0x6e93e29a0b350fb3cdc746cbc3956f136cdd0100e73d848ca8ffd6fcb5663295", + "0x5bb9e7b9a479d9f8523b95380fe471952fb2252cb14bc800888de507819f22cf", + "0xc3b1ff44b521ce54b16535d158c5c8b87ee888b638940a6758f1f269e7b1f77f", + "0x1bb25a947c7cc8b32bedb131e6c8ad3854fb6326fc78ad47eceec8dac1035c9c", + "0x56ee98c34318d18acae4233df4fd909d618f90ab91e56402992fee660b8bf3c4", + "0xc0b7f95e7f8fbe1703961394dddb08b4893bbc2bc1654a950926bdf186e1df2f", + "0xa594d09793252212ef801fb8ae0903d5e0b64761ddd7875c0d96e3c82f224b80", + "0xba896ed40209e56779764343f701a598512ddef34d6ba3cfa1ad8b01a47f6898", + "0x8afe205e487f8d7accada8971c20b1803e95f9eb5cdde04f010dd9818bcc0238", + "0xad78499aa5548d9301a16098ed0a33ffa8de5f48e215348a12ba7c897de46124", + "0xe1c446a6e84e27c52bc36e73d6dfb95355a012667e20e7ba8b578b95c4fc1eed", + "0xa6940e786bad48890ca26d4645cf41e604a941c12e17b13ebb4bb48cd1c5f8e0", + "0xde28ddcd06401d9c537bd9d1bce4760f23214c202f01a6fd7169ab96a0860cc2", + "0x4a9401a352429c849ce9c9fc50b8b77a1a683ccf924207973339c51917cae4ee", + "0xaa5d60ab8e1b439b0278294b07396e8210181d8a1955a834b5e52a59e099a93c", + "0xce161805a583564e08c2af78632242e718431350eab3ae83d62d44761226335b", + "0x72a4ef029a8d51740aa6f2146ecaae617861f4bc63a6f5adbf9fa4546af6a4c9", + "0x326465610f9f0845525252a16c7dd9a6c372ea962bbcb7729261262cae19ac68", + "0x1468ea4305138f494f297e030c5c708e52c4112923b6d9a634214bf5ae956ea9", + "0xea8ce516ca988350a413adbc1efed3bd1c93e5bbcdecc4cdaa3c24940b45834b", + "0x32667adf6cf2d8a82da9ea5e6b11b3311363d9a6f7defbe4c208d2305fa1e8ed", + "0xb269f8dde5137ba2cb0cbb91eb3a5eedcc2e86dcc73ea0f6223fe517b6fda0ea", + "0x1d7622acb9bccbd83e645d84c3b61fe23ccd2fc9a9b0c1d2c7819f942a810ba2", + "0xbde0d83240bbff57dc6e3c2f756b1d52880b8174c8d343adbd3087018f6e860f", + "0x3e32acdd6469f194bf9a91e9a13a133fafdde6a9315109820bc5101f7736c5d8", + "0xcbf05663005c0c1aa1ed9994c1e2c1f8fd62a8e428b05f931627e4c1c297660c", + "0xe4ee5909bc92be4db390f0f734e75f6f9c28a28c361d85120a4735a624c7b7c6", + "0x84f0be71ff968762208ea92f0c10438d6771247b391cdac255a17f46b1df638b", + "0x13990cf2ad2aaf440ffcb8ad011780fc635958393fbc7d02f50fe19b74269ec7", + "0x12b4351ad9c4675b2fd0fa32b6681f0cb37e807ae06df4392a048b42d808de30", + "0x396bd7487e9adefcabfbc3722a52248b916de8501619eb7082044ded3e702680", + "0xd7043c09f0b01e6c9509ea23cfdcfe42d96571933b56a557049191e98ba1f320", + "0x5e877be0dbc6ea06b0ba403af6dedd90580c7201bbd9544a19ff0d0f2a66d4ee", + "0x72b2aef1af49b4a387aeab1780b69c9bd1f7329d53286da56e3fe115062dbba3", + "0x0d8eaa6bd96a6fb4006e58bc6307da932f2141643518b8f69705465100885908", + "0x8812b26ff6e0894d3d18b49e776fd150cd449f6652e81879d7f59065c5495cd1", + "0x04b8243ab59cda1ede5d219f0327a2cced41fc97e9501eaeaf3677f305dcdf3c", + "0xbf01f5a009e9add8390f5562bf5ee27eaafb75436c1d29446c6b77ea5933fe62", + "0x4b87b5d7e17e775645fe5f0b4a968965f5b55536cde5d5b160331f80dea1db8d", + "0x494fdb9a32cd63bedd6939e3160174052a7430e038795b8a9b1391fa5b6e162a", + "0xf628cb604918a9dfd2ede01b4aa9f43371f809f173faedeabb333f432955b768", + "0xfaa42ef8690847fa9e9af5df89c827a79d435cb1be757267944eca0f5fe3a8a9", + "0x469ab9201c1f723b7cf406ff3098ae004fbfc626fd28059e11961f9d8b26537f", + "0x1f2476b947fc3af2d3ff70ebf7ffb880ba25606c30f88b1ce3656b1ea007e4e9", + "0x30888ea1e0f3ca79f71633da1ecb66d936637034da72929cc566cc3c63d02fb4", + "0x21f1832760661dbf9d08d97dd7de4b4ab1219cbd2d291d485c246b2aa0bb47cd", + "0xe7e752d022d4406fef30dcc70938de84de4baa4d9104d1a50df8dc28b0825bd8", + "0xe9f66d4027d13c705e3adf862d855ad12391a97e0a6e6020a384225de65c248a", + "0xb7af6d984c18f4f58390e54e4858d0ae01b98ac1f4645393577bc0808681f6a9", + "0x0d494f7dbe387f4f3a9aea7a1e17ffdc5862b653ef63ae3167a0810661596e38", + "0x3e8c8f0d269bf042aafb3fb460497d4c20b2b1ae1689483fb58d480bb06d3d53", + "0xf6d01e48af52a330c261632af6475e17b40926033ab7241285d7425c66adc9d3", + "0xf800f30918c152ed4db2fc1b364fc093a33c134fc2307b5a071dc77936f56e18", + "0xc032970c76387d50b8733978ce970a63cfefd2d52b6c0fecd25403113ad22589", + "0x6fc266f5d23455aefbb55b8e5726ca4bfa31a983e36a336b365418a8e1fdb700", + "0xbc4e1344b0a63d49c3f305eabca96066d50c5096ac34480e16b49606e840b03d", + "0x1dc72a3da240bcea51c93549c5051d32d658970db4ef06e0e7de645145fcb235", + "0x503d34c9a90517d030b5288e565ed0a1fea7767a5e301d55a83444a1d65f71e5", + "0xc0e86abd354cb9e7092487b45490c38c179ccf1ebfb95c917fd3a59698b7cfc4", + "0xc738bf887e0fe95c0467ee408fa70f8f047de6cf8e894013e7781b86fe5f90b9", + "0x37bc33c37acc9d004ce5a2eaa75d354d28a43bf949319507214a3850fc981596", + "0x0a9e8c743d38a23c5f25d0797416011afba91c047d1f80b3338a2db278b7be96", + "0xc569064df95c89501acdad1927acf4aab9e52fd090b820a0cd7fecb3a5f7bbab", + "0x67b187b41dce3fa7df33b4748650763ca509cd7b861f3c9f316bfec388abb5c3", + "0x1b452bd6c8bf4b9cf95e5dcd87116c551f42261ad26dda353413cb822431f6a6", + "0x5cbd1ae7b49294fb26b850f1c7bab01ebb31437a26c2bb9e5bd98472659d3e91", + "0x4f05533c27ae315b954dccaab8365836e434339732b00651125c75abc5633d31", + "0x12f3359c62f69312606a50880b22bb651be897284f0ef5e4186c747f52d64fc0", + "0x2e5b65491d47717ee4650802351a24291c40fac5beb058f7f8363a828b5b40b7", + "0xb52356212272a1866e4af1521e989d7c306adc914cb6d09174fe17add87a278d", + "0xd598a561458bda6f2e841ed937816936604116d056001bb50bf7699a3971eaa5", + "0x1b4e68a07b0ff0bb6d1016f1bd1f4802dbc01ae20bb0c0af726b82b159761ac5", + "0xe0c011026fbf9d363a100c1a043b8035d7045aedb8152dfddbd0dc58c04b0dd5", + "0xaf193d41da1af7daa165121435fc11d7ddf06fa662abc5d88ca4b3d2b84b50bd", + "0xea9d3c73c6d033516b4931bc6d65491f85b6396f2ece828a78c10703a67c21ec", + "0x4dd429b83dcb2bba38d3a41e161a9c093f84f766bb9637afebd5d727c808db00", + "0xf5bce29895a5943d1da98140bb4163ddf815f175c49c1206cc3d5bc696332bdd", + "0xf6d7ff3f0161018005140c3cdb188f480591218800ee86846bf7c721359b334e", + "0x2f09adfe45dacebb2c6b55d223126da6a84582a316435631a5f04a44da0964a8", + "0xdd507cb75186b0b6e89367d43471e508fea32639b1782174aff2e7b39824bd90", + "0xc40f9a61f8fdacfeaa0d178407ec55a941ec8e091ce9365aa39e83238affb2c0", + "0x5082f736b585ab191a9afee972b2890f6c6dad5a98d360e027d3faa66511f798", + "0x4213761b4acb0fe105c99d50bf862a47216fdcdb89912b703079851ca19285ea", + "0x673d3239b804e4906f1492109b78945d1a5ddd804a2b816422dfdb24036236dd", + "0x02a646f3f0e2e50ed04904b8d37e629cf8b9fbed834a4d6bb422e5de4fe9588a", + "0xfbfcdfe9438feb91a39a0c75dcd50463fbb5e348b9684975165a56b067af515a", + "0xee8cd8583efee90c344ca486e936a64892be27fcf88fb85f3a905f2807a80c32", + "0xb1359a019c1b9c39db4ffd69a44a192cbff7b88d5b04062845560b558a995c90", + "0x2f106313300b66ad00fdfb70ad75f463015c23a64167627af089a25768e19940", + "0x7d47589bf53b1b23b4d43780c1ddd7f0fb366d7c21f0b291668a6b18b62ba08c", + "0x2ed54577d5c75e418ce3232e6b0ec9aecb33bf5cba487e8af17adacbc5647199", + "0x683db1390ce8ade49172bde6e0e39093ddefdd7e89173fedcc373420c35a4544", + "0xb084bda95bc9e22bdc47f3004a26dff72db22ffe54a4701bd7450aea7c549ca0", + "0x964b4a699d0b7942c2db5ea0acf534d7777330a67f0da3d1b2702c42e9fe9b40", + "0x90377b495888d7495e0a08e9222514077271f3782003bb5c8eda1e6fcf3f7226", + "0x3ba49a6b72cf04dba7a65e86bd526841d96ff40f0932f6281a0f6dc50bf3de42", + "0xaa7f510842073713a028a5339613036a1b4d61d03c01f2fcd5088fc708fac51b", + "0xbc04de6b85a540d74bda926e6dac8ca67d221902a7411b1b8908e9d0e5a500d1", + "0x89ecd57a3d3f6d8b7f5399d7acae6c7e9e4d7a9a98a3d67797108ec737906d10", + "0x5ae1b2a1e393438a7fa7ec4ab0892f3b33a2d081841786f4209bab201f381912", + "0xa303b594958595b3fbaffb2b055cd092d49e9ed7ca9ab500240e7d6c3121cdc9", + "0xd2df7a95926cb7bc25d72761b9f3660254e80f90a2356d9be168a9ad0c90fa51", + "0x50f28ca05e045352ec34cd7189cce8d75a37548c62900609fc271da28f03fcea", + "0x154824b025edbd0521b545ab1d2b4b0962b0a70d5453dad88670e22c28f9a0d4", + "0xa1c8ce9c03f917c8433e534bb8c4319c63176109ac09b4ba259f2d27582f39c6", + "0x67a5f97de6a106d307e77331e59f4d7b18d78496519b823dc3383c1181cb04cd", + "0x2f6e8ad28a367a8e85fb3bb4a834cacc3f9ae946852eea073be13a970d18a727", + "0xad87c31f0940de690b7c1e45b68124dc48c661b02617fbf2cc5959c1a2bb56f3", + "0x3c469f885a423c719c5526eaee19191e7522a7480fbeab808515d8be06de1c5a", + "0xb4b11c7614bfe4dc9f1424ce4bdcc439108c7b3bd3bb290f37e80785ee052621", + "0xd670278828d6eb7228a14bf7ac83ab01db60b68f5af79a94758e25a6598015f2", + "0x01bde4ce571373ec6cfca1fa626b40196985d3fc02c4aa2537cd5f059fdbe35b", + "0xa2f7a256866528259070f921e345705325e18a8bda2698056e2380e825eea071", + "0xf62fbb53002959d35b95eca726a0b81928a98a3a7ef4aa7b97ee92486f5bcfd1", + "0x7f098f0d35dc75b8af1ac418993d39a7b4afe79a2c46e038f573304e31c4477b", + "0x6aeb53770dbb4fa40cf7574a3f52fa8a8eccb06c228e93c847ea5f398fc30972", + "0x407efdd98216ec897c383e90f2d29305089a9c750142417b3a6e7114c3d28183", + "0x05931452b5f8a1e871a1c3483538c8d0f2e3e12cebf49877570c2b437415f38e", + "0x806cd1b369a022aab9674a50aed0c5f3d20b0cc49ad092bf265fdbfc7af2e5b4", + "0x104f49705e48220004abca0312acbca6cbaca22709fa9f0fc18c815754495867", + "0xe46104eaa444c73e3eb1f4a87f65770308116f5cb8ac144cc80f662fd32c85f3", + "0xacfbd585cc39d8f8b806eb33fabb2a5aadb16c4b7b18b37eda2e0f972e848ae6", + "0xa9579ceae9ce7b8ed548700ea7b90851a3bcdd4e818dd8178d89e711f63b3717", + "0xab08b9677d61b77aec6b74abe174ed7ebadcfc6a3da0067087e2c7195da22806", + "0x11c5962f0250dc0265a98a6708f824bb644dd9b44f1655395bb07adb4bf9c148", + "0x9463c31e655caf341a1182f80a31b0673210b8d4b5436cb4c40cc7d4cf2f692a", + "0x1cc956750827fb1c66c647659477dc926354aa793194aed65a5de231f1973543", + "0xea8467012aa1f134bc513ef885b7926137638d277c406f290acc709b833687ff", + "0x49ba011c1ce605540011f13445fd25a1ead3ffd316eacb28d3ccd705475b8495", + "0x7ab621257dcfa1d9deea7e11a5a8fc55442199b6b0ba4db2c384196f6e4430af", + "0xb2811b47f1806ea497af2be1cc64444f77371548abf508f9503f0c9723577e8b", + "0x6e91ac0438c7a0effe14a4d1377f200cfa86268f429c369c8144a682ec8d6931", + "0x12eb9ab11da4a07a59ff45b40d4afeea767f3ee4e2fcb04ff275e0d24722528c", + "0x971542e7f7bbd2f828706d755c91124912b82bf01ef2b7e38052f3780197d752", + "0x1409a410b69aaab60829f807f94c2f7c4672dd9e4839c197c50fa8f48d085859", + "0x72d3b44683b0e923e5e75bce432112620d02b9d2ee629369877215459d0e4d8d", + "0xddfa129451d84bca3415c3932cc218c449d22bf89da014b9a17182c8a49c4bc9", + "0xe1a3ff707660b80295c1f4691db45307c497cff9429e315870d61aea3019bbbd", + "0x30cbe24371cd9cb6114feace33b9b25998aae4ac5d74ed1997101a8fe5cda008", + "0xf5cf6811641fd862c32345dc7a1ad8674cde529e9f84af1e5ed8f8b558301837", + "0x05f562bfab10dec4ab94fb8ad4cae9708d9e1dbf8e6abbd9416dc4a3ae3da719", + "0x42c503e080f9df15ee2e409afbeb575234470f93a73dca35a0bc555a8e735ceb", + "0xff4f12bb40c0185cd8ab6164e1e03ef91cd3157fc4e68bf03b183a19ba3293eb", + "0x286294008a68aac3b8c43b56994d1b4254681be6a50edde9657015ee6dafdec0", + "0x61cdd215cf361ead9050ae0729919e89e95e208726e0a63832be3c6b08b4f45e", + "0x2d1751cef2b305d3381383003051b3c884f3f017447a679143fc4cc56b7df1b4", + "0x63e29ffd2f5cb5809d3078617a21673e345ffecf62c83772b3f1624615fde2d8", + "0x20c06aef14316be85fa1a98ef72a6685c1daca6330d050c2b91b2e528e746929", + "0x5420fe8a733d0e267f605d5b67c8339666b24b3090c8a2d1dea9abcbd090597a", + "0xaaa4189e1e4d0a17bfd613a9898a79b432aca851a839bbcf72cb93baa8e2fa6a", + "0x9a8e3e51ea570692ebfa0bf8470746f0e3d03dd497e90fdcaaf0ac3446235c73", + "0x7d5f25743c1a1e651dd7dba2f244986deddaba20ec296a9acc35d1e9465fcee0", + "0xa01c8f762c9d0abea755365ebce860c10378e838cb3da94d334d5655fd31d152", + "0x7b5fb111f23ee6dff57b6aee717bbdd878a9763a9b28b6aa36989bbb3e2830e4", + "0x43ac9dfc22f9bef17e5c4314d1d3fce8d54d5ca93317d2d1f085a57b9613a597", + "0x9fd4dd8b085c21d6d523d8305f25a1c2c752da4eea43511e43be5ed413d3ee2f", + "0xeb680f2a9ed13105ad83f6b1aba14dd32303adaa60e7cd555388fd796819615d", + "0x50efdedc189c914e1e154bce443a840c9697fac2bd20a79d172433049b2bfd32", + "0xd9c3fe56704ce091c4eb24064fa48b35071dcf9e04e358a312707af36c183454", + "0x0e3b70a47ce4627b29e8de7e3e454e806ba4ec4ce3a8c19845043b351d8709e1", + "0x855ff41925dffde0669d46429db65139314a7510ec19a2442705ba4513f83c9e", + "0x10e7e76df6e425e6695113cc3255cf55b29313f144e61cc804503a6c9ceaba11", + "0x87bc3c458d2160dd029e8f60805bdc17f5c2a3630b8ecd87780c7c79b3322824", + "0x8e1fb786be437f748648bdad6d17fb2e89780bc9883f720e2656dcfe6200c7fd", + "0xa4ca35ad3f06e57ebff6aac35b1d530fc6a9e804cd14354138c0483fc7a1d904", + "0xe17dd9bd9d7bae2ea0b2d6d17afb44f4c12a0ffd1de640f6c8740b74e9ede59d", + "0x92e33a1d02e7df438fab810e92a9e14b237431c7cc90ededab0de1f70da841b2", + "0xbba0a3b835dc96e674437a8fc06267ed867bc5b666bb12c74e02fac42f987587", + "0x22dbbece2bdae0a435e85a5c884191003d5edbd345f9157abbe380cc8b1ee47a", + "0xa8a7a1fde70b5c4a243ef2042d36318eb0e54f18306eb3771266217cfbdfc9c7", + "0x083321382d412b0ac11adea52c652080bd8ab6ec8507ad6eb0c70cd3322703c3", + "0xdc4acda4c87cdcd6e3e81fbf63f76c03442c0ffd0d2ef2a788cd4c6819a6fa35", + "0xff31ab89572a00575593f0f29d7b6ae61deab0f2646e9083fa1ce0c177380043", + "0x9632d91109184ad4f17fed5fca9455bbc10cd23910909b10fdf822fa873fd026", + "0xde440565220eb6f3741150199b097e0859da81d02ce4220ad42bfee686804802", + "0xf1df8d91f90dee9eb6e60ebc0691d0c89197b7069435d01fb2c502fe45058bf2", + "0xadc0958011fa76a7ef385cb568fa1b0a210f6f802657d9b7f488b25d7f80a122", + "0x2aacdf949a61c30456cecdbe9f832785f8b1af84558f0a99132852ba537d64d6", + "0x0243094757aed36b8660bc9e19fcf032c4b6f370efc8fa010f2a04eeb5f723f3", + "0x71cd1ab9f2ae349b0f64c5b9a4ff3d9777f9b6f81d31b0e0cad962890817fc37", + "0xad7c06a19d79024709a7f2d375c563e78e197aff264987aeccdf0e6b56f3d1c5", + "0xc6eaa254fcce0a0a6edb1f0fb1e070732cc9f9e60b70bdd919a69dec7f6411bd", + "0x971439b8b920730418cd3cad9a14df52c5347c6a5a1abf95b9ef4d3c278b9c12", + "0x7623edfc8d3c4ff26850c6dbb4b7848731701f97248824b575bc63b6181cfddc", + "0xa74672001e70c7bca9cbcfc26626ea217fe3875368c181cb2b905cb1925f8487", + "0x4f05c4d29f271c729133cb87c5dc84cad3979f27dcb2993b3c0d358a51bc6815", + "0x0d33ef998c26ea4e47907aff2b8ea389c8b165b521ec07b5078de0e06f4ecfbc", + "0xf2bae120d11de5b5c3a28e30f1e111cc0d5224b809f256ede1e51d54098bd22f", + "0x3da73db4d26360e65a47a025cc16bdff149bf8d860affae2222abd684caa1b65", + "0x7143d163751d523566dc6d9d7e5b150118046e72bac5e8e721770e2bec8eba06", + "0x7d349878157398f38e6234db8f6e018fe0b9d8fb6a5769b8f17b8677419036db", + "0xd7b11f9c057cc9b946feae354c220038750140277e3c4fa01e8fd3d09b44c0c6", + "0x6666e05079173d2fa1f3b5ea79a678eead42f4f7f95af58ed3596a4a9244db9d", + "0xd54f64b335f3b6077d0d08702ae89d862c4dc68e173eedf44ecfc6ef088bcad9", + "0x751b28923cc1fd4a81d94c1fd05a0bc461991785a2f82a4358b458608fe97a09", + "0xd8330a3313be42e7f232f16cf884cc70876ec9ce00603fe0e993757708ee9d02", + "0xa8b0a398a16d6d1dc520a1314edff90fcfd2ea1cbf542a1194965a8aff3d6487", + "0x326b4ce375da5d5a5a266d1adc57db6abe403db67cdcc13b7df170af5b9e2873", + "0xac16f33e69d4b690ee9bf18317d2474193713b362b11ddfc6cf27bdb34157da7", + "0x679e57fb6d33b8a016783e04c39db4674148c0e1503b5087a40d3bec5a748776", + "0x0eadaa0ddee1173a38507f131cf1ecef580c7fbc304b4d1937b1cdd2a6d979b0", + "0x2941a07f835c930643f981bb51152b6da3185eafd527ca6e5bfd6710ea76b0bd", + "0x6a2de0429e1fc407fc549b625cd3334a2b251806a8736404160cb2ac7db14c83", + "0x90203a5680918d70f8fdfad60d5c22fb889c9f94151502bd6e77ca72da441371", + "0x02f3326cd2e493b507093644d14155a6a11feebd7d8812966758e35d5010c870", + "0x9fe9bdd5855c57c4a873b9618bf9c96ea124bd5feb753c1ceda490e92aff5414", + "0xf8b602ada37a65e678c29fd0fe26179af317371a0597bcfe1f54d247a68d788e", + "0x8157f4ad66a72941f9b21a77ce07c377e391cf46fb5da06c7c3d43f5a27d0237", + "0x74dd39f1a19b1d3256ad3f3c3527871c74e59740d2a1008812316aa320100b16", + "0x2b194e3386ac82118d5562ff0e4847b9cb959046a625744a33c54e8a761b310c", + "0x4580c6d5c740c8a29e6598e714e1b58a07feb0f6153800faed62783afb9466bb", + "0x17fd6702cb4e1efbe0d747c1b38ec1c76de7b07c0886723a0ccc91b1ad044a66", + "0x9eac63196b5fcd1abcfef6b844b6b8951cf257488d8d9f121a9e0562e631dc86", + "0xd19eaca26e1de2d593e07a76fabea842f0d7d1b271263428a62375ade9897aaf", + "0x7a5eb94c1957bedff94526ab1453cc08719413a90bb7008f8687b81e45cff543", + "0xb292c3558af51df3e2f630cc6b9108359ce6d03293c8a7f9dc5127fb9f46a3ec", + "0xc6bce31317beda1a480628eb68161edfa3f7ee9f1db86106c8d2599a4b49db9b", + "0x2d41238323576a39d9dcb92b573516cb42c759b439672013361a88661cc6cd50", + "0x6fe826070caa1e5d29d6e7add34f2d35e05c7dd7a412a93e803215d914cc0eb7", + "0x915a9b1f237bca0f805f84319b31e2572fd51054cece26934d1c310b9462093b", + "0x85ea7a529cfc9900747c9e07e01a2f39ff24eb77c1d75434892116a1a724ebdd", + "0x5cd5275456dd2827b4fafc6f91bee35d436d9d18c11142f2fc9e5d1d6bcfa0c9", + "0xab84bddbcecc9179ce7dc881b0ca0fba6c2d43624190f232a73d987ed2f8494d", + "0x7c7700e776809550cbcf9cbc9e30f274ec7867daf8b68e2a44d2d54cde99871f", + "0x2392739c2c75e9098259174efbe1082c6929dfbe0cd03b8fd0673b6ae476b960", + "0xab5cb39a0b4b3dd2a392c79988bd3df080868c106215e92165177ef18cf0c262", + "0x951f6f3db40e91307d342bc38e6466f92130f5275674e9febaef780fe4ede03c", + "0x29d3aec826100508fa25d8fcd054bdd28ff4cca5c65ba88c1ee7c8423f3a1a5a", + "0x0cb8d03aec509a131fbfb10212d55ad11f3fb6800af5a297bc307833568eddf7", + "0x73db260d320381ef22758b110f0d8a869e5a8c93f791f0d27278f081801c7c78", + "0x48e4f45e7e6e0cbc29e2a0d10063dd995c068e6a72b2d757bed23c1a42575879", + "0x6141dba4a9f0c3f9283649c2483eb788cc379ed81ed59cb36d262227ad0cc22a", + "0xd66ded264c564f1fc1dedfdc60c7160800a23c10e3b1dcb0505a6749700d4860", + "0x438d2375e775d7d6c32f41136a715f4a6fc5f893e3739535b9b1b5b1be352eea", + "0x26c1aa920b45f801a375a1b6ea40ef4f2a6162310e31d55b03f27f87d1a386a2", + "0x4f55db98606989f508c669664951c57bf36974b641790f81e2974b7abadb04a1", + "0x7af1658480ee3c794870f1f0f6f37a70187f1877660c0f6face45f60c319ab4b", + "0x85e2b6c1ab86cc7af533fd8db4200296dd98d52b3a7921b81cfb90e65d94cad1", + "0x4b260a40c036c47c6389485442e15f103341eeb2fea57b8f39c9a3ced74e523b", + "0x6edaf89a083643ea7a0d4a7c61c64d7118a38873a0d35ba08e0b3a4138058ef8", + "0xdd00be390605bc5be36531c60881c6b27185ded416b6d696270322f855fe2110", + "0x27f8d46495b10f2664c351127b3bf4a189ae82732a27d8210821030644df7615", + "0x41903d42ffb6c2f25b025e8963279e4087b3801fb1ac11ea041d15eefe44a177", + "0x316c7822d1a3aea70b1e0400bce9179cb766b928f5b88401232ea716741fd7f2", + "0x26e41d07bb1105c65284621f1d2334fc71f227040fb750efadeebd17fda4b982", + "0x3c41b112522ef13cbda198752f61f07750eb50a0aadaa2a192299ee8f191a1d9", + "0x2fd18001b9d63f9c19082b85b2a380b8419bce692584c01aa7415faf11146e96", + "0x2c0fa8f5fac50c3e7f6d70e624280fd846e7b45b1ca6b3a6352a60aa1c8b77fd", + "0x074df456482d606fe32c5dbfdacfff8be72ee6773ade851f4653c1ace9e63158", + "0x0be72f82a10b4dcdcccf33bd19ee89786dea569aa19b27035e09143273cb3827", + "0x3e8efec76af248179701bbd87a91d246c7b0c32bbc38699aabfb1b90111a2221", + "0x57fb3e95062ed7d5bb4034cd6caf8f77d2767dc652fa144ce9e14720cade11a6", + "0xba601f814ff5dab1b6a0b4f550f328906e75bf13ff4f786f242aa0cc2b6f4845", + "0x5ce6011a2e8c683d525a231fb085043519136a8b17d3b530782ce44912ed474a", + "0x9e7b324e19c5dc16b14dea313de1ca466c9a562c196b1b12075d8b8aaae8dd3a", + "0x0691c326740c781d9f5a774a3e4c5f9eb560c4d1d2cb34fecc5bb847f8bf541f", + "0x0855f886adb323389d65eba809fa1fdd70e1d24a5fa3eff3d486abc1fccdeca6", + "0x0db66eb786cea3672072d5af77f64c4742292f75d221ac019e2a0aead355be79", + "0x944bef20ec3344639e6e7306d930c5e1431194496d0e7d61405e060b1f75d699", + "0xca108dec392d22216ee38b8c435a4c4078268fbc2a578c070e166b6a5413d66e", + "0x2696e786b66b6caf3a368771c1728951e053c4df69de4658c681b28bc1a17710", + "0x4ca7f46c69d2eaae6e43d1210d829a831242b8f627a869d7b698067e0366b6e7", + "0xed254bc1c369332895b871fad31ba03959e90b268c0b12a391b1867b594c2066", + "0xdee3d0fbb13187cfdd21898d005dc1b10008c7eadc175a1cc9958a35bbc9a634", + "0xa880b195d0952dd3b48fe8cd52612b98f7533bf0ec20157ec3cfcc7ed97ac752", + "0xcaa821741f660f642fb3fd242ffb195883e04207127b147167e70215b336b30b", + "0xa6edef4b6ee40973bd18580510e01ab9152fa673231358102b3546119e8d216c", + "0xcae0b13c65d8c437fa09f1abeba0280f981b1837c80b9b685b56742756261b31", + "0x3093bf40ae22bd899d87340ac95e21259a2192327e136b3638bd011ddb344e23", + "0xadf3160c2067d69e5379a5a34e02d8c905c0c23ba38d2ed6aa8ffcbae8d24f89", + "0xca20cb972b243c926f173ed29bfa0c6cc5471c46a228a1b1e704b8ed9f364574", + "0xea83d2d3fb6e70b5c44a2964c329252d2b95e90178cd566594add6352d0c696b", + "0xf7eab3e17fa627f459ce790a0abd5acf851d7aadc7bfb0a0e87032688360200d", + "0x9eb27d80855c641b2c35a98824387a1bb8cbce030117809c757e1f30a88b6902", + "0xa68c32c23b69d25c074270f27aa79574122c805abb27e88c11ba6603c2606c46", + "0xabfd20ab47f14daaa707afc615a8bd713be2b7767a608735bf6d85a41a5f8ae0", + "0x482416d016540ccca26d5458f1737d5480d5a63e15db5934b9dbb6a7ca08a418", + "0xf92b64f353b74b17fc295261410a8b0a822c4b53daf94efd4aeca4f63ef9b1e9", + "0xbe7a5ffa2c0c231f00b2984d5942a5869f5b5f7cd40d7eb510dec44f1653ca85", + "0x0a3603a7996b47971ed4dc53148a3dcdfa8ebb2dbcbae52c64354a08df3a866a", + "0xd9c490b6237536cdd985d2be6481eab51a914f617f7c0b36b9227521c9cfe832", + "0x2e479613b9e70a92da97bdd46f19c070b1245f70c6a90f9243afb1064d41dc11", + "0x048ffc0b3148495aef0148a6a87bf3022c685bccdeb464ae449e2781833ed3ec", + "0x048ffc0b3148495aef0148a6a87bf3022c685bccdeb464ae449e2781833ed3ec", + "0x0ea76b7924883b58d421a2d2ef353b96db9e9228a4177427cdff5f875223fb6b", + "0x25fcc74e2d411bd5c29268cf2e87966e691c4e3d8e65f02861f52015cf923659", + "0x4ab56085374abeb67147e80554aca90d6bfa9c9eca89b7d86e49cb25034a2458", + "0x4b9079381ba387b748f2ca5ff48ac5e7a87dad9929a75feaa4372773d7c337e6", + "0x1fd4899daa8b955ccdb0030be56fc6b28523b707d88f77210365ab969afc3718", + "0x2375dba97c93494710ec232b1612579a5f8a21a6e3a10b885297d5acd7a94631", + "0xe25a3b9c3a2477c69b16aa15454815723f2d437eaabdea154fd85bb7008faef2", + "0x769c2eab46d9dfea7e91c3e41e64146092d78c6fda19d54540b903aa5c228d2d", + "0x2b5c7377e1b3289a9062922378784c62c9627391f6c01132a38b190395d387cc", + "0x2f77c5c9a0383e5079113fdc9834cee24533d8b54e23aeb3e25c8c8d2e50edbe", + "0xeb97f333335f44ec9710eabca70743e1e80917733bcbf5f16b9259ecf718ed08", + "0x8ce85959c6bf51b2f7a9825d29bcb950213d0ca644610ce0545c497b6397ad6f", + "0xfe2929a40148a7f355602046c65db95e973453ac1be0b2a9cfaec3610aadece3", + "0x5055968e4bcca86de5d90df0f119322bc958fd682310287369dbc297e62087b3", + "0xc18f55d42e2750a31cde888ffb037c43eead6bc379a73232ba9720cd3bee390e", + "0x845feebefd8e477aef025e31de146f08e0132e547b6caf3b74eb125d8daf641e", + "0x788657f8095271e03a945ab9fe79f18185176dc08424ea028ed75e3d3668c846", + "0x045dbf18fd51da4b06de779275442f79aa96f9598e9b69de2cafb4ae3543e629", + "0x0b31b9f7701c03aa4552991910c2fc9968320f1dcb6b12ce05e7349dfa1d13df", + "0x68322ad31297bed1a0e1decb442c3918ccc51e44bc19c5521cff4f41432cba42", + "0xb0d81dd31357d2517611c9e91e1eec879c058ab8ffd4855ea8e92421241ac9a7", + "0xfdfd14bf466d1d82af284574b635086b8cd74705f9046b755b23a440c64eeea9", + "0x63e143e03ed9d4882cf6b951f6b7e8c15635540088becd14476c152e7eb792f7", + "0xf00578938465640e635912ac1337af07b79feb3a0b40a18c8fad1033ec47b698", + "0x67f22b75f6867c4cf8203001167798b17fdbb7f36f31c03968f04c30ed61bae2", + "0xd36bef3cdfa0b58af0857505579e871cbb7c39510965122a32e2ff4ada008a1e", + "0x0871def6d9a654e727107ec0b0b1bce6406edbc1e538f7d83a8054d585d12155", + "0x319bcb2d4171004ee199b077f9d08de14d1ca5113d477594365270fa506f2060", + "0x78a903fc74e894bbdd0d3afb6b0be2e6265a1f94903091e9a9998bda486153b1", + "0x049db7f6a4d5894d73bc3a8198bdbebb2b46d80070504a3ac77194045633d45d", + "0x025ddf2dc81f58fbc823c712132bf62bd0c639a7f5e34f09d8dda111bac51ffe", + "0xa7eb88a7a1945bedb30b6e7f2f52e20816ff907d65e4b8cfef571c17fccaa840", + "0xdff2e1eafcd4fff36b20c57a525afa93de1130e29168b967c648be694cf55759", + "0xcd6828e18e6842c34f338985538993cac2b5d6cab90b5d53451f68c2795eac39", + "0x7519f4345a0c9cdb639779410b1043833585ccc23aeddb0eaf5fd1b0ef5da24a", + "0x765d63a6cc08612eb1597a08c05c53bfc3f754091dd8738d94494413e6922f6a", + "0x4c836f63908b4c664add1b734997346df6c270f40fa3b0ac4829bbad19d2e8e2", + "0x81fba20590b87ccd20bc2eb6500f93d2895f933523e32c8a5d66fbd5fef5f8e6", + "0x9bd2bb476188eb8f197583dccd4b20ce3bda29f6566c2b3d9f55919d138a53b9", + "0x5c375c1001781909a78e927258d762aea76f2a9a54308f8813629f754d09d696", + "0xe2e0944be7848b54adac6cc93c0f9c1ee901a0cf093aceb7f7c82ff8e56b0e2c", + "0xb8d46fd022e884d6bb856ea252da7fb793a6bd03391a01e7bf206875127e520e", + "0x4043a97213dc216c70353a6bb5e6b6034bb5b9ed631fc6af0c572502084b9e4f", + "0xba90ee1f8dfd16d17d4009c0749c666189dc62d7c8faa8a2ad57c368d70c89c4", + "0xd24bee4b917c1e5e008404ed9c4ff06b9d31bb9de7b4e867d833ae04dd622e1f", + "0x2734cd8353f664d0cf3d8bebb98e956898644d71d14455c5b00f13ed24790c78", + "0x34ef94d70e244f33348d2a6dc9128283dc3f1afcf36ecd401ce65a6b591411bb", + "0x177e39b3a769ac5e20e4fb76c9a7415bfaf51ef9c0a223401048d034ba9605af", + "0x0d0523b5bbe942f8fe43e8bd9ef9c2c9a9821bae3275d7f11cf6732616171f10", + "0xe28ae323d9e81be737e17dd2ae89b9382122fb54423b09fafb60a6b9d4c883d8", + "0x3e3921f0b4296aa80344d9d10d7d7a3c6770c81b4d58523ea956f8c153f66778", + "0xe334dd87943ac96d566da54e44afd7005f152608ac3d6bb3e278507d2bcea253", + "0x451289527d0c1b8598f3b387e6a5a4de045a6fc65a90eb62e3b4b8c54c7c4370", + "0x2a48be1f144527f40e28fe77afa0163f702ed4e61ea4656b8209e924a330431d", + "0xba6830ac8a807a4c3be88af792bb9b2e69cfcbe3d8ed3475eb4ecdea37e1b207", + "0xdb7ba7ee76711db916026a607d7e45f59732cd1bf3644285b5b9642e3d26535c", + "0xa30c304b2d1770d5b33ff226fac6326e90b29c192147e5f5cb13acd34e962b62", + "0x437a323990e77ef4f88a90305c598d73b7ec6a3eb6a1c55f3146a1a148918a02", + "0x4c9202286a7c3498f01b33d1524ec813d94824efcb53be27cc1a51a064573e2b", + "0xb673516c2155b43ed55c4fc7192d2f565c93b31e5cde506fd55901fc26f2ffc2", + "0x828dcc21bfd904b5057f426c2b7417dadae7c7a49ccc7446faf3a78dbd54a06f", + "0xd15bb97bccc0318f1360ac523e8269887228e4e0e5728b061df5f0375338f375", + "0x334ff818f1f4f4b3e23d93f86f28863c58cc6e57334b30400cf23066cf8b4ab4", + "0x8755d3c3661555a02d9974732fd97dc7104688db7dfa25b5555ab26cb40cb7ac", + "0x36a211434795bd6d31e88e505b0792fe3b026a53838c4206846a103aca75ba80", + "0x2455603cffbe7b13cfcc23acc32f0598f97c822a0263eafc8b916495e94b0d36", + "0xead09136373b7b62f985a6521ca86a0dcedf81a70ba67cc6fb9e79fb34f4a598", + "0x26889bcc96f97def588312a057ed36f0853a59cf8ddc88cfeef4577724a28755", + "0xa18617e3222b2321b2dcd27e2a9108d9d84765a1e203350117e8027627ab703c", + "0x4f6df4a20bae9db594d683a6f7c9ea2bd8fec741152948d68460fd8c9a8ebced", + "0x22ea56e37ef70b0688d3004312bad89cbce85db20ad02bc7bbec7fc39f5f7458", + "0xafd28e5b86f88317d002290dfe91b8d23d22b5914b52d6c560075d7fb7e7772e", + "0x7f4f145b9be1926fe6480eaeb3b2007abaef6cf8b802c64341d275ce5250430f", + "0x56fd2680f63e48caf48388d427de3670a9279d6c5c668ab39909ff2d6f5b0624", + "0x876107b7389b08894dffd99d3dd7f0c3debfe709dc52ff0aa2213016a154dd37", + "0x438888a9f4a804fd5c440389af1527207e89ec017d24b6e689c9b92bb1c2846e", + "0x25593858f01a80db4075e746c6735d6c0864fc8db99c65bea743a2322614a40d", + "0x764eb0cb15a60f0483e9b399950aacac4da2cbda1d06acd9e48b976ad7b34dd6", + "0x57595e5ed275b92cb8de16a1fe19b478c7ddbc41799e04e9c64affd23a65a976", + "0xb0afaa0bacd7bb85d7d1ea7027f1b7e1d254b46ac587515619f6aaa0d8a09eab", + "0x427b9f7ee201e40d3f1828dc2ea123a29b7206686e669253cfa8f73fbe3f27ff", + "0x427b9f7ee201e40d3f1828dc2ea123a29b7206686e669253cfa8f73fbe3f27ff", + "0xc10b1773b353e0d090767afbf919d7e2f2a9631f50ec5325893e3ecc7867088e", + "0xd7a3eb8380294bfad5fb3845ce4c7cf0615ff426d9ce52767210e344e94f8dd6", + "0xfd653b742757559d98283055a3ab1ef0d2a03ad455b69b8dfdc718adb2fae128", + "0x72ea3bfb1b141c977f823f85e2ccffa50a0e1482f3bf2125abad6880ea36b1c7", + "0x05968196589b185909b32c9435b3ad544c49817db1acc2bd8b01d474124043f9", + "0x16424062083ecad07a46931b9cc4dae7d144ebedbd5bcb3ba730c9247e289d7e", + "0x6810d278b325daba14763178bac43b10181ddb31b61a8eacc0c4e743d4d11a4c", + "0x2af8806d6206326f223170bcdd855752437177918ac06edeba775950283a0e05", + "0xde38b79e3d0dac2e99be1334c6d05cbbbc7aee497edff2c74d8eb0272d5aeda0", + "0xe78dfd07d4d9bbc595ee7622b657d9e51797e0d88b9a55720ed864df426b7ae1", + "0x350db9bdb022e60c41fb73e9a3c23d12d63b731308a71eab15194f0ab5f56979", + "0xf0a12af3ed4f772299fc7edda65bc5607e49f3879d2ab15789eb8edb4eabdc87", + "0xf39ddbce9f784fddcea31bb389a82a7931b87b1c7add3b05b18dde61e5c9af68", + "0xce9e9c0cc8bdcbfec23978a6872e28c2dfcbb35b201c6c239fe0a65124275070", + "0xbadf7dab9cd2cf522bd09cb15e4556dcf584e8b45adc88f613d61fda9af9e381", + "0x57a9308f2d073e05c866e88b5339ac80151f7894f3e2141736b60cfa96d1acff", + "0xf55d4c0a25378545d9d1702f9504f05e01372967cc4b3e76fc410e31fe9911eb", + "0xffd72476ba4b43f181c71fbbe5fdbb65c3c37bdb5a884cb2c7625cfdd95b4de0", + "0x663410ba123f487d961d98d02e38d5f0d07918a80f5d54d6dba92550d868a06f", + "0x1625347575b46e6c076425b293b5c0b42b6d1122fc0c8ca3dcf7f9927e2b4f35", + "0x54dfd76d53dcc69931f0e5cd47a41abe5009122785063eb10fffb2e9e849e10d", + "0x918e262b8784bc49806aa51a1805489adb50bf67e6518dd0a5a0f506cc6656c7", + "0xf6947c8398bf3fb3067d4504e2caa0e0ff461373bc74e11f16b8a7740b9c1052", + "0x43ac7297d059389e609dbc309bd8fcb38ae246457c9d5ae73b67ebab5e947f27", + "0x4dd9b6b08f4e7ddfb6a6604c965c22d1b31be6a95618b80e058f125d1f09a308", + "0x5551dbd2b254993e4615a80b1bba7418a964b53740c103cbf63a7ad6e3d43249", + "0x471976524d63b45031f17e7de1e7a18b441bad55f6b29a69db2582171b26bb82", + "0x16c279b98006fc4bb6c461550e69c8dc963fab30f08f39dd2f146d608332accf", + "0x7c59ffbab99c048f6a9f15ce19b66ea178ea08413129fa3e8ccbaf51a4b01b23", + "0xfa6a89b372117729dc194d14752900d4d3679180ab77faede98512938fab2c4d", + "0x16a496a5cac140c2ff4cdb05083fde9860220a0e436c9c40b89692ef8a6df3f0", + "0xcaedd6e32ce7ca5b1d3883b2b644c2f28cb3e15d3b2d5e23e16f4476f95db397", + "0x9015aaddd3e38e17e0389e93bd1e6398322e94c2817f644bef5ec85725b70d71", + "0x741b2532f755cd6408f9e2532749f4e36b28e184f9b5234a70ea2003c5e6524a", + "0x5bd98056e9e0a393818b7402dfacafd9f28aed9e2cce684d5be4dc2d517aedb6", + "0xbf4ad7da1b9e4f42d2a91ac2368c920627a40c9e5462a377e7df7c332e013a4a", + "0x7c906860fed751a1d8932166c0d2bedae51a81be78c5e4c1b68d332268e3e37a", + "0x10ac50fcdceae57e92d55cbf80953938e98d7181e76ce3cbc136239494f684eb", + "0x0b941a57faf985c46e166558c471180e7c1d08be6681fcc3be7ec04d9c786cfb", + "0x24ea1fa0e4b7fb92ed26164530814625fcc16151ff509f2aafe5b943db77fbc2", + "0x85efaec24e12fbdf484dc64c26f7592b0f3da1fd18d74c63272cb17144d3d157", + "0xceb7d86901937dad734aa26b531b483c68841c0cbe0a8fc2f29c3642ca6fdc80", + "0xe4bc464c8fef91e6c61b0272d509f8a6081bd4b51e09543310ac18eaf683fc32", + "0x9b658fe8f999ee6379f8da38202d207d145dfaa2c7473b7efab739cb7d6a6de8", + "0x45be807033328eb2b19a8633f2fda20c2434ef57997bdc23995a13ae6fd55a64", + "0x987cf2ddcd50e979a90cd17d4903815d289ee77eb6a1f0fbaf2160fca47e613c", + "0xa1850372fb6a7311db797e7b4e6cdc913c7474a198c0dca86f2405cfca2db705", + "0x53240f7966fbe26af8543b5ae038f3becec14a898c4e0f89002e6bea4a73e9b2", + "0x57f311bef9c31c0eddd702899e9e7dfacf4464b1027b6418682c9d8555454374", + "0xf080c2efea6c73460ef448e7366cbd8a18ef64353d77f88273d05621a24cb24c", + "0x84bdd4730bf43aa723bce0bd3a1c7181d82edd58888018851c60312cde30740c", + "0x1c74a5bd0e0e150d2853388666dd2bafa0cc9bcb6072ff77cedf0fcef8803b95", + "0x9cd577dc1c24ae3bb88f417c2ac5786f33e659fa09d97c429074a89652aafd5c", + "0x4a6c040bea8028a662cd09b7e6ee957f9ec4dfa75c846a9a6cb1b0eb9c293cfd", + "0x177509383d2d1ae3632e4e8d3833f6b6b16559095b84578250c70730f221f984", + "0xbb043323c81710d82e2faf242e92bd9cc664a35f0c568c5182ec4306cbda682d", + "0x4d5935e2530a0efa38f8d701a7b4b4727152e6a3b15ab49215b17496d3df1e04", + "0x95d28b90e26bd6e1d75da99800ee54b5aa3bed347bd7609d146322d1bd3154e0", + "0x551c7eb3b14c7ea4185bbf360721c4f4dd3b7d3f995ff43f71ba7aa4bbd5f6f9", + "0xe876a79cd1dfd76e731a3cbc298388301071fb18083a546f20c5c3a4ed82d33a", + "0x5d979b4d2214724b4c8e3c0b6cd8c8ae20f595fdc3580c6f04a11b8b04c44d8a", + "0x36f27e6ef7e962ab2063a141883de78c7247158a9105213d6a81d8d84f28fb85", + "0x37d5e2937635c07f0a0887ddc4cf90acd0161ee320d002b71c857070ef3e4cc5", + "0x6636f354d061dfb204dbfcc65b42f1062174a552d5a35067c4eb2fef2997298b", + "0xc42da31dfdfcff8b7b732885c6898c968c02f6526e66116af30ae53738d43240", + "0xbec97e365e79508e074866aa721c4c5ceb868d821c3d8cf4dd12d2e21beacd63", + "0x25cfa311873e642545809303e94486bcef4f9d3b6eaf4eb07a95e1c34c8d3e97", + "0x1ac217ee062d7ab2766a5f2d2a3a9910e170e1c5b6d2522d5c8ef226bd38727e", + "0xd333045c3152d626336dd7af6fccb4513b0f2b4f1f0292eaa6c3f15bbe87ed3c", + "0xaaa991923928447b6e1b85e608b447aec7c79b1095d48a719795b5b85a473aba", + "0x546e04ba3498a817b07813f25f4ecd0dd4cac9b42dd3ff4ff382a8f8a8735ff2", + "0x7e6e027b17f98c136da9f4d670756eb36e563678a7fa3c54de95bac4ca582919", + "0xab52112291b9e471962f1476329cfb256bbf758afa46eabc4a4da929d374f721", + "0x135d05acbdce6dd47010277d081fba038f71ad03c46105807ae381154e2280a0", + "0xa1c524654a64cee66c576653efcf689fc84ab5345e691acbbfec1bace862a3f5", + "0xb5f3a358d553371e11589bf9a95a3fe7f2b5d4a7c679a139d2deadad92a41ecf", + "0xc42f831c73333cb4adc52ddd8a99b33fa1e856c4381421a79a67f6f14c311598", + "0xc802ff904a2bf4de1789267dfefb92e5f1dd967c168770f5d70ea4ec2805507d", + "0x8770383aaf8d4f7f2acab03f12056e28640d79cf431c44203f7d8d6d7cc22dec", + "0xd8f8ceb1c7da28348f87405b59bf22e8f56bcb6ce9aa446002b911c40592432d", + "0xd40c727682fd1b762783da5a948419d2fb707b3827e2f5220961605d5ba262b0", + "0x1b024e0e4f04e4a070213eb24100f1b04f299447f1fb0b43a292fe014cfa024f", + "0x74a34e49e1d1c28b1ef44c0df405e0b56c1d81b71a17a7daa48655ff80ca9e7e", + "0x8a5070a204bd445b1a1edb05c59bf1ffbecefb8a6f2a998c3b17dbe6ab53835f", + "0x76019461ae6ec7191931c37ab1e955803f5bb36c64c7718c280081d4a27b49e6", + "0xc8f08119b1bb6e6788fb92b12d9c14131d25e898b21cca0d5de686d2976ff1f4", + "0xef8faaf569089db1dd0d9e04b078c6f6d67d1217261ff39e941cc46ea52e512a", + "0xc0abcd115271a4c544fe7b3f7e3e5cadd2d163f6553b6195ad73d887f1cc3a24", + "0xdf93829a34dc1494ca2aa61ea4ccf8e2f2bcde4bc7c4a873f729151582c1f620", + "0x037116d5cf35f1e2aeeea488986827ec7d6054375dd034f83759955b6cf3766a", + "0x2ec3c8b9165b49318f2a7deb41812988df3676e677a4c8d4194b2c7af4dc9785", + "0xe263913ea1b1cefde89506ae005f2ef6e7473ff89040046dedd6b0cb27309310", + "0x9c3f1ae9d1ffede1e5e623685ff8324c22482de7fbfe805b607e5beaec04f27d", + "0x1f771dbb18713043cdf70e301c62bdd53c25c9bead1093051b1528e8ad21b348", + "0x28aaca424c79b84c65719a26c5482ff1089f160a17985871457d10d245449a24", + "0xe3c19c57e1e5fbe4ff0cbb37ee8fa5b5596faba2e60ef5d536b4c8c02f722e0b", + "0x702ab89883785dd4963ea23725c9c1517d9677292970a7020f9f1aa1b5b94442", + "0xa478690cf08c6d99d93a916147a48fed854ec0dcbe1d5bf718208852c452c1ec", + "0x98c90cb8e079ad972b1f461a011840cbbb7d803c8115035feebbcce6349f374e", + "0xc305f6924a77348539c0eb7563b28d2ea9f6ebba8e66cee7718ebcb097ccf992", + "0xb7f999c66e79b5d7bcdef90d1e8125b268473ad17380cf192eee63e18d75f854", + "0xa668f26ffea118f8a53d6a25645d5769344a48191a4f3d0a1dcb2ddacf08624f", + "0xc75bad0e20b0595d7b3ad0e9590e99283ecba2c6cf5be3cf3666ea0e25bc34e8", + "0x94ee1d00689966c7393801c607c330e588c0d877f4b8588a8a33acc116b6edfc", + "0x4b2af6c26bffd76af8fa4b3007285755b63fddcff6522f19315a7ef0e9a7b0ba", + "0x1683cb548060eb1daf3124e4c0a449ed567bb1c2c02d4fcb48aeae74d5cfe5cb", + "0xc39f1b230031289d448e6591feead44729cc42feacd559746ea16427393a18fa", + "0x5a99c24b38e2fbb7383c92125e1db54961b0a7b5ed4928668336eecff4b28650", + "0x6ec3a95063782b3feae9216333e324aac5bc5e860bc0ab5532f628f29a8b525c", + "0xe08d3cce471453d3fe02a182836283b7b5a5a4eec8a9a66d0d5e05b95dacb32f", + "0xdcd0f7160384382c8586520b9efde2083ee788a60469d9ad1c4f7d83d13679d6", + "0xb3478a300cde97e6f71eb86a04cbb1d6a5d44f0955a6d27458de6ce283819bee", + "0x5041cead17ae41658f2bc283e3a878d8f43ea155837458a879ef43d2c29ceb82", + "0x08965f6ef6a1e3ab483179663139ed6e6cd9df87dff6d22e42442724e938f070", + "0xfecd74bd41a77c9794dac5e45080c6c6ce0bebeb365b665aaebce8770c404ea2", + "0x83ab65580f48d578a15b948b6e24cb7aa9e73b44fdb85b0a8cd91bc4494bc2b6", + "0x9d374d0a2c1377b39007e69675f7dba0d55f899f849b4a2a0e9c1d1530dd675f", + "0xea41824900c116328dfa4d4eed06b755ee3bafd99a0b0ab2811192078a01b4df", + "0xad6dca96b04876d464919e9280f3ff4436fea3b45fe845d441f3fe264289f4aa", + "0xae92a29096df40797e8db686c6e57c5a1bf05adcdd04fadb9d66f6a346dde9cb", + "0xb48d451170cb7162f81c54341290d2248e758d193eed60da13e496d740de171c", + "0x85cfca61f147b543bdc5fca15c5b9d48d5ce7f3296647ad3b23120f655b88fa3", + "0xa71e8d09e9fed47c59d5d4f6c03a00dc3f62be69dda35e25361a5d049f39f780", + "0x22094e6bcdbd55629703c51ac5157be92295fbb55e845af80a0d5aa622d82810", + "0xeb983dfe5da07c555405cd6195f04f6d2cb9e028247dac2f30744baaa6d21bab", + "0xf5e7e4d1168d4f111d97c2fc5ab43497a8afd136c9c4ff013eb750c9183246d4", + "0x74b1ac0b94f4f53dc9d3305fcd4a8a6bd76d1b94bb943fbbab607d62d06ce1cc", + "0xe124f7aa2b87e83fbf508a8d190d4715ad537744a6a95866c08a02be78046ec9", + "0xcfc3933c864d16f943a54965150845e034b2cd37acf2e077ab8da49351585731", + "0x912134b6fa5f792a7f4ec09debd2df9ce74727efefbbd438050ee19eef8d79be", + "0x13f7adb308d11140530700e711ca7ce8ff294e5da318534cdfde3b2d05f7b985", + "0x49d3ec4cbb4edfe3096f6d1b794b776093ef529d84d4fb8c7ba2f79e7532d988", + "0x6c8ac59108f86b71a7caa48b0f79cfd50f317c9622a4b6d9f9d69170aa1f9c14", + "0x5971468fc4e40b985c890c0a3b7bec18ff58609d4cf0f67a6fec809557370c18", + "0xe41aa633fc5fcc43af202413bdae26d3bb88bdf8ef025bf3d2435547aceb6471", + "0xbcfa56502320dd093e39b55b7efac64dd4af71eb92adbd6526786a1c127e372c", + "0xa0c898a561ba2e544ed0049bd9c4b0e6c172ab2c04273a3c08ec5c617aaca231", + "0x5b8362e98ba02d5a10e82493dd84090b412ee7eb398073d5dae698246cd1fb32", + "0x032057806f54eb2a3a0116bf7c0ec4e6f391a23836b0683010d986174752c498", + "0xe1e4ea020dbaa7243a1944a14f10903f5b142756989421c67d8f5ade5582c239", + "0x5f430f7b28de149624fa411feb55fec331185d2c117cd7f8ef53d248ad1e61dc", + "0x45ad3905fd0d9895e4da658f18f19b85a1230a519101439c6bc3445b76632484", + "0xd0a136ad50b5d62f9c37ae052deb64421c5e350b89a37062ddf7015c18c09f55", + "0xe76ac0935b6a8d9587684e5acd9a3e879117ac884b30da74c64ea989018ac44f", + "0x4991e50086fce20a7bc90c72924d862e78da43ed6659e9e82b3ab890ba071f5d", + "0xf095bc502e5514a8431c9378eccf8030a514b11b85406da7b785e376802ea715", + "0x0884573ded473aa3fc237497074c483dde7f35d2587afc8ca5e7fa9c899afe40", + "0x8e039be7ee688d174671d899e2fc358ef1abe6c87e885d12953cf9fb6ea506fb", + "0x0d132d5a68b68532bdb1e7212def65b29286e3f0571a40a6befe3682e497c5a5", + "0x72f7608437f6bd0ee4cc19d70df125b599fd53d316914f3a5613a5d80b8a3293", + "0x75e37951aed71e8002321acda4befab4cdee5673f26569429bf779ca785f312d", + "0x43bfbebdda584ebdf3abb3cf716fc6f9e65d25c3a2b8b71d9e414d876162e54e", + "0xafe82f7bb55b964e330102cbf51dc0290dac96c04ade77fb7dc9d88940276ddb", + "0xb1340caad1587ed1f484cde08b7d49052bd45366c5b65d6a177580c5338bd894", + "0xa6746fb4bbd2719ec9647f9897ccddb0023960d47c5d2bb380a44fd26bd4c8d0", + "0xbef3e524ad85febb20e90ca2a1c952908c7f5f913647572c7304a28f64d23877", + "0x74afbddd980d066a5954e65307b37a9492148a40ada083ee549bbe4382baa2e0", + "0x1c5292b9557ca250ac73b735dc8a0977e187e977dcc0d850614cff83d04861b5", + "0x8a301a70e49801c7eb621ef023c266e6b8dfc477059a59dfc524340017337cbb", + "0xb69331d69f4fb09167014eea52c051a68c889da3f52e222bd5af07fd9c45796c", + "0x751beda2b44ecbfd02cb0a631f81333b4d0f1ac999f7ae6bff8065f126f2bde7", + "0xcacde04f160796b41f08f0e270c74ff6e154b7546154ebc2be55efcbf9066ce2", + "0x690331cbcdcb465663d2205d37a7ed332f98e44891e92c5f10411bca77d6c68e", + "0x9d82bc2e1790b785e02d74d8c533e7e9a3f862f620962908c349d7388f7be8ba", + "0x5af88aec9f96adec4ca69a2014f10ca5721129804c673a7801b545c9ab7b7efa", + "0xb5a378acd6547cd97ba3b84ade8a37c6690059222e0836e568945d5985328211", + "0xc20c33a39f47c861b84ba22d704df0a596a170d74c9bb39f4343a24ad7af0316", + "0xf48841338f7ec92844ef75da45198f5150516fe4e9aac79714ade288b6a5d172", + "0xa8bddd629912d685f52309a854c312512e4576bc5987ec5e39724c9a6c5cd8b6", + "0x925d73fe740d736c677705ad076fd470b937bff7f68d42f780d0dda62c613d3f", + "0xeb87ff3dcd71d9094ffb319695c3f03c604b0ae837b1ff338b6812e2d849a2ed", + "0x1d881bd61de53d2c6c87de0a597228c9f981d7381c09322caea8a09aacc3fb15", + "0xd9da2b6961fd0ecbf8be1c5c76f1d9070e6c60561f8344949948544a579b2710", + "0xb0bfa15f870ade72506547f9153dfbe85124cb1d70ddcb960c05710d486a466e", + "0x1d291a0f325acffa95a58aea7d39624ec0e4b3964ea2cc97eae6f42acf3be0c0", + "0x378748af6a1b80c5b3d403f143cee86d73b104075614501ef4fff3f6a88d2287", + "0xb47754803d8b1a67af3fa8b3074e5296432e993020cfc056715400d3ec344cd4", + "0xd3414e3f754035fe13867b555189668481256f07d22aa02ecec31caeff0d46f5", + "0x31388661fc7644b8ee236d6b4ba4365c690069e50a8f69ab1180d497d4364b19", + "0xa0c91e4b592feaedebcf40b6f6df94435b5b0b67babd8760163ca8ef779e0ed1", + "0x027e3ccf20f398cf028de4a9eb88a739758dc926cbd53d191c2090cc90532711", + "0x1372c7a788d6551a264e848ff6bed3fe33e39cc5aeb628fa40682e84308541bd", + "0x39c4559883ca6a10688dd485b64c51a991e480edbea0994778baf880bad5ad4c", + "0x31ae75c4bae03b4fa3601c9b58a4ad63670cf4542b02bf712e28f00655254225", + "0x791f121ccf22483d863958c7ab12135986bb6ce8fcc7a2d81dcf63960dc045af", + "0xf578c4490b0fdd80ba3dd5e9d6ae585b8053a15bc990b4cea075e162e7423b7a", + "0xab3df96cfaa03158f3a02679a66fbb481262b276245a20b1f5412b09c7eb33b7", + "0x736c7482986ebb7db79c9e214e206a121ac1017a6cf6195ecfc246d99071b058", + "0x44852b65ade2a102bf3c319a424f93ade67dc048b1787bf866e86c0af508821b", + "0x6836639351869b17ab750456d547370447a7b04ed26eab4c4a3bfb67b5e4d7e3", + "0x2289cd3df8d7d72ab234336f424284b1aa81b4524539424415b72e847aa1b736", + "0xeae04b2fed438634af4ea4370207078b871d42132fc47baad6fe64dd3ad66b1f", + "0xf1f711d1fb6e89fdc40594114259ad367c40e16f7ab9183d6c83fb4e7e514d41", + "0x241b79870c3f57a11123a6fb1056ef75dec31e8579daed1c5967b020e102bea9", + "0xfe0e68e5ec58b92da638b6b9372c4ff77aa2afa18d17a6d42eb433fa10768197", + "0x3d70d2fdf3faba6805447fe393d413a7cbc3e06096e4b4f26eb8b60f2e8c62ba", + "0x8174c9e99c938d86ce81c34c485e3fb549dccb2a12a50df52f2de35b397c76ae", + "0xde1f29ba5102332079385767f4cc62895a1c05f936692e8739284824b541fa6a", + "0x976bf81b3468cd5d369273b42e2359eafcd16164bd7e95d597fba8b570daa44c", + "0x9142efce8782463d4c35f6e961cd0cae71c66b482887a21df87a286f8b57282b", + "0x324cb48b88d90d8877afbf9a43130a5d3e21fd081c0d10803ce4bbe35cb5f726", + "0x0e4699a294c965dfee3d5104a697ea36dbdf2a533a65fd2d5364db555630275a", + "0x841d5076e34a016ccba9f3041ddb325a002b00efcf5617975dcddda09111095d", + "0xda3b0357780e88aad6ece153a377fd928a36120885b4b0e8ddf3b30917ffb04d", + "0xc97dd7dcc5dd31620701057abcf39d1608b87208ff8bf898ce4560e24fc9f8f5", + "0xdc641b4f43b297729df9fff1e2e0f7a846b41062e82b9602f39210ba585df263", + "0x51b49f294a900babe28ce45174b005364f955635c9c92335abd10a5d70798688", + "0x064608fb64805621d9863eab3d6a159c1646fe8dcb11abe0faa3dd19d9b9ebb5", + "0xc9d9aa1c8583cd4c41a1cda09f2e858eb31a50612af8044a5fc5584b51d75396", + "0xb676571b4b376165f10cf2d20df61f06f593681b3841a3f2deb11747407a8441", + "0x66cba30124692d05aaa432a28e6377cdf3fc6ab079fb4ddafc67982119ffc7fc", + "0xf902290519b2ee9fc8802d1239b20fef291f9a7fd29d9ac4a7491d6416bc25b4", + "0xedf566dd56ef88414cd03f0d57bc8e3a604f5ef2f70c7e6e72de89490ba3f201", + "0x44329f9fb3dc861c831c95dea09c30ae1cf93d0b6654e87e2af0ad555d184827", + "0xa0e18ada69f8879473bc6b12f4113a2242e4fc248c4ea727e6b66bf532ce841b", + "0x1e348783b8d9ff22c12910eb1d02d68d8d31417efad6b723702adbfb32dc4921", + "0x5375bf2c9949f522cfeefd493fdee63006fc91fdabdcdf61ab9f898c6051103d", + "0x5a84d7fd1c1f214cff695072e67af12bdcaf274304f6be57a9688a77c287eb83", + "0xaa185e8d6c14e3d6e285ebb3d776eef976a6703f59cc6fa04d86ca3d0d4474e4", + "0x432b17c8c132fa62ef941e3a7250bf07d5945be287462e2e5a9efce8a6bf676a", + "0x93c57ca891a251a6ab0b2c9a5ff28653eaa9ca2fe2e93cabd57e46b9b00f5ead", + "0xfebfb698aa7c763466cf94225d4303c292e2b0fc95ad99e098fbf5176c990782", + "0xa8b4e6e914da41b4769a2b08fbda76d4ad8408fdb08575b4631b7b18f0fd1f1f", + "0xfa723a9c735d78ed709a7c438f2abcaa67c17f043e038e113ac1365f69be60b9", + "0x2b822581c55ad48888c8ea554f8fc7867d79b2a7621f7be37df02661d87bcadd", + "0x7e477d78c69f46eddab3ed30ae6f0ca8e42660301bebfebe727d1cbfa8837770", + "0x57a006b59446a34b641c6b4f82f95c998f8d1b64c361b9d7045da36d09e27841", + "0x22a0eb9c603e347fd1eda4bbe5ca02f932325e89c96108c7f654429080ac13de", + "0xdec38be1de4c2aec6b6b0464688de25bce64963ca304f49f2ca97734df80c0ac", + "0x52f4a14e8f6b32bf965e41931c58633391db9b1d50e1cee36eac2ba0d3d1a028", + "0x5071338507bcb1fe3bcb6ade2a3e552b6e83c0a36a1e4e5a2ae0420252883116", + "0x8198a053c1fd94bfa0b518310faf03b8d81b967cdaba64eb2db1aba81010b92c", + "0x7dea0b8980d2ac20c5b78273f1fba1d320e991d8c87bb6dbbaee733e4deb9ecf", + "0x891d079cab3b9dfa71f42bbc88c34b5634756143ea78a1ca7eb3670e4f2a0768", + "0xdc745007ed7dd3a243f359380230c0abf23eee328117dbb471f771c05ac85ec0", + "0x13b4f130790513ecf0c99d23d5ad25cbea6347caf8ff614540e4357d8fa1e5cd", + "0xd92dc97f2866ea8cb9b5c0c230e84a6da803f0eeb684065cae0dd4021dcbb9bc", + "0x8159f502491dff6ef252fae73feed06f2894f530aba8faf1f76c4ed9e01ed9bb", + "0x364650a92bdf12a43067e33881d4ae2ab313d05b928c5406d5092848935bcd73", + "0x51791b30196167ad18029e073c82cc6764fb1c763a1fb1e43c3f689abd6b27ec", + "0x555a5d6987dcdbda9de6f88369feab163aefa54431375f42d6ab20ea0b51a15a", + "0xbf31e9efab68676671d6280f415c0b5572803ab495fa9fd5794efe049771c380", + "0xba34b5d79770e3791bb4540fcd453eb38872b82b606887207cef4ff8f9093056", + "0xa5474d64ef4fb6e70428959e3217f386568264eec021734a8f600bb10758a9f8", + "0x64f6cf11b456241530d65f6d46b0a8ffd5985ef59e6ccc4e721d6e1158e746f0", + "0x72c5cd9fdf0ffb5f56a8fe89a7704ba6d253ef707fee56a42acf71aa600b9c3d", + "0x009827f77c0deaf328b492da7f32a9336173a15f735defa9889f71006b9eb60c", + "0x8ab0ce4790ce98e792d503d7ea612cf66fad26e80c88992591b4925a7de49c45", + "0x5c549af0448348b7c2364cee8abff81b608e6b00ced72505d833dd68bfc9c0f8", + "0x6092d3c713717e0661e2f1a4570fbbd3bd218b5a27eb3f7e24956d8286063b0e", + "0x350269daf801817c45a3d954903cea4fbd968889b0ec53257823c32f541e44c8", + "0xfca70f16eb38536dd3a128eb7d9b6f2ba4d179c4bf93710eb5e1e92b2eb8a6bc", + "0x8cc90b926885e591eaf4f0db9e70c50e0ad3a700f6d6dd656d7ffd142b040355", + "0xe70f47038a85239928d184a0514d97788bb1bec3edcf933fb61f4db932d126ca", + "0xec0b952c46fd8f22683ec9f266a41883423db87b41a50d416fbee70bb7fbb11e", + "0x5c679b65d7e418acdec9cdc837183642c1563b86387a2fee12b560cec529deba", + "0x002aaedf1820ccb5dfb74f5039ed87f4777a40a72a6b86213b973a674258befd", + "0xdb092af428126fdfbb1747e9ef4825bdfade23b1ef4ea3dda24dd8617ab2cec1", + "0xa591c3fc103fa4f5f5a26d317e94b42cbf8dacba0e586810b158a404f798d6e2", + "0x8f993404f56b8ec5215ac3786c92947896a289c591158957a82e8a5f2e99fb0d", + "0x9ef8b156e897ca5d60fbd819ec5803858608c8a14844debfcc9811e5983f2371", + "0x709684cef1f8d10d533f99b24f156b5233c2285e4bd0aeef83700064ec7b4f35", + "0xb80dd82ff3625c864e317ebf2e01dc083f2a0c77769dacc37374d478d76a5cd9", + "0x67a924d4a2c15384dd244cba9b97a1eb1e6d22a59cfdc7d91a15a28177795d91", + "0x0ce7e1e70eaa76ebcc9a704b79903b2e8c8a09e8b2d6dea039459bc1a5a5559b", + "0x0bea49d25fe2d288efe8099cab3e2adda1fb522170a64738d99a2ac935536d5a", + "0xd5e0c7dc8cb8a26a4ae20633b29ae999a2f039b2e57ed806e566f3806f31ba57", + "0x050d97ad6e28add7da1c37b17f691e95be4093d4290b242a97ccbc5b85d73e6d", + "0xbffa1829f7012255eccac73f4b814b2b1ba861ca996dc7190f77fd351cfbf21d", + "0x84f6c9be46950ac3974b8f42d884be7290bd8768a0f3d3354c0dcf56001e5c99", + "0x6075aac27775e495f39ec2b3142a980f00e4e015db1ad5b14e755581604300b4", + "0x8356730efdc1554509ad3d79bd38a323e359fc2889e65ba318235a1c4e11d5b4", + "0xe41fe6a54f54302266ccb2ab7c0f09b7241c4e555022543f9ff49d756102d6a4", + "0xdf3a9aedf2ed9c3e9f084807f3bca0c49ebd7179b4077a27dde49d13936cba6c", + "0x4ae0b16f55994fccc5a8b6012b12aa803f5d752b85be30b6fdcf8e7fa592d35a", + "0x11088f74a515da13bd5b70dfea7308fb5351e7af779a0da20b555ca295108043", + "0x72b93d03a09e00bc4cb4454372f4d5faffa3ada1c013ca7a2899e141cbc8bdc5", + "0xf64a7961ec570494b31185cb58c16c2489537db55f294e1b9b9d79147c39f28b", + "0x86e57cba3fdb197356c870f3ad249c69284023eff95d90e8486c6ebeb3f264b4", + "0x33e8257f86642d1bc378effa4593d1098c0d275af275042ca6258aa80bafca04", + "0x55b494f33002f22969a3931d155f40c348e6669cf1bde793f6b55687e2939a10", + "0xfaf30f2e3b00200a0e5ecb297c41203b7bca99ff6aa538d49697c3e579464bbb", + "0xd313f5ee5aa691f79ed3eeee3598e9fb89c00adc9af366ec1cd2343daf8872b4", + "0x5da74c3d52deb6d4bd666d11895c1bdda9de7280e1da3b8befe0fce3ed2afa00", + "0x7c592b123c419a1c9ab0abbd222602e6056e334b5bf69c55dfe642935673203b", + "0xc15aaa51609612052f27ff8308f49e731ccf43d226b5998bd7ba121947797e5d", + "0x58a6619384fdbf282652cc6137fec76bd2ef85a8c5a883a72e1407973721eb54", + "0x252807a96eb05a11c39d41fd7ca2db86967c6b79fa5bd1ab15facf32e101974e", + "0x5f89b6af3e71b798adf3af8d044806f4b03762ce43539fee826f1e9577c2c244", + "0xb268852f115240257a1ac6f0082403b9b15d5f46940ce77ccb81c8a614381419", + "0x566c38be304b2f1156569de4c7266d8545d2cce19148d447c6fdb38da338e46d", + "0x0b008c2401e795f758ab4e6c67b07a2ef35a74021376afe8fd368e8298f3c04b", + "0x1e287c4314fd9ff08cfcdb6f62824ee9054328dc4da66a67691d4701353d0ee8", + "0xc82b87ea8c73f80586c809364bb17c364be349077aa8c81f430360acd042f555", + "0x85cdc0444594e67d0cdc7a164ac3ceac7204ba21e4264caaef106a7c9a6be6f5", + "0x1561fa2f761f244e59adf9a8b74c477aea007ee0ebf71a4e5ab55f3800c0f9b1", + "0xfa46aa6395b2cf2fbb22bfdafce6a61e3686a77df6ba96d4040724b43111ee7c", + "0x06c0e29d4115777f6ea165fd1b396b061e9c7a452355112f724cf633e2775cdc", + "0x94c8f08ed29156728ccdeaa3c26cbd35031f331408147395b5e5b1fa252a7ddb", + "0x6976ed4cd6bfe4e9e3aa34d27330dd164091a5e39b1394e4d1497606ae511c80", + "0x653c4d52dd54c3552a3b8f76bdef337a41f1fd0ba0de43303784153f061f2b69", + "0xb8fc957c96c6c1b22beda4f266be9f64d4857945c52eb24c0c8201c02bb7d0ff", + "0x4edf78fa7ef48d5efd7bebce4a9d00d81b26e4fe90e7ce5ef62494331256733f", + "0x3682c4f1381b7de00f21fdbdb55a00bed41e1d3e1dc8251ff6bc08bdaaa15157", + "0xc68527407c566eee54b753bbe0992623fee79ea8c1cc7fd5334f4c88c4061076", + "0xef0fd81cf89950f1524eb415a33082d98c9a1c98de6e1a48b72e120401f10556", + "0xef5ca7898259439e08a14457430fd481996567f1c42719df73a22570f8b06a76", + "0xf1aa8f7f86de07bc53390d720e41198ebe987d9e75fbc7ecc091fd256791580e", + "0xb7cb8a02a69df042f8e3e492d591ecf3695e7e54854144b4bc07005d366a75f2", + "0xfecea3ed8f83b01b0d4352c3e8b32dab09ee345e6d2b15184d100c2c381fc9e6", + "0xbfa041e48e2276d7ea95a331470b9597993a32aaf6eee6c9f534cb2a051ccdf8", + "0xfc5d7e395f8456bc0d5c2992c354475cbff65ef903fa591b8df91bbb38e99871", + "0x9ec196a9c9cbaa339d7162482ecbb42cbf09059f03bd657a507965f51863a32b", + "0x24f575d154ed922b08989eef1bfc0b5b391465689ebe06aa8ea9c93073599dc0", + "0xe40cc8f46d2eada692c7a24b011aa01be84226d9e872e64116c840f43fb077f4", + "0x02948107dd28cd9a960d1dfd74850aadca1067f0ff083140e177b8382e15f661", + "0xf12eca3d31b6e5db2c691d03eede6254732c793810f2caecb709277701d76d0b", + "0xcfb25577a9b17c6180b7517caa37e886cedf4d81151952b875d098ca60512a3b", + "0xedd6439f32d262331f1ba0f32e708f2a4c276b428d68a1bb1a314ba155aa073e", + "0x243bc7f71e8cc448b81c57c813d1caccc8593a9cf402cf22463d59cbf9817476", + "0x60defe1c412681fddd761f76fd6e2514f6a2b6805054c3153bb2d0ef0cb958cd", + "0x4428fc332b81407e0ae1d20ed02e1d22987301602bf174fa2385fd1c2cb24094", + "0x8d17147653589c5775b0aa83e1c67dc28415b28305ab3edf49598ea0f391ce9a", + "0x8f9c9a092e0eaf281ae2710c52a307684b4b9f80f1d7cb7add8603aba109a624", + "0x98d1b5188312e252f48bed54ee1cf9cbb2b8ce57609e4414b77ddb2993bd8b64", + "0x075f549e68b0ebcd19058cadd5b8e53b6c357576f8fbcdc07b372f836fee9f5a", + "0x2449b75ac97c8c3033e7fda073688f924b44e7010830a08d7be457609aa99e9b", + "0x3e6877610730d22899b0435202dc4c10a335c8c873c5166e0d7d3f6fe8698bfa", + "0x6dbc5ce9b4c7b42af9cf6aacc6aec57d83681888f568b23f42581eaf3fb404c9", + "0xbf5c92ea210642b51ef5c3ffee2c5a8c6c6ebb4a1a84a36c688cb9e0b6659a56", + "0x94703344c97c9fa3994b725a5e62f5fff8e6678b6bd3922f1a71a53558a109cf", + "0x47f3543b4f13504eec88ec0fa7410d6fc96676b2a183f91688201c49a88ae451", + "0x1f2f0f8852fc54a84e4a4793ade3e68d67e67f7aa2aef262c2a73469e81abb84", + "0xd492fc7a7db05a074daddf9416b30ef3ac49247bbbde3199ec08d2995fb67b89", + "0x33bcfa163a814a7ed80a19751dfdd0e10b6f1a8411cd59074bf5d59e6b4a8d5c", + "0x84ddd749897aafe4f35545766e1aa3483bdbd05b970c365d935f802eab181bc1", + "0x6ee867225e4700c150d43cb188e81aa7387395d84f9815926ab04704b0a2ad61", + "0x9e8cefb03c89fe690f02c9ca09fe4d6d29f16dc92eb89a0a2985793bb2a87492", + "0xe2917da970b1f912bca322639e257569f3d561cea0fac3671dc013b72bdb5451", + "0x8fb462be79afbc5076d4cec04e135201d87085a4c5713c5b686494c824dbfee3", + "0x21ba6762729998b9b5e380ff5fee47b5417fdfc093ae32b96fe4afe2786a0aec", + "0x52f1901099f38d2b33706d36b84c5096a6b0cc6e979491246e51f71be71101c6", + "0xbd8376a36887ea9ac262f31de1968d2ab82226c1ec7dea417582a20bb19e1882", + "0x0c9077e9f0e9be52928d4aaf2bbd0ffc7ea95f106c2248d3e647864763c40eb6", + "0x79f3db4eb51bdc332219d2ef2a14027453b5ef0b90fba38a4056d741ed95bba1", + "0x6b130caa2368f1e93720e58736633c328ba09869a056205a2864d088fccbabe3", + "0x3b59e31dcf0cda93d09cffe6fa7ec3c53632715eeaa0191eccb304cf9cf48e6c", + "0xa1f7ffa999bdfaccfdbfbd6f4ea8f12d9bc65005c4c8c45237ef282eb7a720ac", + "0x8558f7e566b27bcfbb9c1e5f1d49bfb15217001bb48e65c473076d9e1b2ab9ed", + "0xadfabde2531e3c6858aab02026f7a0f0de93424f358ae473c87e7f23204bc70b", + "0x3c321854b6504a5bd48313da876629d6825c2e4bd9967f61a9d1077e4e033491", + "0x655d60c73f05ca65e8b7107970209e81d7682413931ff6ef13113fb8a4c977ec", + "0x2c0928195fd5af120d12a8da00e0fd65e5d9eac57d3323257dcef374b92f6691", + "0xed2de2341d2e9604ebd3eb3c94ce00e41c9dadc568082fd2fe61c8bf5cd37e16", + "0x7c89d5492e0115a98a8a6a9c0898f574bb512cbdd07699fc4090a6dc2d0d3977", + "0xad4591aa1b035af5428da9e8ec54444bc2569825655818795832eec038065c4b", + "0xe58d10c08173e5c97fe97ec690c01c316cbafca8bad3634eb65b16e88017f64e", + "0x1a1b0f6652c35fba974eb46d0237906f2a7b6256435a79f5641f8c1f9717175c", + "0x80c841d4deeaaafcc4999ceb5e721ec74f178fc6e46f0f1f207324ccbde5d212", + "0xdba1009813a8562583d737ac9d1cad005a88c6cb3684406b41570cf53f1cc188", + "0xfe43a1b19fff7834130525b745a89ab231d726ae2cbbd57f2f5248ab2749347a", + "0x1f59455c286d0e844e6c9592806deff697e9009f8bbabb5a8f03f1a13385ec69", + "0x7b81923aeff9f98758936038e218555befeda86bd822e7717ebf51d1ac182f79", + "0x8c6fece014548fd4b162cb017a7fe90ed0bd515469cd7bedc6e13522ec677f2b", + "0x758a168d3157fe7ae71200d98c0712f9f37edec264c33b7677ce96c24e7b4a87", + "0xbac0da4c302a4bd1a15689881cb21df3982a24ce152d76c8acd4f4b93bddfb17", + "0xd1586fa3305d00f4dac3d9ff33bd830838c8032fd7d48a207864ab7f45ced5f7", + "0xd7ac8aab9ef12602eda4d5c12ce42bf78e43ab07aede23ca371c172b929f1545", + "0x57213e30ec3bd16005ef7aa1b54e336a3e0959db463565a75ba80d975ecaf4cf", + "0xea34de84edbe275095fab6c46a25e8586b3501e7389bf94241fbb84566f0d837", + "0x0cfed7a9a1b0916a1f7a11b00ff47f49414d5e82df7dfbe0ce2d80e4253e96f6", + "0x764ee721a4be39b83f6812321ea574343d1b2b4bd0f4f734889be385494ea7f1", + "0xea29f970d720a649e19621bb9129251344873abbae057df34680201128e82ad5", + "0x9738182270f170d17a6539a6f4905b82961b64874a4686ee5d5a84038d2b46c6", + "0x2c34c31e7819669a35d0a2a1ef5ea0fc5dc68c9d557fa3a7a1e9d785a9505169", + "0xb5d5d79004e9ec9c19849bac0246dae666563bd351c63ca070a2ecfd0fc3d75a", + "0x6c1bbc2569f254f8790b706f39aa014447ded3c694e64122dc6f7965890e98b7", + "0xc3c04bf02f47d70bad3a3d88c1354d17d3e3812151b012d01c74c349ffe5c892", + "0xbcc591965b6a08e5f8507d8ba58ceb7f8d0139bff5073f4fe3f24b32506c4a73", + "0x1f35a15dfb446f1c2e4417abc70bae73653db746e1bbda8aa9990d15d74807ec", + "0x5322dd58506cefd28a707f885c9cdcf755c5c67020e816835b369ffea8fb9999", + "0xaf11cce04fdd1204b970afe7b43b62f4c8bb7d3c18fffdd3569cddfb144ec078", + "0xa1adcbb3533d838ad02c0aa7cc55b0341d048501177db0a36344c8ba35556318", + "0xeb4afa02ce08a4852b4d32d0ae83b0b7d99860e7bc0ee48cac50bb3626734c4f", + "0x8a7d6507cab955ca3ed05de29cf3dff9212201360ce8aa7c2bb7f05b82845405", + "0x584042c348064247565c7e3d2165113d163edfa56f10799cdf56de983fd8a985", + "0x795f6f6e8c51f5c5073bd0919ba12b07f80755a9ce925a08516216655775057b", + "0xd4af70cabc54d043058a183e76eb45f0511899eeaf25a392d3de5a42f510dcb7", + "0xc5f7dfef337f27817a6d5e51b32ee83a83278ad93ae969918bb78309dd04b669", + "0xf7f49dc5be6b94cbcb8961457bb3a1305bbc038084f6e2e480df283a5c7415bc", + "0x6116dfaa9c97e904b65add2cde9197dff0df6e7aed86b9f792a4539ce14a2921", + "0xc48b9a2e5bc30f59fadd3686bc7e0294741a45f06980097807c00b4ecb0b16bf", + "0xff1a0077288a5c08b14942eb8b0b950f9282f963461aa254be88a128bfd1dc8a", + "0xd4bac82f39e8d7d3ec4e6d212d96a35d90ac4a6cd6fdb38a44dc04360eed3db1", + "0x579c34925c1c73405d09db947a17bbd24a69aaf3032be2039cfb218fa3a03c14", + "0xb180ed65efbf15ba768e74565104be4b8e7375abb001f4c95f1611b416d1693b", + "0x8040f1d0b6468166766929284af41ae9a4263ee8584487c5c1563c7eebfa0e1c", + "0x85a696294aa0208a9bf6afbdb4e8ac1afdbb2f8e3d3e556c7057fd78af352c41", + "0x5a778ced0179939122519949472c9a406e65e4fe59bc6bc93ca6c0f08b8b35e0", + "0xca892fe763b506b4ef02ed77cd5841e0db89c5cd6ffc04550d5dd9344e3b2c80", + "0xbe9e3f751ba0566ff903697690162c48e734497c0707cf80ea8d8f917b2e6b44", + "0x130714445c73bfd803341490a0fe2bce810eaa2d7da246b9c7974dcbad9b8882", + "0x009cef7da54cff1ada1a1011ee25826510b79904b5ab436970614d977f4c68a9", + "0x805bcc7291f138fc0e15568b8133ab51d530ef6e53894427292f48d09a8f4830", + "0x46983707857c4bf7b2ed9387dfa91142722247c783b4bc0e303fd9144322236c", + "0xe3bad2ac1107fc4bc6c07733c10ae3aa266a33f488283b10507059ccfad05054", + "0xf692d69b852a9346e23cd8ca67c417917a04aa94010a52f061311a0d3c03ce84", + "0xdff35f50f6d680388783c07a3e7345622f70419e01769ca5bb7e884ced41c755", + "0xc154f2e88f755ec7de7aae8b884ff3703c3a14eae1bf09a6ea29948c22638c58", + "0x2d655448ff20a8771b424bacc8e5e76f2ede89d84d362c74b3e7cc1c5c89c7bf", + "0x52977235eaec03aacbcb6be43d7990ea8c1ffd5a67b0af1b8f42303bcb110b43", + "0xb29545b3e236555a0fa9186e8dbfb7c88359a375a35db95e314adeb61f986bd1", + "0xc80e3c3e0f207aeba7bae3d1fd5e04e0888e3ed776fcfe808484c8f15145308e", + "0xb8a213aa3606127be010070992deb9104c3166929d862336e199c87c49cc3722", + "0xc53864974acd92d235ced3ec322d565143fb4a044e54eafd22f0e310d7c8f9ec", + "0x972a23ebf6057b9e82f755dcf6259beb96fe4679608e6f3681ac9c9102b77a73", + "0xa3afa11419591ffe305113362e98f70ccbb6ac37c61bb317925d63272b387bdc", + "0x35bfd744a00fd2bbee8680ce71340037f9eafa5b6043187c0d6af5b3d89b336e", + "0xc2d018dccbc3625c847f32ac11266429c1d9faa5097a26f87194beaada95497a", + "0xab609883fbf9b18fee08641acaeaba36f64192135cea7c55832d8bea17f9dacc", + "0xa3835e68382f8017b5abe460a251f87460f4a569cd3e7c881c28f32dc042653a", + "0xe72b0fbb134a342476adf80022c1d8479719a41f16c0507863c18bd353686efb", + "0xdeb328a92daf8dcb3fb412a4237306a4b863bacafb42f18f63b416ec71414e5c", + "0xde01acef3935c0c0f4667b27a2213309c319da0ff72cfa2a8e55237d861d9040", + "0x625d15b2343a31870c19e2fad2702a4c7fa01be8ab1f43f3c778b0ce4ddfdf37", + "0x7dbc1c0970221af89af6e2d9c90497abfe834bbaf16de5eab893ea9e67cba3e1", + "0xc25c38ae814b1c6d06ae17751eea4e19709ab26756f2cfedb51baa0524056eab", + "0xd6ca68426c6d22c17f889fd7f4a28f4b271ffa8069f4886883071110703bc202", + "0xa4ff9f8ffbb08215291bf99f1c0ba2b2527d1ae758062996a499bb2b47751e3b", + "0xeceb4b4147c6e2fbcd5fa77d78c52f321fa2250adc09151fa2ed8a4a4df7a17b", + "0x62aec2ed9e1e6d5a530646d4db47c3711ea2ece7e2aa0a1144036ee0873745e6", + "0x05d994f1187bea2439cee6617a0b7f3c7351537c82707ef3a87aede3ae76188f", + "0xc958098874b18673c6807490a30bab54ad4a54c6ec110845be3c22deadafc756", + "0xb49c2190ad9c13e46b27f80dca4bb778b75e4bb4c063d6bb44800a03983ab27b", + "0xc08422debbb51399734358c8785f62fd9650edfb1e3cdc8689ab7dfc6c615444", + "0x65b6531d09106c02a45ef05a8b0704994d9f4b05b9ff58339abfe383e3a0c13a", + "0xc65df3129130aba9265f3c8eb7cad92ba5f1437f47fabc15d7b094697e8ec6b3", + "0x35777550abae56ac7742a24b91290627e7e8f079555a1ec2f92f5e0d18c390e9", + "0xcfee161ae9cfe4e12b6abcfe1260ca36d8ca44cc46c95e957222607037d8cea4", + "0xd847948d570b05f3dfc8e327acb58a28ce63062a3e933b8eaa3e57d104f542fa", + "0x2af5722e4fa62d1f9005e3d6ba9a67fe380fd1df176f4ea58d1c1db345efbc31", + "0xf493a20957696fee47116f2d4b458844c93d43edc9dc62f204b4c769ccb2a9c3", + "0x574c0c563b83e52a5294763e6b06caa1a5cbed812c3fbfc75b2db4042f396e6f", + "0x748c20e1d2bfbedfb228e577470aaba71c21db13042f847e3e0f88753640f13c", + "0x5e2a90edbea69d99c001976c868692864358b10b1a042790bf3dc0bf20de6ce0", + "0x5322a22634b745d1a5394b2f5a54e2b4b08b2c3045fe67639ce56b3c7f6ab74d", + "0xfd71340236adafeccd4067d7f08d92e0c13db2d8065d685767c83df10c6adfbe", + "0x0a4cae7d3678e44629b6319d8a6039fe6ae6bbb5ddfae6ec4aa1f5e469db970c", + "0xfff460f470a9e4e6f0d8f40ad50a28935a9e26f24e9ba24b0ccd1568df03663f", + "0x687b7de6a1107a5f4ce9b710ac6a7b756423fd118aaae40c6268727be478197f", + "0x1368cbb0e29c772e917400bf83bca77ff797723b44c0b00936ada2c4f4b506c7", + "0x124dd05921f622c9a60fce8ea0c053768d2bb41ff1a8c80faef164d305957d51", + "0xd2f7439917469193681897d942401022dd1a594b819e52540a92277a7b948673", + "0x6c162710d7e94c39b8e93aefb5073ca7133a9e84c636c9c3376dd6fbe40f4755", + "0x90c94141882e5cab894052f598b18adbd338d3d3a37e9a8b1123f2c61d9513ee", + "0x3a63c01fc55e09f9b319fd1e6eac22d24172fe52316bc593dd303deb7dee1f61", + "0x5eb6fd27abf60ef85432c3e3e2b6f0acf9f3cffd928b8ca83996e9a3b5af5ade", + "0xae453d2d9fd30ba8f2d97d39355d1ec4220ac1f0e18ee831e7e760b94be24a44", + "0x7d25a5b2312244981adc49a2eb38ca5167bce59a31621724ed64f00bbb74332f", + "0xe03c74e3d4bee39822db58560b4bbcdbc6c66c86f7cffab9fc5975d5e8a8a259", + "0x0b159442cacdfedda6dd04cf7fe25b1d9825fcc0ea4d93adc6b785f3217db9c7", + "0xcd0e38948f941e3f491e44b459d5f70364d181240cb0190f0166794594b8cf50", + "0x260b6bf77be7208cf8fcb447b7a89576f594ad40cb1c6373cccf7047306a4af3", + "0x1815b351ddb94301dd18a73ca7ce8564b26e1370f87b385ec66ae98070b18400", + "0xb897a07ea42177c4cc075dafbe4cf90efc0e98693fa3aed18e88438f8624f5b6", + "0x7fbeb91359846954cc19611fb641eb12bf54eb6385f7724a1c650ff4c582bccb", + "0x082b5d2e463bd15565ea8b2d5e03b6c406d3d54e0d63d88e22c83435028c7b6f", + "0x4b144ce5e9fbbf002f6988af5c65e3ef718cb865e02d3e42f11c18f93ecb0198", + "0x6b85c02ebd1e6a1a97af8a03657f60c70d60361792dbe75ac92815fc8c17621d", + "0x5b9e517353398eb0626ab332611e5ca4bc26e9feae5278aafc86f7b02d0c2fa8", + "0x503e9b837c6f20b36fa067e4636c4f2bc5a0283e4a246011bfdcb5512d0a4914", + "0xc1e4a7bc0303344047c41b7f2e69c36bdf1db9b84a8851da35142bbe846c143e", + "0x8cdee5ed8ab4147bc5e672b65975f35544cf75449f03a10705b7f61ef7f9436e", + "0x429796a1b431a8d344f3b105f232eee7905a0545629a37ae2f176c2dc61904f3", + "0x7c498892893afa9a9c9efda544d37ae06462a11c24781489178fb989dc85e1b3", + "0x9955660b91e49f31f036cbf07f7475410bd52cb9c8d7db70591f2ceada2e6178", + "0x16f58f51ad16fdb9290d340de1ae85f6e4ba8983f56e5b8e7690fe18ace6d174", + "0xc5671b60280e769b4190ea1e0b17cf67851827750512ef85be36ee006442fb32", + "0x5f42ad8f5833077d31c48afa40ac6e00cc67ef498fac9116367176393e8b28a2", + "0xc3463eac18ddc4b8a96ab4d83eacf54837b179832499884f000df2e5e756c4a8", + "0x08b56898a213663c9cbc650421aae3a70413d3ae4297326b602c3a4b522eeb56", + "0x640b1f701435024c223870ea12f2fc2dedca32e333fb15f912a917ffc9d66f52", + "0x0e117b1cc631cb68b5de647eeec23e1583b890ce9e354c411ae82e179ebf244c", + "0xc5caa15aa829e7cf42932dee4f429653eeea8b1336ba25dfdbfa649e347f7993", + "0x5117c3571ea863e03e4e662c384d133c597966d9665139be1e617172b9f9f8c2", + "0xbfbe48ad49201002f3cdab97cde4d063836a79b36763947f5ebba6f3638a9440", + "0xfb69996f31ba2a1dbc5ad76131772085f616c4c5420855643f8a2c0e95455821", + "0x7ed6473b020107b23a3b1f8b7f011839b5fbfdf64efcfc9f28aff4078544dc38", + "0xb2494896f1a0aff47647d295f06db6f9f5e8246ea2c2d293bc296873daa901bb", + "0x4339b6145ae09d6a48f1bfc59466f453df9cfdaaf20eef207e1385c3d4bf2b38", + "0xfc678e9ed2e6af88b413d0e5792162e2b42c5bf36e634d160ce8f33af1466642", + "0xf7ccc60d894d7f86e675fcadbbeedb8c851432d6b31990faca9aafbe89b8ae6b", + "0x0d3ce4586f813fded212cb059af669c255bc54de8ea85bef54b120dc5bd04e8c", + "0xe9b475813281987e2cd86cdba417ff26d8a741da74340f217402020829b4e7c5", + "0x3878109bf36f79c8d8152793c0bb98644481d5a7f60cc9c41ad789e39f1fffc7", + "0x627cdc8352b9b52a39c7a2288e3b8f36d4767a6d2e62d64f07cb53896daa6309", + "0x9beff15428dc70eb80faefdb2b8bfd1d6b791bffd48371664e12739121d2c959", + "0x6ba25cd2e0d960204563dab80c36a4fa71a642fc1e0cefa632ae29145313a187", + "0x6436ef980dc199c7389df735cce0779c2864e800460f88eabc7ac5a874621906", + "0xe53e70c1eae4afad8b82b246aeaf93d80fb6b28485a90c1ad036e65e2d712840", + "0x21580e28d775a6269f5cedb01542ccd0e2fd71a5d012eee0c04e2354273cc866", + "0x1130bcc19231ddcf40981fd06e3839130a6ecfa04c3cee51f114f7fbc25c6934", + "0x8ae81d693da380781ca972a2afb4d9c2d4e0074458015fb2293cc65832a98af5", + "0x3622a83c10a86316af452d72cbc4cbeb3f2a5a552b895804037e4e2e21043fa7", + "0x7dafc8fc3f2a5941518819bf6ef0b033f140a66fd8a9c8328a996412a9edfb70", + "0xc1832b84566b11a80cd836a16449b4debeea8d718e6eb11fb0c0549e395252bd", + "0x2a655f8d996a7ae16bdac11c635e868446ff4bfd9083d02df5a488384f50f255", + "0xc6f8b155fd343ea46b766ca57a3d1fbb2acd1be5f0ed4dc6ee5fb14e684f204f", + "0xbc2f611763c4ea4cb0895cb8bebc5ae09c6aa40e60a4f5eacf9a744fa2db708e", + "0x8cb58b565831685c2e44c8f472aa33a64a0b163beb83ff2996f4feaf983afc25", + "0xecca3508b7f56cb69081fe133c95ae12b598af52a91e6254e24447cbb84d7e8a", + "0x8fcec78f4c24288d1790da7ae7cfe51a23d2e101f25c76bf31aab00152db5645", + "0xe225af9d399d739467489962abe99418889223140c6c01d7897bc90bf46ef808", + "0xe0a6aa5eb0c0a2c06568b483fd99c7c497bc89fd1b65b84e42841641bf13ae77", + "0x524e1cb7f3229149570339fd5d15647be461cb4529418c2c6cd8c8bb2bc1608e", + "0x02b54a7ec260491fe0ffff7bb1d08781d897b96cf41d1164fa01e2c6cbf8a89c", + "0xe25a58c113a78d2aa181e0788fb5d2209af7b806817d8bd8f0ef9604218b3a89", + "0x168a7425faf4b98dafdcbe4c67bc98d5f0cc52119c5e98d3d6bf01af5906166e", + "0xbf9cfeed2f99b6b4a77edf6a73f42a906948100997f1a858545d66e0f61b90ca", + "0xd0f7fb958b9ada92faf007eead3ec6ee89b061357dfe891d8f319ae534d2b512", + "0x20e0dcb3e5461ff7f2f1bdc50acc68f7a880956d8c7dfc2a06e7f941b00a2841", + "0x282a9f8858c6602c5f5bf4ce5e485b5fab49fc214996977aa1aea468686917e7", + "0xe74bd9b661d9ddbe1c64ec20cc36661aa2a92e3ca6f74e1fb569b11b8544d1d4", + "0xaf5ac548b5454efa961e729699db301f8ab6aa414ebc973475f23256a8ebb02f", + "0x4bc9c7f50e2e0d2f780e85d15c41a4b0f06f00932db9ae76eab253a74f334d6d", + "0x434ed3659f0b94b328c18c2322f95a150d173cd04d34d662310976b0c2bca579", + "0xb1d331f4a052c950a6bb442318b0bc48b447735a715187774ebb257ad1a4dbda", + "0xe4bac544b7b424a6cc6e43f6e63ab94eda2d683c9f52b3f1bf80788342fc33b9", + "0xaf07465f6cb46ec0fb10d9eb56368be96aeb9a8ef630538b1ca78462ace4df79", + "0x723d5f15db07f5ca4997732fd7bbbe7a6e83bb21249d7f2f5ffdfc296a4ac3e8", + "0x554b598a09ac744f85678b551e44c62aaf92a5365e576cb6760eb11665e429cb", + "0x7c58f6731dea52cf8c680d3a2c6fee73aef0ec2db4000c836f309b34e1b6a3ad", + "0x9d80e949e03803d2fa578c246c09cbb403e122dca81e629a730e36e4d9818262", + "0x6800e56f1ca225d2315969ff4d7154c27d54b9c35ebc178562ead72081d3b1bc", + "0x292271b5aca6018ca8d66bd9c44e136ccc363ff9b4dbfc017eacc95a855bcbb9", + "0xfa04ab33f7b24c4119386855b5da8fe68f97f221189b08a887da846538884a3e", + "0x16e79b0d1f2f904ccf37a7797430a05d01789f574b004266da09b596bed2b23a", + "0xce2f1304b9fd6a552d6052fa03f7c3ec2644392e04b1884ee6bb679b7d984118", + "0xaf6895fe72bac64bfeca96610fb93e5aeaac1703d5a61b0f523536d70b4f76f7", + "0xb04e782f418e19fb0a5a490a8795518f30d8e89b88bb9655d8695201d5c61440", + "0xfa916557b03df6af19b1c6d2016e5a6c6e46bbe54cfcc8ad824eb6ba1cc108f9", + "0x634a291e512dbc9df506ca7412beda3f92e4d99b35b216172fad71dff0858946", + "0x1e9bfa1a24299b29de252348a1fc715e1d1b23d4138c681ac8fce0acde7ff123", + "0x843cbcffeae95fb9162a18d428b35f3a11623d619a93edb1f0794ec72ecd47f0", + "0x78ec4afd43f678eccd2579c4a2d03ce649eca1f9beb602ac4da11113dcd0ccb4", + "0xe1765f8fa9ce1203d3cd2dbb0a9380e7e123e3a5d6895fc91c7dc61add6aa204", + "0x8d21bb1cc90dbef4c278ea01f6934a1196f73c0f25534495198d238423f1133e", + "0x3be5c60c0005166fcffcde2e908a1c381d1d8b16df59fcfddcc8452a4e0d61a5", + "0xb2f744662b5358cb1bb698e4213f1e25af0ac76bd363e38523d7be02a5b5d1b6", + "0xf40718f13aad443fd225bb10fab835add2ce776bece2101c98ed7e9eaf307d42", + "0x8b33d9e4d9a31ece46f1c08a8608dbdda463d1e83693bcaa1097c4559e615890", + "0xbad80f590d83ccae4c20ca71ca7e163d8085ed8c7a9f840f6f45b0e6189d90cc", + "0xa076567b917ed732b264b731a6c28c7022b38c098f3fdf556e6e713b7e598164", + "0xcd6c732d1a64b6f6bf3deb67ee504cf3384287d1e03dfde1f6ed8b0aa930968c", + "0xfe90d9f9932535802d2e7eaa41392060dcfff7db4312233225c4bc990bf47fd8", + "0x0b1d70637e85dda4869b43b23796f2c3bd3e36b913a13127c47be64404f01383", + "0xb9d72a8b31b6bc1c278dc274789c8b275687d82563ba84422a2cb9f97dc6e80c", + "0xb051360fc7a6b33a7fa4baef847b6eb96e15dba0a4037de46061024a75a4c70e", + "0xea78f7298ac6843e5eac199b8fef8abd61215711f9b26e42f5308be7a72a7131", + "0xaaf7fcc39829401ea8d94376fd6c3e2d736a9320c36c9909e00b2b0df4fc547e", + "0x9dc8cb2f02d2386b1a58a47773443522c1757e7b3034cefb82043c8fc6c17147", + "0x890e8eae69ebf2e1a0694b754f1b3c443089739744b1f7fa307c3b464aaaceb0", + "0xcb855d67e84cb1f50ed7d3cd6c9f4cefe0fc9f1144af0c7eb6501c927b535941", + "0x7c73fe7ca96ad3a44acde3296f511cb6286dab71b950870c048e3da4800dfec1", + "0xb2b7495b5bf126f5f9ba3f2adf991a5b753fc5b6951b94b2daa2d4b39d7df69f", + "0x3445872f201e50ab9c1e3355fc6e2d7f4a4810cddaa23c11342e855f46d6f602", + "0xfb9f31d62e5503775f1669053bf022a467880fdd790bec0a096f8c769c3aeb0a", + "0xb1e9a9f6145b050a94387a583c9dbab37760365107c0f7e533442f67b983b4bd", + "0x87b4e0e7e7bbfeb175ca84dc78ac110ef2a24bac2fda4578e3a1e04fba689cb8", + "0x5cc31ab87833cbd86867bc407087cefbc25530436465dda651653edc3e61b382", + "0x2aaee4320a5210c454e78c9fa9eccc6da607bda04535b812dc696687c77a0ef1", + "0x6d535d24a3f2cc9fbadb4b72ee32ad22a94534a58f30a989a69ba232f5df4d85", + "0x8ec96b9ea62b178659aa460fc3fbc95c806c09a66cbffd22fe8319955dbc07ee", + "0x6b7df6b6e0ae580aa57cd386e12d73375d00326e88311c87045068d07bbc9e0c", + "0x4be095d5d026db7520e7284023b41ed8597e607966a45fb04776077ae7eee3ac", + "0x334d67241f5b642198bab7236a0ee23fcb97cf25d6bb58c23101b06d53433061", + "0x9bf9e00f063b45721be68c0075ae6af744c15f425d7d6985748384dbf4b7b3cc", + "0x31a8f21182b2e841d7b679b3cb2d5fd95ee7f24918e089f8279309663b8a8719", + "0x902dcdb4b62273b30a7182d0465039c55536b85018e53a3fd375f044ab9bb967", + "0xff7a2e8cac601f475e283a7f19102a9ca002fda7eda26087378a968638ac0a0a", + "0xb549c6b631a40e7b3ab2830b9317d40f5fce16f8083dc3e4904b0b44a6154463", + "0x483992375dd0bcbd4250b94341c193b41d5a324ab0da89549519745589cce49a", + "0xfa3f45914e4d05c2ae5c74803cd5cbb58762554b00ec0bc791c1214ffeeb1a12", + "0x9d65372f527601c4e4c84c9c15641641ec6b4cf76397f51541c504ca84d9e16f", + "0xf4c2e420b4db502c075275001d0332398632abebb67451828d524c115500e034", + "0x9f000a16d224f7d1f7977950ab122d8c79a0ecdb2854e9035462f0bc96568767", + "0x474b71b031b423aaa0b8e31b1ca7baa76d0d0824647f3cff3dcfaf17b6d6aacd", + "0x25e52a1e1de9843592aaa9fde82ab320755e97c726ce9abe3ebdda03a9518c41", + "0xff4df303d8242e9d40403077b2343b48a1445462e2e47df6ddf1a35fbef6d11d", + "0x0053a8998676236a29f8430c30adbfdb24e6d19548b1bf969d5c4300fad15326", + "0xb8f90839831f28e0448d2b971a8cb371e380cea5bf59476ca8522dbfb9a29c2e", + "0xdd4bbaaf715a82d9cf84b909dbe7f38d18e99738b0f6cda1494429569fdb1e55", + "0x9085baf2d45318ad02bded9b15bc5c2723bb47723814688a912041835ea861eb", + "0x60599e2277e67e0403a0c29757ce9a087233c0af3c057b89dc7f095b0949c36f", + "0x7927aada69b5c043b312fa368334c52fbcacba6cbb341ef71bce958ab88a8af9", + "0xa64b2fe53eab4039d4946b9ef2a0374a84eec6f27c926dfa96522dbf0f623cc5", + "0x3e8e32fe00e768c954ed9db8dd396c02fdf8f20cc0885ee722615a58268f97df", + "0x379d425c7040c5322514e6a717d7f62716eb0b2444b099def41f640bd0358697", + "0xaa20582c83f459df4a0ebc3337fffd1ab4b2913f9b72c5ed67dea4e7a442f4f9", + "0xa4d580a11dedec68463bf1c4f4c3ab58586b893ff192a612d28aacb112c67599", + "0x1c85a38ee244daa8955fd9a1d3a24a8f73401ae73803763aa5d619a5c0208824", + "0xa081da672b2a9c52db30e24445854864e6cb7ccda86b593758708e488d218edc", + "0x5efd10a925fe5619faac2e560577efbffd5eed3be238f827bf6874c4ee706467", + "0xb0ebacfa845a76a83ec040e27b7fd453f01a85079e1d8d5ab7b577b0ec9f39a2", + "0xec3e7f9eafa98f583e0c644b343eb4630c65b7ef5f8a9959f4c3c04d96a97378", + "0x13ef40344ef56a6c849aba6a87952ddc4bfc1085dc4367c43dc884359bd19efe", + "0xc419f40bf5b3493707ed85f22a59c5f82fae64b2e53d43211ed1574320645ef5", + "0x2c486c4fe7c6a2832ce5dcfa91dfb5756d95d8ab3ea1a2b67fb708ca759a2f37", + "0x27f78ae30c4a621e961b2e6a3eee226e3bfd246d7dfa20a86397f847b38ccbcb", + "0xd40a01c4307ae307d6ba7762e7e511a8f73575bf160e74bf64a889eeea552b50", + "0xe8e41a61fc9baaec4e156c0c0d1b451278b91e79bfc3ad9187baad6c11498c41", + "0xb0a0bc02521e7a1a20070c9ac5a0acd5b01a226ffa68728526a713d4d6c22e4f", + "0xb97f1630e92ff260b2917f09bff375c0eece95f955058f03e05937d7c5925319", + "0x042815728a7324b4674c4b05041ccf0820911ab88911f3a27819a71bf75b0485", + "0x10b16aab8bdbee8b79bf0b4f064256ac5be0ec6475daa9bd811a4268af1a9da2", + "0x599aab021ba66f47e24dad9c035aa262c52bd6d08408b9f97fab578442aa4b8b", + "0x0cbd27ac5f565c1b3ff6742cc4130fdd684226e6b5634485fdebebd6b9ad6d1d", + "0xf78dc12e0be562dc27edfbe7a048aaacf0a8e87be0acc98180a90b54167cd7d4", + "0x142dc51281239dc2913c43b795051bb41a20c19d0730cd6a911f61b93ec67db3", + "0x42f522f4fb8e536ec51d230850208fb3f545b1edd2527aad8629db58f4f7c28f", + "0x5bd26f8c24bb24b586b8b54ae9d31b00922dbdc394814855a1dfc695e21c2d21", + "0xbb594924ce980af6ef6fa48e432e732d9c3adff5c2b4d80b1ec57266cae4f1e5", + "0xb27635040f0612031d80031832551c500f179b5028c814aea258f88eaf724ef9", + "0xe20f9d09c20cf3768137006cebe1e3a9a4b984a4a523ba547f76317acebf6d1a", + "0x3eb6ee220481232a897578d357b76299bf8473c2d9dc7a1df0f05e72be23653e", + "0x347184e9af453ab3e85f1d2ae5f3b617f26fc3c6073e196023c5b0dc564f478d", + "0xbbebf2a82888ece1cd1b5b04f807919ade09d426683f929d41eb3f631f302e31", + "0x1dcc739be5ddaa08143b14219ab9b053a7b5aab2c6423a4dfda15e7c36e5c221", + "0x71cd7164d93d156bf768f479ebfe8319841624b345cf47c38f6c0e687379531f", + "0x61202fe95a3ccce9a89a9012f91c0a2e17e2353cf3ee4c203c98a79048752cff", + "0xdadaa1260d9606dcb58bfd73a0af3c6afe419e182f56007a96be0c87a0bcffb4", + "0x3ca8b19cb660e7073d717d93f13b0b0d027f40e4943c11581b9dec917baab895", + "0x61a00f1b29c1747f31c9a440932a693040ac0e3b6e88dde38cd390cef83b02f0", + "0xff604bb3b6bee6f48a9423062c023345a1d0204d072a9c8e1ba76ac9c085728b", + "0xfa2e1c080e95a405330ed8129fa2a2d3fe0787769b7eaa1226b72b039a78b0a7", + "0xab2359656c99037b8cc343a09deb2e56aa3e34020948dbe9e055ab728ddec569", + "0xf09caf6978ff5d8a2aecc28fee8158e469145e795c8eda2928c516a6f58532eb", + "0xc71e599522fcee7c3cc734ab1fce96fa59fd0e143c453f43ab0a885bc5ad3418", + "0x7cc32a311d8baad26dd1b4b1442139dc1894c4196018e517fb3c995fb18c1352", + "0x43251db181736dd552911d1a55e0e9a29a3c7e11aba13de391964b23083824f1", + "0xe73d105e1863c9c7e5bfdc6185209b880423f5fc8ca6e03cd422eebc4905c695", + "0xce2ca9c7d75d49e63742eea3b292f2509fc21b69ecf59c5f193243145050b909", + "0x5824a74a4475dc3c267287ae1a345d282ae30293a5d10e1e0ff8372434d0871d", + "0xa7ac2dd30139745042bc26de3b2b9d9a5c69fe9c93958cb6cb63089af41a0a72", + "0x8948787f8f6f9913b2953a0d68927b230ffb12dc056c874925f189e264ea4e6e", + "0x55da62475dc7a21d223ef8da212215fb56ed2cd8f000e0884f551b053a3b79a9", + "0x38c397bdb6348d3b431e8a89bfa460f15176715ceb7941a5333a570d5197018b", + "0xc952c6c12d46ea326257160e3486ad26b141568d8a86b10ee05b9d13bf1cc6bb", + "0x711966ffd54e0ee768eab776336e4142459d7a3a2db967f57ee9cdf6bfc7236d", + "0x482827c84140111179912ab9af16518908e47e2dce105697edd5482806f8ae5f", + "0x9b4169253c2d99fae8d10d0a816daa13fb952071965014876674aaf91730c203", + "0x943f7dc725442ba657ed9031e51099dfde62f373ff1617235faa0a958a0b39ee", + "0x3be4e635b2fd921327c421dfbe5f9acc9eb130fb46d6322665de6feaff5d9d0d", + "0x17ee87918eee08c7c827d29ae9261cf680ec126bd74b4320673c9e32c9d34033", + "0xcc104a00a0045aee5ba3a375949ffe51447e020d88a9104a45632cb2506efbae", + "0xa95fb8a0158c166ffcb273ac0cda13d620456759e41afa50981c34365de50be6", + "0xe5d2ef4099c050a6dac85344af8be2955881124616dc3e8157323132b08f94c9", + "0x83ea4ba70991baa3bb5664a247bc96763a180256a38042f4e903544c139c359e", + "0xf60becae380ec54712e7f1bc65af2d2e7a275d5fa890ab0c11391401bdcc3319", + "0x3886c2b65034f71b77f8941fcc7a0f9fed452432bbdc7adddce7ecad8d9e28b0", + "0xcf724611a390e60005de38de49fe59d674951f5aed28640e1717788b280fa016", + "0x615898a552734573aad541a03f2f2a3aa7686837a246c123adb3180a8a463b4b", + "0x744ebed67dbed8fb8495ab138012e347d513f67c0ce3f770225780cb4ba4cec8", + "0x002b869e2ed4f77c51f3fdaee3ebb761d4235a0b2aedea73a94060dd70fb49cf", + "0x14e755e0b79a24791639529825ec47c10c47238042f55be957c333b8d95028e0", + "0xec6cb0d6fcf6d4e02c2e60f315e942a200e588229133bec558f7f0ac8b0e276e", + "0x490b4503db825c69c68ae88aef895e7ec2a136096a5614bf23352c9b6261f6bc", + "0x3eeceb53fd7959136908b8fe02a150eee65787b6650a9fc8a53f89faed52889a", + "0x23454b4e34c37623be37c220f3e9a1815de0c259d0de44eaacb445233db856d1", + "0x430f4af497c48cec059c11c1071134d7c62b9c8e58cdcbaeb7177a92575d611d", + "0xad75c3ebc5006944c2dd37e1924ee3e4696c5a6f60b94489db2c3fdbde51e228", + "0x2c48c05c13e071fb7e4d80cdb0e468f5996cbdfbde3737926f713835fb3a0877", + "0x954f8faf7bb9bfb13f8518f6e833d6ace7803579d0ab3e7ebdecb333890b017e", + "0xbdc1bb5e50ed05982496b4350d9de5b96477fec06d685ced34129ea475686ba9", + "0x8997ee92992e7f7d581761ecec07b2488d65b7b321ebcd80962b084383210413", + "0xbe00ff185ce524963372a7e04c7f626b093645f25ebeaaed6ad91e5759b42496", + "0x134d488019cb843c7c6dc08df7deed6cb1603695ef71dcf819a0c7e5b6296aa7", + "0x3277b2d5c51e649492a5ad798aea26348eea9dac0ea0fedeb05961427a36c3b3", + "0x12cb231b19733798622abf0f9e3233032a27bb8407c5648a7d1eeedcb31d2438", + "0xcb3fdeb813f4665157c3e1d9ac631e6de79b5b3f6678332ed17e6f8057ed8df2", + "0x4cfc924ff9f0a18e2ab6ef292bd7685486b4b8e16ff0a457f3fd6684419b589e", + "0x09e00afc565baea2c181da066856c038dad8f2eafbb83723fcb91fefa5a36725", + "0x486eb5fa4cf06bb1a3b38fa87c57fc2090a266fd7f2381c586d2b2cbdef863cf", + "0xcc113de52746582283632a4479118579ea1add769d148db2b425541fe98d7159", + "0xa77da96cf049b174ac60f60885157ed677501d9a7d62f9e5bf9172ec165ebe14", + "0xaf7ab591021f101989882780c41b66e1ef1b868f0ab01eb52c1fb4a97c9f895b", + "0xedd7f50692f5a0306746007dbaab12e61a49218a57add36662273dc9b2075504", + "0x44316d822eaac785795b702d9f24b19043b0d099a1e5e74fb9d9030585f04e18", + "0x8859a0546e147c47737919cd035e839ae7008b0e9edb2639d72c8033895a79bd", + "0x7a73a5cf288fddb62ab014443116556a77737e5fa5dd63491d944bdea3d5d544", + "0xef4d2006f02a2580ebe63b1d9b59a29f53296496d60187088a386d7f329e815b", + "0x6cb05271987df63a504c9d21ff71548327f76407f181f7f627c195771a6b242d", + "0xa2529554d685001008f07719c91e8398177b70a8a54431e26b94b8830e2b91b7", + "0xd2b90611aade9b6b08f45a8955b35adff9e2cf7ce5c2953f7e39a8eb2e6c57df", + "0xbb6c836e7718f5b9fd4811bcdb76ec5e10fbfe6210bda7caac31c6fc450b10f3", + "0xf007fa425f41acae595ba628ee66f19e7c5d026f5582380be001fc1d17b1ee69", + "0xef4bda8f13a84be0ea9ff6fe63673380821abce1aa2f15e2633b3b7b8701c782", + "0x6505fa8093fbae125b519f0491a3b82ba68175b30219322ef48e132a69165f26", + "0xe97627730d34cca88ce405469107db8d9bef2427cb319aa46ddf7079ccfbf0cd", + "0x53dd1e1ae739a6aac4126f35e637c52f8b63783cbe801b37ae28fefdf33ea7a9", + "0x3a4d59349a6c96cecc9e73b2d5f197e06973873fcbff52511f7df7c18de71b5b", + "0xf1783ba643a81faf52068013060ef257dc850471cfb95c84c1e3b6d3ca4c38fd", + "0xe6a25ca47dd00d8eb1fa0339fedaba63664f5da47c98d2fb47dc5f685f7269fe", + "0x9929c0a16f7f778d42ac30bf6f834918bf8a5a9f78f6b1b0db4a0efc530ebc24", + "0xc28fd995dbdae38ea1dc32851e2918c50c67e1e8fbc21953e178ec04d40daeb1", + "0xce10488be588bd7368fce19f2171c1889e977f7e3530cad5e37e6fa94cf5f44a", + "0xe74abde7a9a6ec706f8a15396160e0dbcccb875a2db665a9d23a509fa0cdf8a1", + "0x7298d59d4282d5dc7db3adfa47df76a61ce7dc4e1a81d1e121f7f5e3b5bda30c", + "0x18ef4f8ff802484127d35bf9ad23dd93fc6e61c21e8f44f2ede3943f95dfb270", + "0x20726f3434ee7911ca690d39eec3df346b38451471a33ac96f074b804da0c2a5", + "0x3295aadf6d09ff6f4b88d912de9f45b139d9c45fd41970de65aeb466ee17e0c3", + "0xe0f0b1bb5657c70f2d3874ea375f44d0a88743c44e5b9f935eb0d82259c09121", + "0xce5974c803660f910c914ac8051f534365b7c11ab9504a70c6dd90fb0639bab7", + "0x1627a145177438f351b0962835c336ad769a85ada69a913088ad5392a724281b", + "0x3e42ac03d57497d608020f40a92afd9e2d427ceff2600ede30c5998d54297676", + "0x5ae456dd0d6775c0165127f756cc55644a62acb4d18e74f7b8846a4b37249068", + "0x917252330533c4530326aec7879598f74939ad831fdee92a37b8f7a1e4d6712e", + "0x8884b02258005e4a7961b3d6790c8c89468f1ae008645e7bc50f350b24b3a738", + "0x2e741b1d795505e19b0c2e746c5873594c24b84d4aa453857c419bde78a1ad2f", + "0x36152b48911d569b40c4f8047e493c3aa77079511078dcfa603bed9f8a8834b7", + "0xcc1618fce4f7a63017591ce93f51d6dea8dd0b895378321ecba66f5f6ca5252f", + "0x842e071ecd37f88b47543d214ebccfa0fc5ec5c9c6b834b77fec56c8b2e79586", + "0xd3a2e6bfebd0db698668e1d8b1792012e770d50edec0b46d91d44ed942dd9b91", + "0xa4e06d0635cd312bd9755fc6624f7df4af34ae7751deb3166b0b4f9287d64187", + "0xc7a72814581209a5c90605ccc36f1bcddce513fee9afd611b7dc5780bab7e5e9", + "0xb179899885cc4260d83a7cfbee0f5cbe58113bded1971f6077c798fca589cb57", + "0x3042c66407957bdff091443ecc804db99cbe6846566cd1fc805ef158aab30e04", + "0xe0ef234b9a9ced71deb6f3ceeef3c2770debab2ee6fdf0f3dcfc978225530d64", + "0x526fdc7e769c942e597a94e8b6f75c801f07b432d898268b5724b32fae1ef893", + "0xc009b6644445b52f173de1d57381c86219666fa6268472c104273500d85bd552", + "0x6aabbd2443129bfeabc3a837d98b3fd128daf9c6979946a10454e8471de5e7b9", + "0x9f2bae6cf3c8ab81a74e9b93c1d83f8bdd79170be317abc76315b0c099d102aa", + "0x66dbff12d3d9a27becb843ae9982737b2cbf64ee4c4eac1d8c26a86fae9668c9", + "0x4c331f9b3b71a800df2944b35e545f8616abf78b4c8a7e8bbfcc860625bb28a6", + "0x83c1525fc110e2e5fce4d13b5ceec47034c9da395e0a0df6be5479ec88e3e9ab", + "0x7ffcd7ce3ad00c833bb152a36746603585787959fd40910e399ba49e7d73331f", + "0x41f83c29018950a3348ee02a2889b80e152c193e314da349c830644bddd21cd7", + "0x9c93b25f75b2921a3e68432b134f6c3e25327769488fdf9bccdb75443049fcf0", + "0xf96068397a3a44eef95e998ef24a839fbe96e9b4675ddb60c06cae52fe76b5bc", + "0xebf520429a373639febe505a80292d79ef30c8de51f6d43fc67f1b32fca78950", + "0x28ee7bba70d838fb5f29e8cd717cdc429e79776e0f21200291a00aba338146a5", + "0xe586a183573b531cc07246cd3fabf3535b7a56803d943338c0750d85b152a277", + "0xe8400392cbfd688b59356762e67d476b5de25b921e4b14a341f964c10492347f", + "0xbc450311f0133f2c0028176519b66943c48c2ea08976ddebc42648a082b42ddf", + "0x99f5d3edf35d6cc5a91774e53f90eba2d17b6b83c5f118aec6cad0f10084b5a7", + "0xca4f2f9a794b6874e702e4b4b131fa837262924757ab9a0135ea538c95ef8d53", + "0x1390448c0626e31e12b66f46fa1eae14a38380638709fb7f89bece84c82b45f4", + "0x45be931692e2279ae58e75ee684d1d339e11ef5c0d140d7602213d76f362c687", + "0xf1905024f35fd682d6e2185b4c0aedd499f930eb631fbafc437fa8c041d9b795", + "0xf4a790691d70f93c4e2051d2f8d61057e2108aed93fa8b2a5d37f2bdcfb290fb", + "0x6fdb3f1a4e6b77da284f90da9bb2ff636aed78fa9ed9ab8a9c7aaec302402b3e", + "0xd22f33433e37a2574f7bd51b254b285241906e86482459090c4220f998b006ee", + "0xab4158e5be983d53faa9cebb8db1987688a7ef9cb839e47bf6a602ef62caced5", + "0xb53918492194aa3bd61a4fc9b630f2d2b91730ec9d351a0e494e1ad7c31af0c6", + "0x7351bca2f099e41fe58579f6c4f92545adc6fe1a95c3f6231f7932a1b96d6393", + "0x183ec6e203a36958ec7c68e1c006ddb05a8e7400f9efb92b87eee7759c0e0ccc", + "0x5eab8c96a8cfd19e1bac19cf47505009d26d8f3820c596ea22de45c1a418b9ba", + "0xa91dd523250a9c3df2a4838329b6413b7a4cd7b4160bf3e26e2b6b74df02dec8", + "0x5e7160d8d82a066802b624e7a68bf4a047f116c5720ac074cc46451776d9b2c7", + "0x5005c00c88850e14e31b590a2bbaf62523204b048fbf0b59a1bc6e65dce269d9", + "0x832c008572db50a7ccf292d1fe4ff15d70e8166762ab7d47d193cabbfa07ee3e", + "0xd52018b9580e9c421a759ee026f5e032b18181535718ce0b108610d6a612d4a6", + "0xc42a6cdec91421735acf928a1895a676dff7936f8bb7dada1c1603d53521f0de", + "0x86b9c955196946ec00be23aaba3ab609e60d8e8cb654136f6418f032df59c398", + "0xe079f97a6b9f485e364a47101d5c8273bee57852c304200a7720f325c37cfdd5", + "0xfb5b7ba629014f257108a95059afbb480dc7ba0c7d5a5f7cd610114d5f9f1ccd", + "0xc3fafc6e31ee12df5b911748dc1f1e041b89e3de48189e2af81f530f24d5f3a5", + "0xafcaad34b6f4f749cb0ade82153f6b7e58be344cb2da3f673a91b72cc9cf4296", + "0xb1c96dfeab49b704b6e33b9f6cbfd7d59b5f4e2a2ed8ca7148016bfb2b32a10b", + "0x576b0f66f7c7cd931e862aff5b5d62518e1061383996c149049a023178374281", + "0x5363337cc24187ee29e5e15652a35a013d63fd5bb7f6e9213f5d25ccd16c9d59", + "0xbbe1af7fb4fd965c97cccde25eafee51546828bc686455487e7dfef2fa8017ea", + "0x61ce89f321608798b6545bf597d18ffdebcc69afce7703e5aa7e2abd19d901a9", + "0xdfde11cf53f423565ff31d3f46b26c6eff466c3ae521d376746d4869e72ca2a3", + "0xca84b7fb3d4b878cb8fe151fc9d3d5aa55336ce2bbeb7cc7869ca41ee92320f0", + "0x1902f9eaca8aed3834ba0e51d66a80dfa408869367427aac0411994b8e6eee55", + "0xdefdf24e770dbe4ac004be42eb5569548a6f126cefbd504d579a2c8060e1f68b", + "0x1370dd1a14a4ebadada44ce7b3d16c1f7d12c320437123b79024a150af7ed13a", + "0x4b7b342ef176a31129308102f6923556ee17adf947e3b12c7c3aebe41947625e", + "0x1deb1c0cbb4c7bb379a97cf49a64260412474d9c7338e441a99d0869247624a4", + "0x21bc9e5d94527026aecd0859cfff89a3273550d4c26ae1167f9dc9df2f56d619", + "0x7a109cd22faec4c1f4a290af9f5d928c4ffb6a47a8b6b66b1e6897ee7cb6e73a", + "0xbfbf83e87f3f493ec6da4fd6239232a1a37f49d2dc7c65bbe19a487c9fab6b3d", + "0xad27acc503a3e79363d05e5198107621c740a78b1191636bae3470e375474428", + "0xe86a14c7178d9a07f9715fe371474a8d9f112d71433cee59cb63c92c3ed4aca9", + "0x04e6a8e0d0fe2bbeacdd9f6cf28298b595a762b9ed1f22f78bd8cd5108e29b76", + "0x6ca47f97aa48863d30e15fad917ea3f2c210ad75fb08a8bd0017b72f8b9f583f", + "0x7ce31beb8fd0cf10db6937e69720b0a55f0e15418e6b759ccfd3791e28b759c8", + "0x0a6b2fe3a9399f49b10656e17c2adb52c03ccc149731eecdf4be4b9ac1139403", + "0x833e63c2c983207f3e6f28b437fea0404eed339de65e7d4e083dc4fe1a912bed", + "0xd7d8e91c610ff99cca08ca0d0969e137c42d75c8b215ef860d57143961c4048c", + "0x855cb33d15f62600d9e583ddb73a0c1e5e41432be2d69e2f9f14366b74056cc8", + "0x1b46ba11b97fcc3575d8690e178cee3d5d853371391d7cbbf61c4190ed944c71", + "0x4f0302944f9c337289720070dc085eb5e5e444b8119ee6b701cb88126c6bc8cb", + "0x9f072d5ff1b2b6a14a8ce9ede86d000fc9400efb1af3d6dff3d5abb6023537b7", + "0x95c961dc5199880dc26e5d51649669139e004d5c7d807ba401a0f1e53032e65e", + "0x4fb82877af4539bc115fdf26747f0d6c5055dfd7ddda232d6fd0f8dcde370bad", + "0x82d847f3b16cb1084a5e734ec6da038099610388cf23cbdefa1d751af1271ad0", + "0x4df0ed25a022b7fdfd081c1d7e69d7d0941be15d39785aeb3941dd49e65b730c", + "0xd996b206b83a665e791a580a2927dae659dc61ef9b62da1835602bbd8544b0bf", + "0x1966817edb3273a496c3016eb9389b7626d73a8dd9e1d05b214d13b4671b6824", + "0xebcf6a4c69265a19981d532213800b31e34a4fb097f722507f735a4dea341dda", + "0xe5c687a07447b08f2f5879862e76034499c2f862cbe190a246c2f5ff388aed84", + "0xa4f5bde0a579183837d1b1c64d276aca4af47caab1e8f4852a5d641d1669193a", + "0xf75a82de99fa75103ccfc029847469450c61a3cd3309a3186f03db9bbf8c03fd", + "0x096cbbd86028513ac3fb7d76db599fae604795e073686404d24015aeef221a49", + "0x1029af9d8183bec66d048f2feb3270fa47fdc543bb5c38bbfd333492bd339f5e", + "0xc6b9449830cdd8abc5199d114dc724d9ba0a61a8c12736ce2ba374c63f5cd2c7", + "0xac2a540641fffd49e521f34389e519d1561fede4c2ca01ef48682eae968e9510", + "0xfbd0486151b7c2142e586e71523d4c4d337d066757254e0867f2c92ee2c26d13", + "0xe49b8ff34f0a4a11b7266560caae85211667e37b9c82e21ecf8e1620d3628b23", + "0x5a3f21670c7ffc9b933b6bdf519c891883c7d3e5b25d2236b64bdbe8b8ec419e", + "0xcbaaebe858b1d969cacee2f800b9e3290a3a1e3be21b0f40526042b960e6be2b", + "0x8e9fcc763aea0cda7782abbcd8e2fa4dfb7031b95ee792a55a522adae28f79c7", + "0x5ee1721cd14c3b5e77f740280782ceac4aefcc82436fa0f66e5d3c9af29555f2", + "0xcf70dd1fa84c1c27b4b4da72c6bbe5cd6178b5f93a42bc61d84a81d71a98f915", + "0x4511c88d8dda49453dca28e38c2d6aeffbadb84bc6d47f569fad0b1cd3eea1ae", + "0x76876c7c2bd50447245315fa7b11d60614a5bc7163c8b1b920b45fc541c24b72", + "0x64655e8401091f9af02023284d983c2287fc3f6c379dce8ff4b4df82b797d5b0", + "0xdc797c41b759454c5a813a08beb0cab390f63599e4513a0f5c2410014e668ea1", + "0x73da94472bf30ba530fb871287cf6bdc6c0518800e4e80e8a417c93678d831e4", + "0x31488723ff7dabb919620cd7f9bbe115c2961fa98dd440e4ebe358fe8dbffd7c", + "0x0aac50b399b22b10e6e6aae8e7d2e083b32fac7158271fbb16e2c9f1b1273b9a", + "0x123b884640e7202b00d8b78af61b5287d655def08a23e80c8727fe81cdef4dd9", + "0x7bb334d41d882fee172c353a98636ee033ac1f9c614b52cfa0213338a42a106b", + "0x621d6848e68fb6a1c275aaa649e67e6eb4b803ca29fc5dc7a403b51e3ef0b0e5", + "0xba8c8d93169fe324cd3b9870ca5772732da3a5c198bca49e732af47023b7d479", + "0xba8c8d93169fe324cd3b9870ca5772732da3a5c198bca49e732af47023b7d479", + "0x1f02c8133ef5b1ed3cc15588f59fa4a5d8e1947cbcf6185a75e68c72b79ecf19", + "0x51528e0ac72f7eaaae5fe142bc2ac74baf5faebc6b1a1b3562a6275a6859fed9", + "0x2367bcbbccfeb25dd71713119d078d5d0de7b301b2a4a2174f1210d9cdd5d7f0", + "0x75301b18568572c0ec4be40c1ca0d56dadfda7aa940adea80096b352b6d2b607", + "0xe8a5b22f914e7330c5255d7263aa563a6ad196b871d28aad71030b6a62e56013", + "0x6c6172bd5cc28cd41f81b8fc5408f1f4252bb9c62f9ed9025e63c910b2b07a91", + "0xfc62b6bcb8fb247f18dcf3ea5004d99308b29fc229debc682175ecf0d2ad8a7a", + "0xec3e5540019afb1c8e970d3486df0821266ae198688a46b65b02f15d218ee116", + "0x159368645ff862d88852422a1045415486f7e9f78278bda4d10af6f751116f49", + "0xdbd18914b7ef8caf959913218a900db8a2e47c097b5903dcefd9c07cfc4f7082", + "0xce9d2b3d81a40b4571f9965f9fe112ab32bdbd317bd3433cc40bf2e08af33020", + "0xb7025009272c1ff98a3060408e29e8a3b1a67d6d3b26beb423cc292c844cfdad", + "0x43237b356a8914b0f0f838e7c6d46409fab141906e4db249abbec168ef96f65d", + "0x664c785cd27570b3f68666f57aaeea3c87293e7924cef3320c17165944bee4be", + "0xee370259a92e08207b5740af584a9c977d6d98e54d52168e9cd35e816594d208", + "0x1413a091d60a7f0e038ab93810998b570846fff7081aea74066e5bfbce65ec59", + "0x5fdd040d8c4db7af75bbb3d234a3c5e42e1a38281e2e8325c82efc9720e8a06a", + "0x343d0b745178857194c24f1a39d875cf5babd10d10e4388bd587072d52156a03", + "0x9b9e72248c938f72ffac5c0e328a542ee89fa281e3af96caacf31e8a53713ab1", + "0x5ac4c727b7973159441409907229c854abe08c3ac82ef5fdbff1bfc6c4345228", + "0x879ac254bc7fcad5fb4811b5aa385b7e148c59e92df793db0f7f34ac85924e44", + "0x584ca6c6e092ba317ee468410790b8786f848a4d4b38e9f0284d83cc575c41fe", + "0xd915a867150e7fd83f7c5b9054cf227a1969658f4251ab36fa4d31ec7dc22759", + "0x675e1d3d90002aaf22f6f03235aab3d3fc2dd62737a02ab2d71a89f20c9488e1", + "0x873d6b759c320ae09dbbe1dedbd12cf3d7f4b8e8c3805ee9e2de6f4c7ee637af", + "0x862a14f365375e729aba03bf5e4b22612d2356a81b773366d92926c7ea0538ae", + "0xabe8828925f7ee1cc711c3a682a021df081243dbd99f6af7c34d25149fec25d9", + "0x80231fd986ba23f25fae1c8ccbdf5b75784617826548925def936d960c04df01", + "0xe57a850ea09de61ffc9282c4c941c2c412a220a042339d64e1e1822b052b13ce", + "0x2fb4384045cc44906f75ef8470f7db49799a5aa4604c77c7b6a1740779830d69", + "0x966621e27be39888141a6a8115d4595d8795c3ee9f50edbe5bc6d96a3efe9b61", + "0xdf0a88d40ff4362e7c8e8a3bf6fe41a09b86604db63dcb124df16300a5095a03", + "0x0a16d982c1df8d8d2d8ce4b0d542321e091e972f2a3271fa50a83e7eae8994b2", + "0xcbfcf3362e21d4d41b0f88846214903efe938fc69245def6b5c3cf51f7af6172", + "0x064617d212b6f043c6d3abbc157581645ce5144a08e561b082044109b4b480f3", + "0x75645b00d77b9da2a0f4bc37fbea2ed69995b1261c93fb0565e6ccab9e9567a5", + "0xef616dcd5f6d2fe5e6c3b32eb3caeada8b4de5211ea2a7e277e731b751ded4f6", + "0x486e89428218c769378f47dad0262ddbfe1217557c94065d0b61c744fa38fdd9", + "0x89b97eeda64ef07ce7006c5772e14c22889897ca8b4f36ffd27da7ff7973fc53", + "0x4f22c45a8aa80cbb97b2a3e3f7b3bfbe430f39b3b477f0bb0b3284c240d08b5e", + "0xd3e50b43a16cfa4f7018b8caab11036c098d0b7320d9c6cbb695ff97ca8d9abf", + "0xb5c7875bfc40f3b37f405a0fa2fc83ad632ad08e32e2adfea1e0f95e9819b644", + "0x5def22eb8e2735d48204638da9bf0b61e655c8e2729c6b1bc4e73c0cd2e8347f", + "0x6f862a0ce4670f7ef1c747b74794b769c2d5aba4c6095c91dcadc4942189efe3", + "0xa5da725abc8ac0a4b8c1ae243f6a17a08893bf30b417b8d359960236977339d4", + "0xfd7fa8adace9a3c97985fef99d0c168844af151dc75444accbba699efc719cac", + "0x7227692844801c679c53a3bcf84fbf2865ac0595cd01a1f6b4d27a4069aa6873", + "0x60911dbf0b814c8a0fe9c17bbda303aa93714bf2298849b3d67ce3a14c430125", + "0x5c46fd166ef0249542302af3414b55ba1d4441d454c14e61a9c9c425cc966852", + "0xbaa7f626f02134f74d6e23cb4521d48455f881782f5cec48ebb137633c39c901", + "0x16e0baa48f2ead2bb27418ff3f49bc1f8bca39b8abe89271d0d679c570ef1328", + "0xf96744556565ad1b255a92ca39243b9e6a467439c23b93d195e3cb3ef2517719", + "0x6066ee6cab3a48411a00067eed0bf328aa199f0eec40a7047c165e53fe56a1d9", + "0xdb23b49b4c133b891f817d90b487ab31e603464aa3d0a6d5942099f589c75445", + "0x542dcd9fff424d873504c0dd3c1340bf344560af40416b05fa9e2abec443c2a3", + "0xb541422d5358ddde77fa35f8d7091edc9d96d7548548632ec74fcf9d02eaac1f", + "0x586b1145a0aa17fc07b99833dfc2059bc604d128e7d0cc127244319df3dea2cc", + "0x6cb041f818db14fb6ac80490a4731de89e8670ec12b68152f4729cb88f507f2d", + "0xe3a440093f167a58cb30d2fb5962f6b655021f5d75b3ff3ff621357261214328", + "0xe3a440093f167a58cb30d2fb5962f6b655021f5d75b3ff3ff621357261214328", + "0xddbb0c6d6caec6907430c918dcbf619a184372a9272d44525bdb354a5d620c37", + "0x3c67556f8863a797230dfb255f4d77459cdb95d4f53dd1ede2088af49b6ab61c", + "0x59f0f478c14089452641554e8e7d2568d0f50649c177cef999b991b2b3b1013e", + "0x92570c7fa7397108386504a76737ca89f94bf9192dfcc631c02f0439f5f5c063", + "0x967c7d71dd6d528fc5c67bf1d37d4c3c0597a216fd5135f8d921498c190547ac", + "0xef5b7944c418d422d171e31341ef028d74cb72c6d6ff886f4e63481ff1fc593c", + "0xafa4488344fe778a33fc899fbbc16dc84a0537939146878654a91807525693c6", + "0x9dc484670f7c4f8101e29e799249f6f6e36cb8281b66e8140d213d755bab1d5f", + "0x36d49a0776943ce38fe0b9b353179ec1d8208797142942b35b0383491629ffcc", + "0x55b4167fd93bb81eddd1329891e2ee989a7fb72667b83e141c0fcf3f524bc5d5", + "0x71a14abfa06a8aa60db692ddca9f3a8549db3f02b5b5ac5cb6ec75a81b0ed441", + "0x2aeed17616e6e66b798b50be95b2c1d20ce065aa5dfd8c873c88027099c68b3e", + "0x37bb0ba9d3b004b64e5bcdddb401788e4a7bd81672ba4f5a7d6f08decf412e04", + "0xd0f0cd06bf0801c529542f55f49bee710b94d1b41a6edb3bd304f14ceb1325df", + "0x4dad5b13b598544da10b7fb1581bf233305cf245f9f197bb0d3e0ebf0ff33415", + "0x164166a01d270f9be9aa3640ef84bfa892f4694c73cdd2727ab1a96639aef891", + "0x42930f2c5100e90e0e52952d8f146e28e481665904e9a78f5996048dc5caab71", + "0xf85e0310915c405c6743e483141cfeb07effb00e0b0bf9d4a748da37fd20bf60", + "0x7fcdd8db5a4e56b4ce094345f7fce898ef7d08bc4a89c0fad881c68b44acce6e", + "0x9f3e00f6803818fe1bfd176278e9c6ed6ab8117c537a46656bb01c80702de57a", + "0xd3b45187e8d93e7ce8e62993a010ce87dba5f89e0f0f9d82403d222c19b3b3ca", + "0x17a668817e69ae48833fa9b685c759a981fb68798f8b9c1ea0f171af671dc51c", + "0x4996d4086d815966bcef29581fd08dc482652ab9e6a4787bcbba6b9668231531", + "0x1815829a3740b52046f60238d1c090555bf85c40dff0887cf0d4e542e5e44dbf", + "0x12fbd86f0491e3fe88add30751745f1ec005e64b803eb29df7ca065a5a5f3cc5", + "0x2f14006b1a69443d466f549bf1408949cda63e6ac6746b1937d8ab6772dd8341", + "0xd65c5bed6f9b0dd7a85e10e36b2752338e1bac2ac41c58de41cb40ce1eb1f077", + "0xf494390150e252dce7d4bce7fa0935647b80d3cb81e7c070bcb2c9e24c79d77a", + "0xa0f1068b7913303e1d718671498e2e7e69ca787d7d9fb7c4489458f6a6083dd2", + "0x56f49acace2dabb257b7b052e208a5c34bd7c3cdc8ce416c3ebc8f68e4058f87", + "0xb861bf0c2fbb64a099cce8e4c0dec43f19e80eec845842fa3889b319b8622a24", + "0x342bcaebed9b2e013de5f2f0deb332633f415a5ce79f702308901664bd9b99a3", + "0xff5efe502694d05c5f80ea9229767766e291da013895e813652bc39d0d94ddb3", + "0x22e718977239663f6d8cc096652e6825aed096e212362c66b96f445ba72584be", + "0xcb358b757f1e84b3066b5c193116b88b4c043c01f390079ecdfc6fe226106d2c", + "0xde68aeea91025e704a120e818ded570d6ce0e607d439562e7f5c02d191b268a6", + "0xf23bcba6198f485eb4903d0b1bf6a9f39321c46f233f1f657f3025d683fc56b6", + "0xff2f668371d0440e1066a493fad4502d3d4ca89fc466d2d978ca5881c93212d5", + "0xebda475650e1762412c0a755482cdcbefac4e92cb2f1b002e12184371a5c7fd9", + "0x4edaf52a56ab0b3130d887f5004fbd1adc42a2c3fabeeb10981770dee66a25f8", + "0x34469b7c6144fca0d144c8eaf77743cd2f0368d45071ff4eb7df936bde6d2db2", + "0x44330bf0fd8ace9a2cdb974003eb33cb032262e3fe74e9a36e512c6d7162bd04", + "0xc1337a40b9368c94af92c01674ef91622a4195cfd71d0ed77ec3f014c5a0c42b", + "0x019aeaf9856489788fd880b28f56170e59008ea4ad6579e4cf3e11bb7cfcf590", + "0xb1847fddf4835982169a18349af6a56657ac0a01c3535be557f74cb1e17f6753", + "0x58e30c282ff02610fc7237e241411b418e1a1f831ed739d852905fdb2896fa25", + "0xd057023fb2a375214c1c95aa8be24ee9db60f6b0dd907193c61752c20fc09712", + "0x9a13bdf7bf9dada7788473e4d9c0a01487f24e037e451cffa564cbde81a2662c", + "0xf2adb9120d972bfb9e08e99567418ed59ddb3d9b74d2798e9487888f3fa62d7d", + "0x7721918d18a70333a91eda2fc4569958f5159da85bc60216744230ac668128c4", + "0xaf91ea6620abf9f6d9f01736f373b9791cdbffe1eee6e6445823a7ad3302836a", + "0xad4ea12d65df8a194d233896a5f3e33e0d78eab182bcbd2dab5dcefd196927d0", + "0xb849408b99b187a76c395ebaedff712e373536f84f899c795b9e31f15794cf3b", + "0x20c41080f2b2b9b13587a00524552c51aa86d3cd6893c74e8db9738b37021fc0", + "0xa5b810622c5e5daae3142969c3ea85830126d95759c7b62b78734532f815f958", + "0xcc78c4e1a22bd18d82ea3a469343fc76464b22e9cffaa923bfd5056b522f0dbe", + "0x8733db8284efce2d25f668639336415068a4168fdcd9f2bbbe269cfacb3ed04d", + "0xa3d065b0301857e044192536199d2c0758cf420e55741b4fd9d274d7aa095bc1", + "0x78eb19e02d7cb4858b395e71beafadd66c8ca4338b8f308b9fec0d0da09e2d5e", + "0x77a0433b4b28a27a1c0d90e4cba8576da10c5d145a060dd1bf276ca0023f87a9", + "0x0472e43c1a9647d044d4ace812363976162f46a5c459d2c6b52b69bc10e00085", + "0x039446755e228decc503fa706d62f2968f8bb1d8d09dc617bc6f1b9c2c6820d2", + "0x27261f289b869d5cd6ab302036f64981d20bb038cb1b423240be99ea8b914cba", + "0xeda4c23d488c99b596a6b0d1442e342b27e97f0494bb3d349294d0ef82361f2a", + "0xf4d08c3e392bd5f3852a79910846fd9f95c2c12a48f04e88cbcc1f86452f1d1e", + "0xfafd17c4e772fe4f61c3af40ac117b1b7815afd95b909ec9bb0daa3a4411daf7", + "0xb8f965af69b52b15edf5cd2766835b48348d4880c4b139f5f11127b1cafe37ea", + "0x1c5262bd6bc16a35254908cd88ef7ec8d60efb43aafc4b47464787da6947e111", + "0x0d78dc2f7c2c565ad9787acb5450ec41430043f75ee2f5759ed38c48f735417a", + "0x604787b652a3671994624e81d814b253423da8d8e8f39813debfc37ff02ed0f2", + "0xa3736ae495927ed52700af40877846b6418a3fcb53bdb8d37ef49e1dd6c998ee", + "0x97fab18497901f9a04ab1a683ed8efa62dfd9c8db2eb175d3e86922b39185759", + "0x023bda1e082352743e4148cc1ab27eb436332a9d8484fa33cb590ff85a02b448", + "0xc71b4be4d58b8f1485dbbbff3a46c33941cb6a4ea8c712d378caa2fcde0cefe5", + "0x607c87e8c71e70e00cfa3438043c3b247e3e27cbbae9ad681a2c72c3dd94c689", + "0xc03464f31a94dd145d7d7bb9b102d6060ceddec9b789618b814af7db43a4e30b", + "0x7d7422321aed83879df86345134d9e7e5029be2ca98ccb418c11965977acc6c5", + "0x08def3014bd62324366fbdd7448e05718a31a1bcd542f064c9cfd492363a10f0", + "0x3842f7c756164fac230d3d62f899365491d7116c1025322a02081af54947a4ec", + "0xa5f3569a9b58a441ee8d59341d96a0f9c369692b4ee4d01ec34ae454a8e3a18f", + "0x48deeb634961a4551a633d73b25f22cf7914ac8e4dc68798d6f275aaad72e817", + "0xfbe04fefeb3ff73881f24756c08ff231c8b2a387ddf26717bfd5a3f7e869ebab", + "0x908ea62ea27a76de3d60757547244d9b082bbb27b1f0dd6b4746dacf44973ba4", + "0xb84403c672d8365848cb62d46953dcd7a6d7dce5f724cf1747c2644ec16784b9", + "0x079a0a877f0f5c02344dd210c85cc2d3ee1fc028d1611c1c65be4f838c027281", + "0xe5440140ee34e1b53853e671d903571abb2b21e52e11833085cba056adc49c7a", + "0xc9d418a6975f7e977b1b6a11ba68fc322410bb9e96b99bd4c51ba32962ba7a86", + "0xfda43d33d73a717b7f1e70bb74bdeef89510eebdfe3addb09f1e1fd1b647ea03", + "0xc51a13129ed96571e476ed729c7d71b9a3ef1656e1dcc752e5b64a9add122c44", + "0x61834ad9058f0224465a7c4297ae504ba41808e1ecadfdbbbd4828bba2a54ed8", + "0x85c3e81e54e8f37eb4edc5b352858eb6ae88e95738893e96741be6af85eae228", + "0xa38a5a2b4871016dae0896135f169bfe9fb1e3629726fa2344d9b2f8a036e21c", + "0x9f7e4c29254eec53a041bbeea4f16372b6cd30c7e8ad62043e9ec9d057c1e0ef", + "0x29bfa3b5bd8d81f8cb37029eb4f9836da005801d280764779f4920f24a1a024f", + "0xbf57475afecf9803c6893da204120c530ac7ea33db485fa734207aba46b4d575", + "0x66ef707f492fa5004f6c0c91457514a2b9c12da9dad1e305b475602a7def0548", + "0xa3849539c7440ca61e4f7c8b17ec9154e87d20ce8e46c39b793229932087138d", + "0xc943dca4c07f42c19363edb8afa3f6cf5bec6220062a7d2bc48dd1bd54b5155d", + "0xd218e864cc40d686f14c9d64255787b9e8b23880ea24709e57a063ae04f0dc5a", + "0x7c77780f0abb60d08805dafaae17154cb8d6ba734110bd78f5394905d5ae219d", + "0x1b1eb86e0820234ecb35b443695c118c0420e46524e393801ced0e7424ed1ce0", + "0x13ea259b98a76737a6f163f3fd2cb597ae5f52a01729fb37a2dcf2d03bbbfa8e", + "0x4d3547c76782fcad1a087f216c566a0b2e6993ed71a193356a8a06635b348f7f", + "0x826d9ed7de76f788d768bf62dae069bb56b82fe7e47092a917a6d0ee6a8d4fda", + "0x89a64fcb826e928359a2343e79a2d90c47a2f6cb734d051518188da38dd1843f", + "0x12d6ada341a0a139ed7f174504de3904dac851d93477f93f2857280575c7c7a7", + "0x5f2521215261e2d98915136a01799c61b01911fc709d8f39493bd71a557f691b", + "0x03f034d979ce51f4a154edecaf6d61acb1fadd2520cebcc90465f5a8d4bd851a", + "0x25784a349ce67cb2bc87aab1286f8e8706aa4edec929f385b63ba467215f0e1c", + "0xd3e7dd5df724c6c8febd3a9f2302916e395fae2f5d13e455f24784ce2830d26e", + "0x34bdd3e9f39dcb3ac72fd5945710bdf96138a721f361cf920b436bc9dea43123", + "0x4962165e9cb80d9daf292ba577ba510517e17d9dac807d4abf1e4a78d0fb8d19", + "0x8ff975447bfe1fa514963a9478b628b052d4d9d7040a5afca8eb3cbaed876040", + "0xb327428a37f4fec160889a918105cbd78a0f82f95861955c68bbc58c8d605cb0", + "0xdc15dad167a8698557688e6b0535785a6d205cf6840ba549bf6d381711c17cc6", + "0x9189ee7f4b5e3bcbb3e74051bdaa0677bdd4c8d19e71e0c305cc15f939e72e4c", + "0x9cfc1c62605a0288b32fc774948ee17b9428dbfd607f4b1e27c54e3e17be196c", + "0x389ca048360704d33a0b1fc16aa2c0bd9c59567708dc8410906054f51a120a76", + "0x61d63141bb99a1cb797af3417915dc6dcf5715dae5b8bf273f26eab69d9126af", + "0x9e0eedb49417e3e4c8b8ff667fbe64b1af71f89725039d3716fd57c9f61dd914", + "0x2ade3116a6d75e7da380f2902eeafa2807069596304c37515108382ae511d487", + "0x1db406c309a0f1c1425e21bf49c1c22e63f207d1dfc4d2bb5a215c45905ec64a", + "0xf5278877b086741752531f7f31f9feaedbc9d32989ae63b42e754cddd61acd52", + "0xbd7dc89bcc84a0cc8a5f3beff8c83f95238f73e07c21a525cb3e57c21810bc0b", + "0x41f810cf951bd28b854e0fc9e46e45c92332a1bcdf8a811151ff02c3898964de", + "0x98adbddb7bc936b7a154b8ff65d3962ef6df82521e3202b37bd0906274bd5051", + "0x2d0f288c3c0388603929d5b8a9baedb33874fde76bc660fafa4756c6cb2a81f1", + "0x4ebca0000cd784337016d865de023a085c4de39b29d7348cf0bd1c4c89e2eebc", + "0xd069889ee0c49cf5c1b36552715ca69c96a28c60983c2a6eaaf9d4771ec11fd8", + "0x041addafa53aeaf30a0e13c8b22b81da471516e4db47839af86925a78a6a8b44", + "0xe0a6629725de3c2a6ebad280e659c5db2e548d83c9d7162c92923926f2b214ed", + "0x93e7716e777d9deaa1f228833b055eb4953eb57b710da0b41a5249b35d4af3bd", + "0x36130b5e20f31e90e057929ef0b4c21bdb51757d02a514d6e989244d5accbd5a", + "0x547433d0a7bf7f27727892e567860f4b1ed4ba2bfbbfdab314fa629da32ce7f1", + "0xd6460f262cea28b924a9d9b2c97e2e302dc783739ba0780d06d5d55360830dc9", + "0xd5e7340c4a8a618bd8c5a4bd3264f46cf4a1eddf318c4677587fe1268a8aa853", + "0x5559010816235fddb4eee824d663bbd52103ef9be37df771ad25ba7da372c756", + "0x9bd242e96eba2bdfcf403c1e024997d15415ff6852f7e1ade186c1ea8e8cc1a8", + "0xca54ea61ac367ab501c03beabaf19f56d242f2c2c775ef0ca540c3f7a920b8d3", + "0x23c7be5155942edc57e3b2a890c4ca412dffca8f445a418dd25c66237012fe7c", + "0x26568928093aa94a5f31307518c3c4563d9a1076c1425167f7c25b304d69fa53", + "0xc0c064fc5904e20258eec62614519ebfe450fe4a25b363f5eb20a1194ee1c703", + "0xb5bd8b01098b07cac26925089cddd20f2a2e39f3f7d026a1ad9b73c7a32a5af2", + "0xf7aae883dfca9d0a062777c43f3488e981482e551734ea255f1d436a878a22b6", + "0xf7aae883dfca9d0a062777c43f3488e981482e551734ea255f1d436a878a22b6", + "0x6fe4543b0d65504bd21a33a5806e30ba3fafae8bef651e591473ecb8b3b04e06", + "0x666141d32c4e44375e9ad88b73d5012f9393aa585489d7b196c37ce7cf927c1c", + "0xb3e10ce5948369a2f110670b8c681a1c7841ae2baa1ac188fa3057b55cbfefea", + "0x41457c13e6ab2870d807233905bc0c90c23470abf35a5ec4d1e34d3f3af124f8", + "0x10f9b3a600ed1a0444b607a1caf94ecfa50567fff39cfbdefb98c76fdefe2fd9", + "0x419c7b14ade8527604fd43c7bc9f19b0bcc7cb05ff9472ba7f2889c614fec883", + "0x8ff2dc7b670519fe488024cf1ebdfedcf8423dc0e2376280fd9ce50e43bf7174", + "0x9b92f084bc0bce7a585ca6118946a06cc0fd5b895e49e74e6fdf4d672ff1d2c5", + "0xf7b3cc1ce68e36c529c59654ac3fabb190c205feff430c13ac9e0904b4860b17", + "0xc3e43ba050ddb43635ea214f859bf0940e80432d5da2e1d432be7d8e35440f59", + "0x1940d126103660c9396ae5be40bfe16f95c95e7eb2ffc6bc01fb217512ebfddc", + "0x9a72ca41a88db880ff13933b38814f0f4462e0bcedc348ec056a9c841bf45417", + "0xeb1b58371ea502a6c96b1876bae71c2d9f8075fa1ff6754bf439753550b8eca4", + "0xf3c9bc1c1a4d5a15558634ed55393b6de0515d7b0d7b03b01709548691245a04", + "0xb4db23363cc465b2e418a290b19e99ca234126cf193e7b68d56f902c53ca4b7a", + "0x897f0f906d6433d748f39f9a2c067cc1f2f1b065244b8e520822bca023d03302", + "0x07e2adc4077dd35eb52d1aefb30888f303570a57200c3fe3b96f34b18f9bd8d8", + "0xaefa5ebc6d6009f3922881560107eefcae818bf58e460cdb3f344c7656b9f4ed", + "0x1d5ecb2294b1f1bfc86a175a1089023ab6cad5ab7aabff314e5659b0c5fa4ccc", + "0xb8ce2e4a6eb7ab47409986351d3f3d89e292cc3cd3572bbd42f9a92df1a931de", + "0xe9b08a40697ddb79d1cf10a25a6c46ab29bdb68788decf2e40b6847d6bdf7fa0", + "0x4152994dad83480a61dcaf6b546864c337f1c90df5b2b15fd5ab4ccddfe98789", + "0xee69a0a0c919202bcc96c8561358db37ec11b9e524e7eaccb1d04fb83f693961", + "0x3e8dda0aef02f81a8024041591aeb2753f68b5709934929e5ba1686a8baf5f72", + "0xfab648d7bba4ba8a16531a858e37506b426f23d1d4649e243140871d87d89ec0", + "0xc4f852f82964b64cc75d9bfb8e63943cd7f8bb06c7f88569aff2927284728421", + "0xf25a5ad2e420239a08d2c16d0ada65bc7373ec381c5a94a8e3763a471a6625ca", + "0xd7caab724a85a1b9fd1e400f1d17fb0f5b1caf785cb7c756c6c5b55ab28d6726", + "0x39abb010b43ac09a86aa8042333c311ba1a5673761d7e67ec85f9a8944800119", + "0xcf595c5ac53213e985d201489355bf591cde85901cdc6ae644b8b21481c45838", + "0xc0624a2935db7b300040d09b72f18d5d704a6b8a9ca4511a77a0d033c5bfc6cc", + "0x9d5cdc6183f67dd5abc02100d4caa39317f35cc8a8ce551aac019e7d6677216c", + "0x856cb4132f41723293a2122f9d13263fb3b7219ec833b802d364e4ab4b41cd52", + "0xe64b479218062e0dc1f02500b2011e77e09f9d074f7fe0d496c0ee6792e0455b", + "0x2faff548fcf431ba8bb0d85c58472da6659aa57562c051ee02a335eed0a6da4d", + "0x1b0bf2f4a474314206cc86692fa4e8cffeb1b69d11b07bcf1719774ce95d3ac7", + "0xf41bec390481c763b9ba030355d279f2b5a2326b90d2abe7f81c4cd2d619374c", + "0xa98e6555593a7431abf2847b02809d3ec984c156538d1ae53c49565b4f38e1b1", + "0x31b9180b56f436e27f8cf9af921abeda1617b087fcce3f539e1663b0bf407175", + "0x9fbf75d97fab2b3dcb6f71a71818450f57506f0eca396799ec2fdb172da4a59e", + "0x07a8b83baa8bf8677e2a4096157044ee0979192f90598b3f28be7bdc03fbf14c", + "0x1a8b0ad617d3bb0da68f993b93b93bfd9750cb314c0da1898aa0de3b2daa30fd", + "0x934877022ea3e5f7c3fcac4e11baf28c3252132e07de51e041b73ddbd32308eb", + "0x8c64f255b648504882839e089f47c7201b5456aef4b82e0ec18e9a8aa7cece80", + "0x317e5ef865da2b25b18a34307c34be8fb7c58bcaaf6f1b9f9e5a4dc2509a471a", + "0xd863fa093d24154139338686a7fc1c87e65028661e404373b1fa3cb88204962a", + "0x02281bdb40b903024c3882e42729433e6fdcb65475d2e5e62db66c5516d2667c", + "0x5b91cd556a20a4fcd10df50383ba774361af00edba7bc9026d0c31c117e784b4", + "0xc028dc0951f20009d34395d7ed6c709fa68b5be4d2903b8897ea0f14b81b1125", + "0x28e3614b7dd337a6eb16fe362eb11e743428ba853288d43d30f6562ec6c3a6ef", + "0xd4be6956e8b3daf148cfed2e90ec61df4aaa8de3f3fd26c31375c2f943e2ceda", + "0xb22a008bf2b182a0a32150be78d02a7d8d964afe7ff4f7b69383058caaeffa7d", + "0xad9d535cdf11b4d68fd86abb173d73b49d7622f60778eee69f3cc9de2933d287", + "0x6c3dd9b317f482dfba83b8a1e6cd583dddbbcadbf86d5be068915bab66486770", + "0xed5d646c9901f5e0d45f908ec88d05dfb783b406fcbee5564b18164547ad4119", + "0x9fbec7db083b578ffafc8ca041175e6772e1d23a01bb5ec2da00db2130046966", + "0xe4b0669bb33557ec996be2e128d23c9aacb2a156c871555c636ba55e266d402e", + "0x19b38c4f1754ea1c15c09ee8f7164065bab8b4fa050bc8d8d624931a6281eb4b", + "0x6f4c947b8d011a679f53b804be5bf971becb318322dc71eb5734909b19cab111", + "0xb681c929be00aa3af1830e3b9f2283b947181591c086b5156681efeed4736f5b", + "0x8215d8286754470826a44160d5398f6b0f0fd4b0df6fcb1f5f6033af91b4a99a", + "0xd535392750312fe6ac6577834e4a26a8a38f91653c859072d01c3b8870149eae", + "0x98c5d61c6f2b877b298c248c948875398249422313f413737136e93661217301", + "0x56d6b97c97f9edf097499940c1b11325058bad6ad7dc6ae408f009566fd2d769", + "0x23408a355e8f7daf02a281eb228321fd9f00d1d307548f4a3fef404cfbac4181", + "0x951801929db99e2c17b15f3117b4b8c51f6ace17576b86aed05cab20d4d42cad", + "0x4ba4d0ae72af387b5eff62a1e7bec1bd3ddd974f07b5a5a2118c194276811c3d", + "0x2415226c1bc4e5972cf628a311c2b387537936842e21eeeb4b3e692341fb1ba5", + "0xc2962551061d4575f091aacba7be792999d374502a56a82369dee27e18789bbe", + "0x6ba2d854bf02db2313212e5aa9c6b063ce7d1afc9965bd3f13c3f5aff5ef0c1b", + "0x694572557733f340df6a5b0e65ff809dd74e6e0bc63d1da5a9a8ab577038c12b", + "0xab4979175a2a0a0d1cb5ca05fb4dbe921ec811ceb458c43fc2143f2b7178dbcb", + "0xa8a062e5c85560314c5032b5e77d721db115dc4ca8be0c4ca4918d9b65c9473a", + "0xf82bc55565b82d0ba28080e0b8b34b65fffa5aa2f5959d77d6b8f127ebe8e788", + "0xaae78451014052a6389d6daed33ba16214b5211024b64a3da4941544b5955dd6", + "0x6e65d296f5d2cdf134837d283d3e3f6f654e961445b11096bfe12ab25fa442e5", + "0x82b6d1a70fc6efa423d39354f46c5fd7a0d90eff44370821c8c0e78a29eebd89", + "0x9183324db359fe0f0754354ae17c6e15121e9fbf1068a10120e8189e02b353eb", + "0x1baa0c0d4c81614fdc58691a634d181f888441fdf028b8e6b2066dc1e2bf4a45", + "0xda645a580b05282a036a1d27a9324a556f50316262083893cc462a3cff96475a", + "0xc26f2e72b6e5e1abc8f16ec28bb34db24db01ded1b43aac7d5900da17b39307a", + "0x55f6f119a92cb88f5d7813a0f0d9b10d97848090db02eca8a5199004949297f3", + "0x90773f898d4e58d7dc027422ad69e389ae07ad84ea9b8994e2118d4fc4acbb11", + "0xaea6c99352577c5c92eddb431494d5d777f25f5e3a27df9393ba34b95a567a9d", + "0xc5df281fba234111735e36a584d8989ced92a1dbd39e83f957423263f1a35b6b", + "0x27cc3ca1a5c647c3c28eab319252f6bf65254ba586311ce40a1f0316c378ef5e", + "0x361e85705f5df7072479bcdbb6c0a3f9ad617423faf8d5f7b9b19b1a9711618a", + "0x9d787003cb72e148ed4fe218e983a1764ab7f39e7b4aacaa780155839d90136f", + "0x3174f3877c35e533ed9da48d27e0e6fd547f002ad49145adc7f38394c23f5bd8", + "0x56bf93d98b4c87bef2d7c1ef50ee47456c058b5a69503564809611cf0a0de28b", + "0x614131fdefae70b38c26be10794886a4e3d407ae7330ec42169c6fb6bbf201ce", + "0x52c00007ffeb4682f63ba1c672984257814efa318b9cb02056cc94f00dcc8705", + "0x7e43bda981da820f817131eb51c09da798e23c64d132c0fead29e7ecc9cd2e27", + "0xde817e3bb4cc7ab4fff7382477947c1f1d2ea9a776275eb78e25fa16033a63eb", + "0x67d3633eda08a532734ad7c1b689879b2961458291bde12b05f6a506a7748395", + "0x493eced6d71728c5c56c6d001b8e88eb30819e6307f97cb051847f7a38261b99", + "0x6be304d81e5a2aae963eea27f82c754611a13271c393bf6fbdddb08f97380c42", + "0xf8a0b3c2a2b934959a3debf41ec96718f3f651bc4b1246a1360701144016ccb3", + "0xdf9e38ffad3d39a82b3b55d9d4bb916ba52a1ceb30eff59e794c300358e4084f", + "0xc25a321a8e725cdff7a51f8699b4ca35bb5e00331b3bc3c10eab8f44cfd7f059", + "0x36a5ea954e38241999288c1d56255136712b11fff84cd36ad2408041ef3722a3", + "0x7262c5c4dcaa1680d033992b121286527b505b78c081dc5b37e77dfc76a6b8a7", + "0x678de80277d52871dfbd3039e93a5dfa02dd5b76b5828af3b52c7e190f107de7", + "0x2e62454c34f9c645415455bb939622b7ff588728e8948b0f85c9d4f1296797a9", + "0xb7ddd7af19f6c016d8cde8643952575b4df599ddceec01f690548d9ecad9be4a", + "0xd02077e1940a868f4c19745b6caa66d39f9d7e3471963ec383e50956df1a4212", + "0x2553c77a1ddc67733fb5b965fb0eee7e6ec112cf4cfd3236927d1f1f9c74d0d6", + "0x4368c5e7f223ce87b235509f6b39acf0e82727a6a55728d87ae7e70402412087", + "0x40ec3168d97b5febc82e7fd2a8cd210b2f31c8ca5e8c5dd2818866a01a6b9c0e", + "0x47aad579837e34fd7892ee13f7345c9e5da049d5dfe238b9e066df0f6029c3b0", + "0x9feb72b24aa4d11c7b68a183244b1aa4b03697bc7e4d657aa4dd7f38bb96c228", + "0x355855d612ddefd74485f3c8147648cced113722e4aae5dc9cb9c2aa01a57327", + "0x7a8371eb1252816fa7b8a14d4fe9e8330480771148eaec606481b4d5b81a2f84", + "0xb05c90b1246eaafec6d20d2498d70c44838f0336dec656bf64be5aa175387371", + "0xd051ddfb3271009802cfaa1963a3d758177344fd6125078b40ba8b2ae509b62c", + "0xcce698229f6d0cf13d25687061076820d6e0f7f4ab4c4c1bdf4fb016f2aba181", + "0x1c3c03484b4bb565e64db298459b36d6da9920dce4ad52d79dbfe89ed0fa93f8", + "0xc93b4a791743f4e9ab93c9f9de74190b2d12ae700490fd6eab159173bec4e137", + "0xb4ff4c451bb3b8433f096c8274de96263669eee073fe56107dfed0b0f2c968bc", + "0x946b77f37c3fc55b32b83d86e99e6e6aeb8f58570755cdfbbd11a39a77d2d9ef", + "0xfc29a4a621d3ea9f919f59b0c7de2cd7fc92de71d14a4e8f556d84f7460059e1", + "0xa5ef49e46f82aaf7bc3628c1301bff069388b8b988dd799eb2d76e18d1b8bc8d", + "0xbd28bb566ac3997cfd95a3f14d2d9b89a13a64cb9d7c1a7599c2c50c76229fe4", + "0x6132faf7f02c5613cdab8e6810fb78f31bec83ed9a19f61e227c945ba6ab3ade", + "0x6a8de161bd40e8676568f7e4a0d18d93957f660a517cc429b32ae01af9fa2f6f", + "0x2bc0edc73b4aeae58b04500258f5771994d2dbdcef83383bc4120bdb4371fd4d", + "0x36aa61a323f1c58d6d0af5d37974c8a47523e12d3618caebfdb937cdee8814c1", + "0xe790adb84dc3853d5145790ee25c97bdf720c5cfb52725e0c974ab02428b0b1c", + "0xc3a2b3a290d4099d6acac4a8907e9651edc25795b4054ae77b21ff843702974f", + "0x69c6bf5ee910a2228be10afecd8a3175179f8927bfa85db2b554f884244ba528", + "0x719f88deb26db7e4f8131cabb81e77b9cbd4c5969cdc0be8a3a26fd122cc2abd", + "0x49615ea008f0eff9ac2e7ba1608b2dd0d4f57f25849ee7ddb7b1b83138b25abe", + "0xf8218935c88d0cec0defc088e104e8d894b9d7f71e44cc9931f30e5eabfd6e47", + "0xe9c45e12348e972357692bf0b3a309211e943714150e26058ffcd2c654ddfd19", + "0x1296895bfd80b20f62493be6c7798f8a7efd2f263604d5de6094dcbaa288e073", + "0xe4e686025edd17a9ce4b97e94cf301dab45f63cdb889785567699c55cebc79d1", + "0xf2b8113b4a73d3bb57cb4a520c3fe769d202c57159d2c66b5d0c3cbd98bbe019", + "0xa4c93d0a73a284af18f41638c1f13e7908efa42b8d82aacb270326cb6827f6d6", + "0x0271dfa3fdb588a3ac4a5a1c7e9911df7680747e24686cc7a441c63f4fc10670", + "0x45457d5794936c2cbb99f279ff651d8bca95ba51850fda5dba9435783e727c99", + "0x5692b7f693162b49a1dda5d376542393f47e40cd565844201acdcd25f9f46e0a", + "0xf46100cf901902310e4314aa9d0561dd934b999f52012959d4d23e73c558a212", + "0xb3c8f0d8f0944cdbb3155c41b2f09a87506584aad2d7ba53808aad6c3c332c39", + "0x6b5458736b8273e6924d193c627aea49ce721d008de9c47e306eccb1f43cef4f", + "0x2260adcd33cdd2b049978d9dfe97e67c3a351e3dd884d2b667d2d97b648b6d58", + "0xf2f5c1243b8daa69ec6d40365e31fb49eef2ddb02a10d323e2639af25b6490ee", + "0xa2f0b7914962ea65a18a3219c6708de7bf098f4ac886e0a33e10d38f23591439", + "0x4d3f217672fa23f83da5a193455504dd5b364d6ba24f20bce602c0b16a2a7081", + "0x6bc08e214698b50e7dd26bb1b71c07d6304da9e2935ee611362a34d0c9bb62b0", + "0x67d258964546f90d9044d5bc494be34b08e2d733ec9b6efe5e1bef0b59cf42cc", + "0x8104480112a64f64efbbd1a92be7e7abd029cb1f735c4e40432315f01f456fb5", + "0x4c44d42301fb6bcbf2c576578568c31326ab6896dcbd1fe3298b50ce9ec6eb38", + "0x1672ecc9d6fa2ddd2f28d88ff6db5626986eddbe45f4339ee8567d61b840f5e7", + "0xc48c8792327b3bb03be4ace417500d3d8035c4f44e54078b01a7b75b4803e515", + "0xd10038c21035a05cad0556b094c4c3ea1a243a507acf9ac02879d2ed90df39bf", + "0x4adf1ce96a8b645ffaa8fe3e6826206fc216326988a1a33f5b54f41d413cc1cc", + "0xb81e236093849539c1faf60b481259cd631aed290b357cc2f19855dfd4017827", + "0x86761c8efe2b99231e743b92c39c55884cd5241e4250adecec534a6f2eee3a27", + "0x1a2247b5197570f2ecb9c2b05aa146938cd4b7205810db7c4419fad4b7df0e27", + "0xc8e737c726beaaef0ca5a203562d1e97ed9e4d7f9430b57e9bc0a66d398fd1e1", + "0xe4e28406906b5813a710ff8a8524a122d3f06ec14d94951eed5485436cab3067", + "0xe91020c1c95f20f512d8cef411baf5b8b1457a1fd89e2c8f6c6d43b3f16fa44b", + "0xc388ccf991416eace6be1418cf7ceab3b6dac45e34661ec2f5fc00cd7e0c8e47", + "0xd5f28d30178e621f4b33440fb5d5fd6c110ec419cd9bef7e6f375e64bc09a920", + "0x8905b01fb8ca93000272118057dbcc609a060c4fea68484a047930ca666ad637", + "0x0def79612693c005c2c7fcd4bed83f9b5ce6ce4f9af90586539fb2d3d251c9d0", + "0x98e72858930639ea7ec652a8b3207d254c9e64240c2455429b70e2fb81f2503d", + "0x1bd54f916873d34fb2054e18b252ccae67ae094a006aabde1531cd5d44bcc31e", + "0xf339eda8a9f64f19cd3e3ab4e66f5c80581fa7e95e07d8bce749be8953390210", + "0x7b47242065d6997fa5cf8c6ce7c6f1ea73846af4d95d8f920d5de13e9bc8ae27", + "0x32132850ea7604de967fb0f8b1c6588fd117b64fa0d1789743e4fab7fb86b1c7", + "0x82025ba7ad2b581f645676c454ead3b874e20f1ebee00e082ac2f841055c4ac8", + "0x2659b28d80842a3bd2683dd6f99d5f2ff9cb48592add05521d1c57d91f889498", + "0x3e3fdaa280b10388bb88ad4f15051b32ef9ce3108bcb700d74f9becc51eb2588", + "0xdd68b543c9f8b6275e7bfa0180eff4bb814f10ace4a137cd87eff7212f927a1f", + "0xc0d98f9b07f9c861c3376b9b3336240aafaa75a8f679dd98deeda9cd8d50c979", + "0xa0be953766a665c8f37f63997a8af6c3bf584dda9d10dea778b8bf041a57df25", + "0x0f232d06c940af8778c3f6faf55980d1df0bf1cab612aa7d37852c328a697be0", + "0xb6a791b1457d82eb05a3a06f444625f03c6c0276e9cb80d7e860c93869936426", + "0xdf3536f7214355aa8995601f17eaafbbd28f4217b9cb53a9b092ecdf4129256a", + "0x3f476da6b48569e6a8d9d9e75f1d0f4789720842fa3fa44283d745162f2103fe", + "0x3f476da6b48569e6a8d9d9e75f1d0f4789720842fa3fa44283d745162f2103fe", + "0x3b8b35a62d328abe08dcdcb032a9bf2d948881f3f58044b4c72fcfbe739b5d8c", + "0xdac29c42688d15936e13d0d6a61c2e40ed91ee95a194dd21728d46df825037c5", + "0x08813e5e70f985633e85c88ed08ba1fb10f95b9c72ac8030b12c44fcc0c17a31", + "0x046dab361cf38e1240ccc5fe1c4232da2bea6235c4050d472d5a9c5e9f4eb6ff", + "0xa81d6e5aa1d0846beec161c823eff58271fef93ed3cc24b1b41ffd71f950f075", + "0x00220be1e2cfcf5ed29460b02e4918e8839088aa007a6466ec9f4ec177970356", + "0x2c0ef6234b030a3039f790caca8882c2be95884190a5c49ebe478b4cbc9df94b", + "0x3fe9f14b0c67fc9c7ec9194396c2fccd1a2a7f509516db708907c6ee642d30e6", + "0x5be8fd55be2815ba23b96438a9436de3ac084becb86fc465ad0a8288a5af7ba4", + "0x0a382c89eb3bf7506e6fb5d6487c83a51040eda975860d178de503593c121538", + "0x4361abead5cc6ef58c39177798b774aa0ae8500a91d16faf2e481974366cde8b", + "0x788a7fcc95e9b970d422c41f70177bb2b319c1abdc20996559fcc1a7d849d8b5", + "0x56c2fc53882b023349f58ed043fe47d44bbc655cd6f370132e748446a9dc0f02", + "0x2c8799368b959fce861b725cd9549763b78671315b4eed4622bd2193c24293e3", + "0xbe5da38deba6715e5683c7f2fa0b381148306e359a32f64191bee65bc8a2b758", + "0x2126b52108d183c72836fc5c3bd617b39353aceca24e8155632862c16b82c3b0", + "0xb2835209658bc51a8e315d99edd60260f73c94c8e361eaadc05e39c60afcd8a1", + "0xf5e8754d6268fa8810dc6e7879ae893030e92fbebf12ca986a99c78e70528d3e", + "0x4a8043f4e652433604b6df688d929a9b72ca8ab2d1ed7d7b95b1bf1d90e4237a", + "0x24f59e7df2ff0f6bb790e3866de8ba5caa4cb16a383d7ca48d98124c1ae38ebf", + "0xa580406e90996dd63d3823b4f33fa002975a852b9f9fd3de9eadfcd968ee9264", + "0xa580406e90996dd63d3823b4f33fa002975a852b9f9fd3de9eadfcd968ee9264", + "0x0a5685ba11e1c7cd20330abc7f5519540a230b1d33ba5babe37264e2de337545", + "0xe569b6c8833e0aa84224accb7e8b7a41b166d22aa9e16d169db2f6ff926cefca", + "0x9d2adf9aad9ffa50d950039775916315a59783905a10c8b5f3762d4bba9eed43", + "0xe3dd5b482d4dc86ae7689b4e48daf92b53d55c75d352d92a0e8d87faa4ec3492", + "0x5eb44981411819fc7d3e5d292a9c58b8c388a6c41da22ad09e5dd0bbc44fa150", + "0x4b817cb0edf4669238a069277a39ff663e5e6ab062d203b960cbddfbad9ded3d", + "0x1cacf0595ee8a1f933f40eb9d8d070c906c4283d7cb463c9d28dd9dd5cb050c8", + "0x8717ab4b2728405884f514eccb51b4bd8aba047b246becc68a0cef9b00317eb9", + "0x7e8821ad448e46f0fe585935a856993a8fc83f5818de761248e49ce8c3ad9db0", + "0x64a0e6347383529acc1150bd6edd6c10fdf16b66c147eee9b0c90f12ca103506", + "0x6b35c7308af98b405ec609a3b883b9f8a7f4e50a96b102f28fca851040a7ff73", + "0xc7d1e4d3d807727680bb914d456b156a1749a93ccdf7a87a46e10a267626f689", + "0x5b11f0148289e0a4fc5520b950db5366927cad955c8a2b7308c2110d120a947b", + "0x19f633f5e437334543dc431514ede8e2654b1dd39a9231e5d9f97f896b6c7673", + "0x8124d6a9afe782af59e602da9eeaa963f9d2999e2018e984a66e6925912c1796", + "0xc815d1d636128a16c052d89b0426a1fac05f28796399df5daec2893e514b584c", + "0xc1056db65bcbee2c02778e1c3b848b385ad807c08acacbf4c0968df9b5c207bb", + "0x2ccf373b4c83edcd07f14592eed141d8b0e4fc74d40dad7b55d6360173fb4625", + "0xf8a9e9021cbc1751d7f5577799f055baffba21bb0eb961c4151b66cdc7fbe97e", + "0x1b1c5d83f0a88eda5a0eb2eb57efbcaafe7872681e41b0d59b8e37c43beba017", + "0xf7f46fcba7212712eed16109baa6f3c571d8e2d42669767104ed5b57a5a50ab0", + "0x13fdf4bd9adc535582fc01d045a56c72cf2b1cc5e1194c6aa996db91c248600f", + "0x991c89659434bf2c2af67c458c2ad01ea53298b31b18875ebb1bf33416a97f6e", + "0xb9267789f9a0c3822d7b2cb4b093335b23e1cfc2d4b90882ea9170bf9b632410", + "0x2d79d9a8dd238d077f11cac90c2c2ad08d7216c0f972b29fbf0a9374fd474e4e", + "0x9530dbff50a91687037775b87bbb13dd3c1f13463235f521283c95e4d659d928", + "0xc6920dd3ca2c8dd831a3bfbc0271fe4e3cfefca29fb1cb770a9466e6a40c4da5", + "0x7be585da8d915d7944513e7fe1f9c66453bb8ad8d9f400c5e98eb19db337cb89", + "0x5b142a378baf5189ce6031bad232fc6e1be0a0dc4061500ebeb44f9e0297a0dc", + "0xc2a45f34bd00fe7def55b43505cd6dcb5f3ace2d69dc03a76d3548890c913971", + "0x3a5fa2a32be4364f191cedafee1de6ceaf7dc90f0d7246169069f964a1a6d49d", + "0xc82a3ecadfb115fb96e6feef9f60a1449cb4d77da9048dc20f779ea8bd4ef430", + "0x16a6d1bf2e85ac8cdbcab86ac49700d618b06bd5157a1d89554251216a637449", + "0xafc3dbad938cfca793db2b78375400cd3436f4b458a6fb4fdab88de2db598e5c", + "0x1ed0233e45aa49f8f60de85ad2bf223cf539f7409938fa9d1c0519e194547fe1", + "0xacb261a408be902dc771307c1d4286d17e6fd508dd96e3d179f9ff3b634d0f19", + "0xc9eaa59f82810d0d6b1163b9f22f1c0f83b5c06c2e5b99c0b0db694788d2b6c6", + "0xaba185bf917ad8d615cf65af332348c08f2be31e36df08adcbf8fd9c551762fb", + "0x39054a2197f9e32059dc51f6dfe42c81972ccc57669426807654189b4672fe51", + "0x887dc3c7682941122bfb347ac8f5c2a653b1963033e56ebc0df15c898b702a26", + "0x25b121d55b4a5b5ba6ab80f40637adef78dcc8bb521906971fc899fa683a09c7", + "0x5be7e112b28d43d7042a7110a1d4bd60f1e9f1ab3eab6b3c7446d4bf5a8b97c6", + "0x54984db77627f23ebff7b0b44371b997bbafb14d9c8c82dcc1042a1494ead25f", + "0x971775f4e24946d92fe9641cc9fe4a34ba38a85726ea163ef4f99ad5c266de62", + "0x70fbb680cbf71215c3afc8088629f6d7cc44c73adea9816e41a8a8d648cb3c61", + "0xbabd9599b46a403c787097945c96d3556954fb521d171b6ef262d727667498ee", + "0x0c217c6ad794c5358c8bf5abdf555aca47ea393c155824890ffc8482a7467c3a", + "0x4102f0f5cf49a3d8b32fd8b25e734a5a8596e7a5accd4d5653f28df46fc50279", + "0xf5451eef0f8bc38c661b0552aac9c91a7285fbd35bebaea08f6707c58c8f021e", + "0xdd6626638d3ec2043a5e00f3a0a3c2af8971ba6826baa51cfe13a640aa340e61", + "0x6a82d9a563725b4bdecff6ec79765f828fa45c2620ca650c6828f5af1c14c414", + "0xa66cf5ae2a81519f998bc2b4621c1c44883916c898c7144161bb49610770f935", + "0xc9d1ab1f0840fd1ab792e70eb71e2c0ad0d1aa46cf763ec5272a18d30c93a6df", + "0xca53338534c3aca1712f3fe2f39b334fa253fc2845a279216f439b74ff054c97", + "0xd0b0b75de1e86d0c2a484802e928dd0d0e91e5a7b04c2fdd223bbc032c47a919", + "0xf75ddadc3e02e27097d94310e8d067f12bc6721338c702b01ca7766504579b70", + "0xe74431e0409ec845b953487078c73dcea3047f0e2853ee988495d2b681fce53c", + "0xe5b57dbcd14ad54f8c2d6b8b51d644a4f39d3cb743c49f5411b3df52bca5ba0d", + "0x75c3bd01815bdb9e09206b47c5dbb6dc815d9a9768cc49517dd8c9defdf75232", + "0xe553d78d2f1306b3dd309fb15ae0a1bb8d3d7d42980a85fc7d53f90d11625bad", + "0x7e6011c66ae01e6ae47fe46a8b01300956a45609af0136ce30a0114e5d95259a", + "0xd43ce6fef10deaabd7a009c891bea8e250a4ce9007a5ce12cb461673b03887f5", + "0xe52fee4fd99691f008d1d6f7efd989a5504c9bc87168a7cd618a5d529fa52bdf", + "0x3bd4bc924e5555ec8956df2c5e10e4c15b4c18021d961acc96e03976c9224a2c", + "0xe7f5a2c6e39554704de6035f360b2b7d4fc2a32d6747fa3be7904ece455c15ff", + "0xdb73968845e22a517356e549ef993163116b9a76883daaa48e5fc22d9e9d6dfd", + "0x35d73bbdb8d8545a2b39e55b20713289c2949ed05ba5d6c5a06670e15ac97e60", + "0x645c708fcf1ac4aa233fcbac58f1d2f5d3fc34f1df31c8881987b0918d4c15e0", + "0x8c647a983b4dbed68ed5ea758bbc28b0651dee5d903f59b37f0af9ec12398cac", + "0x48c4a74b4d20b1dc14a191d7931eef9ee941c9209ae65f8f83c468ff71b29823", + "0xa2a689f5cf928521f74e2aad1bbe2c7b9c08ac1facf55e9e8aaecd57d7c9e7b4", + "0x46c14e49dbe7bc349aa5ec2a1cd6876890917bee5b571365b90e6f612f9f0ef4", + "0x835b9694fce7da1c4c907004f53e1a771eb70683cf4066183b22b77608e5faa1", + "0x8b6556455fa828836bb3cf3bf0b0f0d4f0879a41d4ab06f02e7beae4cacbd75d", + "0x40fb2b51e1c2780d1f5d91cbae6ffd241f2f5402e0f493f8a4cad7bc1c73c48b", + "0x7f37ba7ac8a6cdcdbda8ec624f687d1e4deb850e148fc5a39e5ee830d329eddb", + "0x3cdf0454e0056038c972d0ca9f612e41bcfcc2893392bb1dfe42d8897c8c1607", + "0x1c373fac06d765ae8953cfa942af3ddf4d60a3dcb99444854e3715bd5f8abc5a", + "0x0b7dd9f5dc036723a0024367c7f3882db577dd1263a19f4cb1aa65e70e102aad", + "0xc7ee2f4e41291e24abceb19f5367a51da07ce6dd7c3a20292ec16dfaf07cb057", + "0x00335497683db6c7a921ab925e218de2846dd2a5866bcf7eda8b232c3f55721a", + "0x0aeeceb315bed4ab88327565879fa37974c0537720aab453b6f7dd28e3ffa5d6", + "0x2d95528b77ab87ff9c1c0928cdc22cf9bdc3fa77ddee3584f4c6ddb74608f8f4", + "0x51385892fe40d2d2f416b08807d0edb68fe92038676bca0f71c33208a332d42c", + "0xa58865e24a4ec8fdbce0c4f9a7b19eacb296027878fb7146efa9d02c9aeac140", + "0xcd2c8bf8fbe52d3caf991927ce4e4a960f01e5e793fcf25c102b287085837580", + "0xa4da211aefc1ef8b82c66f85c5b9da15f195ac6b82be959e8562c883417c4d49", + "0x9a3c7a9cf26aa3511164e4d7933b64fd165968331a3a6713d87c0cbbdf1230c8", + "0x2441f1abf72cf265dda6b94194df0aca4d410d8329cd7e6234b542c01883aa0a", + "0x10211b1ae531307c239f7e0c3870c911cf1712381816296a6cd4777c764e6963", + "0x2ab85a74512ef9ca8398aa585d12daf96a6220c0e7028c05942f704ab5958a3e", + "0x0631b5f78d151094e3dbb681a0e5db3a36a20f7c7a0279793141eafbdbd73a34", + "0x3315776740bbdf6531cd47eb88c9deaa92c49591b56e82bf1f109e5dec56f047", + "0x0ce24a00c752a27cb8ff3ff781e4da9531fdfedfd9f26af3b32137236d2fd746", + "0x545f0bbc7f73fea61f3aaf85823a34d73bfccaf64bd550b8286dda4b6e654012", + "0xabed4368d513082c7adedd9b7ea2e95c069b72e49fc98e74c91b65d793c1f85e", + "0x1a7179171e6c8a9c7e2fe434d2068528b1e21d7b8790573e238ed5af1bd96eb9", + "0x62975428b9d1bb5099695765d70655ffb3fdc2cc79a465dc41a59b911d58bdd0", + "0x62975428b9d1bb5099695765d70655ffb3fdc2cc79a465dc41a59b911d58bdd0", + "0x1cb4190f5a7ab713d66db2f4a1f884afbf6b7484f291abac809ad7044c0dfb1c", + "0xa94110c88dd4c84d309da94764d126fac296187a9d9f7873f670cbe5bb13e1f5", + "0x2f45af69e93f300e0c7f0aee912167643e7bf2100755b817f5dd269537653cbb", + "0x436056a9dc594af13ebcbf9306f26140eea0e2867d9cbf906ef18aa48a2ff6e9", + "0xabe270089ea04eda70cd7a9719a2dc4d830316d97ce7671ae6d9af5fb2661e69", + "0x1437aa3575e7519224b516aede626637495eabb78bb0773b60bb698a0068cfce", + "0x1e15a60b8cfbd13348355e53bff1b14973e6a3faa25ee4c1f19d44c69ebc92dd", + "0x627fae346b2c2dcbfe0bd094df9a5e2835b033addba76ca5d06933c0880e1b84", + "0xb7848151c502c011db436406d9bb4f44f6a66f6853d81011756ba63783d68139", + "0x33b37a3a888f117835e992ffb270234ec56550f4455e7122f3bf28733ac82f1a", + "0x62a6be45960ef7779f16150389d41a305d8f7ae9cfbf2f34222a69f4b4107342", + "0x36357d072133b2e9b20e72cfe0099dfd177395045f0e777d9c0c5953640b2240", + "0xe016ac3a70812a17192d78a1f41a7b4476d19825ba5e1eef44d013057bdaeae7", + "0x988bf0a8bec51abbeea09283697284215c6a8150e1cb849f0176d204a74795c4", + "0x5a0c283b4aed8ce8d74d8fe4aa85699b95c8480eeb44e2ed9609d7f18ea7ceca", + "0x663c3f7e912e5fd53b7d1df2c641e86bf14befbc24a6e11a737bb3f975d1ab3c", + "0xe08d75dc1758c43b5c470cd8b91688618314c7be95b62592a449a61f874604d8", + "0x24cbf48c8820a48ebe8f0e9b22e3dba01b324abfdf7137301575013d613fdc59", + "0x0328903995a73899b2e9b5f684e6b4c4d16a3b9daf9f8b41acf80d26500885b3", + "0x5d3cc1dfc9eb0b57887e5faad874c1d8029e791a6c9e214ac7c0a5063b99b7f0", + "0xe6fc2e822abc41aecea46b2ae45ced78b15b163356e89c52709c2d956b3c2e23", + "0x5f8d9bbe555c21f2bdc29aa7f5d9e255bccf5f31a52c27451bc879425f371a0a", + "0x9d7bf0ffb1e15efffcbe4fa70eb5e45b38ab06c52063ac3da1dc48acfc5a1570", + "0x9814ffcba895fc0c54cdad923c1a8e32c65d589afc2761fb970181bb18a5704a", + "0xd6856a5cbe39ff613f1285a68b0d7f136ec890c625e500814c2c2353b687a191", + "0xef92f44042ef6de5a26607fae17cc18cbf2b729f5e7a0f9da712affcb1fe394e", + "0xc26869a7b7296d7dc861880ed851cd2b0e29f00ad14cdd0942b83d902a63da10", + "0x135b56e26f922c7384789ce7799b743df07064c68712b9adf2fa991ed20061eb", + "0x81a233b31f52d45b682151dd79894bbc18ce0073a32f176bc4c5a741c6b8385b", + "0x9d48dc7c3ae4aae5a0e7637f1e7f5a09cf2775cce40030c3c65d51c1784ee9dd", + "0x75f42aeabce8d1761779d528fcfe397a8637fae4a61af59903769bab1ffdb20f", + "0x736817cd1c11afe3104c80eb2b87f7243714bd4a582406b3040ff780d888ad97", + "0xe5d8de5b0da35c5372132b6a34b9e7ff9019b79a0c46c07a28dd6b4d4a39dae5", + "0x3fe6f88d1270ebff598f94bc70ec26fbcea888f9e289a861cf2636d7afb8504e", + "0xdb48f2dc4b45edad086c48365d470323eb4f1e9202c7feccc440db9a017d478a", + "0xcbc54db98f177d5ad6a7a97ba17e7f42aeb4dcb8700f369d7d08c6ed24a13a01", + "0x10ea244b04d245be1a59d7f5537e5e835e3b2a6867761ccb304eb6ef2193201b", + "0x939811d1a7985ba1c357e35d45f439cdfa73e6deea69f43cb63eb4791a146d8f", + "0x5b6c952fe025e440461857bb927cbdbd8c67dd808b14f2ea32b9f2c1909d0ec9", + "0xdbfb567e3db7fc20f3000b2a098ab8bffb4669016ab6d81473d44b64d51faba2", + "0xbdc5802651c6ad4691daa20843d202c0722db46b2579159f1253ce0b54828dd7", + "0xbc72fc675c159ec196ce9324e6166342a6cdd172b1a7cd58dcd2fbacd1f4a543", + "0x5f72dbe45325820134da82a25713d0290aca25587097c51763d573b4aef559af", + "0xe6d11bec32f08d753d97d85c199dbca5e4101705b99de926186c8c47da7bf487", + "0x37d5dfa5f9487419f1654e214d6f870fee3193c15b3bb76fa87fce752da5c40b", + "0x13d73d4ede7c39fa3b5e5e6c5ccf49b60076c9e59b012ba81141406f88e1f9a5", + "0x0591cdff38f931943243dc57bcc11fe00016c079e7822e2f5c05abc1f6f620ca", + "0x75ce0b9b092b2cb0ca2c8c47edb9b4efdee4c9a02cf4ac7e06e66fd895751914", + "0xa971d354079609255c37c435814d8db0ab9128273e364f46d322a11e6657494f", + "0xbe6a7bb68f830bd66601b680d524b171e69f41b014b52e454d133f33f05d85df", + "0x64dd3b51c24a2f558eea042fb26babae62b4cb8fdafcadce838545998ffa40c7", + "0x14087f31b2b6788c8e4e7458dac6b5c119ac0d8c45a40fa081fc577544caa506", + "0x4b1ac3dea9f8ed24d2b6456bea7e051daf70479f4583d712d86f5fed134ffb5d", + "0xd6dfa7f3bcd633c60680e677370dd17f1a154d9a7f807783eea92ac4e0a4e32a", + "0xf868973293bc99588ae6f334b36da7fe7663b877692d1ee6937541310bef6ebf", + "0x31c8899e75d3b69e17a20b8af85ae9826c5b5026bc9bedc97dad5d825f1ee232", + "0xba6ecf4d416f88be79d199c0a829830c2e3e1b53434eede13185b8ff54647805", + "0x2f6c1e0c08f1ec0267e9d6fc53f88c2a2edc137e96208172ee5da94bf97e5776", + "0xec86c7c7ad119860889d7e7822bb9bd75d62846472ebda1b3cf40e845faaae1a", + "0xb27a241f729d6688c9bb40c3f7cd9f9db446c7ec5b087de8ddf0bb0358455b35", + "0x29865d34f76493314484203815af01c87cd4eb2efff4ed1963f373eaa1ce597b", + "0xa5ac0f879f8e1b265faea0208571a669116fb35649f8bac06b05b434f7fecf21", + "0x44fbb66fd8eec4f4031d85443099b27d6d7483691b44518d3645038544b6ea22", + "0x5f1cb19724989e54106f22448beb0bd6f0f65d3562666da70d87682ddbd2aaa8", + "0x1293b854d72082979388d7fabf9cb42c9bf44c6c8946048541a7b296370f1087", + "0x6fac8a5d71ba11be6e13415f97a9eb9d14c10ea18457391041ec0e3d66873bb2", + "0xc9a453ca74fd17cf1f860b52818df541f29c00f956e8697e364a11421663b209", + "0x78f32cd4aad107a96528ffb6cf3e4a8aa7b7dfdc463826e43cef8348c8a08064", + "0x830327c9c07e392312271301ba59a7ab2026f9c0e35dab95c0bdb94ef1ebe470", + "0x830327c9c07e392312271301ba59a7ab2026f9c0e35dab95c0bdb94ef1ebe470", + "0x995e254a08ea8a5f5d0ee7eace99d16ce51ac813cf4164901e0f15e28e056e45", + "0xdde06dc99a5b373732125d7fc0dec815ca380fb178b6ab655b6fca806da69b7f", + "0xfcc609220c594200792be93fa7f31a8deb96c358a473c69220bcb5329141e805", + "0xdec516e046089b6f597657bcbd07cf84b3fddc497d829cf231939ec70d4426b4", + "0xcb30f77cd141c5c3d96463ade078a711f8276d7cdf32c02ca9509fd2f25b973c", + "0xd79c5a0fb816a97cd79544cf5343a7417a2cee78c6bdb020448e393e0694a2ba", + "0x575296e0e69d8ae6668919914cce9d5e42c5808fe3b55d8a1f3c9903d77a4d47", + "0xbc32ab1a0f3b5e33025d7d123dda0a527d8f1b24dbd0a22ce2e1676e841af576", + "0xc160a1d83eeb76cb6c258e25731aca7d3bb8825f88333cbaa1eed606543dacc2", + "0xc52ef17f610b98589549b0e6a5548570289fdc23e7e652452afe070cbc82fced", + "0x6e75cdf101f786c93ff19228af25a575382d533edae22fc2b1b523ca4ccc327c", + "0x028d1614c0d191a39a0a8a84e396c9adb2b670f0ade80a6c21c3f6e67eabb5d0", + "0x5ded022fa4e3a5ab378fb360b8008e422871b744ac8c30ca28421d434c37018b", + "0xb5deb8b87c8ef4c37093c74641079a9d3339c3d2592bb7ceefc54d032913d34c", + "0xec345314d36d513d589f716d6238f968f0a76f2f925c14e3c92fcb32e5e2adcb", + "0x7d8b74a824c28dd8a8b83d9217f1238774512c02886725116e68b58944c06729", + "0x609d593f51783d97170b0dfd8e4b2edfca21e8a6d8b44fcd81e2e9a764774d7b", + "0xbd99d9d470b599aa439af3bc1cc7dfda735ae8c0ad15e96dbe5c59beddcf0485", + "0xf04f0e72b911ec2d57386b083708c921c57415e1011b91f612733675e8c49d2b", + "0x77186e48080b1f77881ecdb85cdfa2e364eb2a7533aac0d3a54ee69b3ac3cf8f", + "0xdcd8607d9d461c5706f1e793105d76f426e36273dad7812326536d06aff19a77", + "0xa020ab2e867702b40b3390ce0d5d5873f5197ad9fcb09b604298949ebebf58c6", + "0x0b83300040b623f78a20bcbe0875d8886199407ea9e208b092d314a3c2ac3407", + "0x66729cd431b02a4f0ec0cbde5af4f3cd228d7a1ee7bb0b0960475b124589fb97", + "0x0f21db015960a59891576369e4b0482bb7070442fe3d312c05eaf2b7c666907a", + "0xcb6cf5c627cccb80557e46c2816d13f90925efa3a635cf506a95e102e2cb1747", + "0x53467c962a451dd51927f79e979157b7733a496ab526b12a463a0ac58aa9d3a2", + "0x9efa907ee0ef9b61e40fd10090d940f8f49905622e12d0ff51b77bc3b273725b", + "0x1c93b5336d00c76f23d3977534c83410e0382f1006b29aa82ed013b0847bedb7", + "0x168acbde41433bcd9a68830eecfc82ef0dea897bbe6cda85541e7983b6fc6e81", + "0x7533621f173e676914f49a79d394938d5383070481a6108812da18504b8f6751", + "0xadf417104419f3a640d5eecb5fd956a1d4e90a2840394f6e02bdb065d97fe226", + "0x35079a979d6834d09d6410f974998b05e69d0b5963da808fd20cdd9c21619bf3", + "0x4be6511a0a69c117270c99adfb1b7a380776891b488b0d3124ba1748e4415e27", + "0x2c1dbd94b3284b5c4ae28254b7fc0d135f176f3ff2a68f78a610b853a2181a58", + "0x515c606b3592289af958cc02b9ac15cfa5e7fa267e65cd85ee2e91454071818d", + "0x3bc1e690c7f7d59b0fd14bf60f82836e56897fa33ef9374ace2f970d3f11ebb1", + "0x50a75984cc00f5c535af015b9691ea8cfc64cf39b7951a315fa64c1c6043bd40", + "0x9f878f6176932dea516fd3477902e4e50e60447a09fdf7d4516b05500fc26f45", + "0xbc29bbc86d02f0c2111e78dcd9e81a1db38032a376e53c45dc4bf82f03d9a657", + "0xef1956722c249845ac42225c61dacb2e31b33a99837023c843bf6152bce02fad", + "0x3362ad80291f6561e2a0de86463a55b99e1a4269d36c7da5dd9f7e3290163306", + "0xb78fa85bf0710b410cd941d047b9523281cf070d3e369fb58e6635a7c9be9c52", + "0xff998614ad3cfbe5e294b3e4d989a20b76cce329b9af8ced6344e2155b41071b", + "0x61cade694c82ba67fee4c260978adab03e5d92188fdad467b5588a35d3ab760c", + "0x978a5bd6016a844c19df334768dca93f55b8546d5a8c968d774cef38f1e3d1ad", + "0x222e5a0647bbc3b044549256aaedde16fe03b1453439523509a5c5ddc49cb26a", + "0x74ccbf647c39ad6fe2e0990ab34c766b4b0273a761ff35efd03cb2caa39351bb", + "0x14b1046866f81bd974d03056f46feca954f734b933c0a667acc87f49143bafcd", + "0x86d9b66287c0472ece121e2c10b298952b0870e2e6453dd833ddfa5e4f5d1fe1", + "0xfd53005e04b5a86eb92a6992f4a9d774f171aef36a042d865fa48c8146eedfcb", + "0x8e721a0a19517703777b33edfcdd945bacb84505a8cdaeb8eb7270df3800c22a", + "0xcbf61c6977768f1f5eb4ed751ee1019aca91336f14fce2e5830b9eb6e0846f8d", + "0x61e369dd26d8408b88fc954f3487a3133e7c42ac0d22cde568546c83e3f736d2", + "0x35e7670c4b6371053b8c30428ee70a05d7b57871b844fefd5a83867dccc11937", + "0x0c11dcadc4d70365c317412448cbd1d99661962af2a39d1ab32f3f01f74354b4", + "0x368d7032360616ed52a2f5754f3c56c3a3e69d6473f78ed26bb8737ad36943aa", + "0x20fa177520f82344e5693880cfe659608893384b3114a66c73fc287f22700c7e", + "0x1542f87fece7f6b11b5476231cec1679b7755d5fad171d1efe4aa96ce57a205a", + "0x3aa5b68376d3d14eaf76d562fe3d19849d227b92c3126a80c8d40b37bf607969", + "0x7f0f0e5a00d20f6ca97f7f4b5160b25f6f612be50c945d6f866e2e8ad4123868", + "0x8c617728827bbc574b9dbb6c162c083a7d2e075ad24076b9a9724e2b45d3913d", + "0xd732ebe4e90f1dac4ffb227f2104c74ce45f76deeb1d5ba9d15ac661591a9b7b", + "0xf39056937067fe1447c1f3a37f573e379d3283ff5ee063ba54e5f1bdd12695db", + "0xcf71f79b869b102637cf96ecbb890deecd1a3e9628edd1ad931b7135bb6e90da", + "0xa3b0b3b02a296644e6de6327b32cde2761e8b0760571a270e38a574b2e24c55b", + "0x97139f5114513a750520b65bb521bc924c866d419dab866c0f030e239a8fb07e", + "0x8470d9504061233171ce203ef4766f068ab69d5275c5e148f55102288133f401", + "0x71058c45d01e152cd1c38a85d95ec4f47eafb6b6ffca8291cd17041faba81adc", + "0x0e48217c1c2d652d35c47a6b3878e5860184b6fc520ef90ca1ed67874ba6582c", + "0x3b596229c4f1d8b6d5401b59e75567927027d6700f4dde8320918a5bdf8dabd3", + "0x7b163fd5b1bf5c470bef2333c3444ca5de73008c005a8b62e313e4320a712c74", + "0x02714f6ef12c3f78b2db96fcfb46313542a20f18bd4758de248c450e8e0ead4d", + "0xe382e55b27f4b72cc484007e1773132ebd8e519d53e01108cc14a8c6902232d3", + "0xe4a480abc85e00c7c92025a957a78cb4d64fca31ac96474e4015c2fbf6157f2c", + "0xca33591af0d9090ecf68907717ca0be929f09dca1957175b908a17e6830d0167", + "0xe85757400cc57f84c2f90a2160db535e682c8edae83606f59f3f6a629cf851ed", + "0xc52dcca1e514af9078d1a972f495e2bc52b1e12f1f57abc31dd5b7f7751b07e4", + "0x776037eedef898dc97a4453e1a441d4a8300065694c394608fcb11536d6670bf", + "0x562d864ea91ff4dc12cac8d4aff19d92551ccf6faaeed3c395518290da017384", + "0xd3bba4a51c134907ccdc2eb948d7c51dbb697d66a85944cf9eaa5a2e9c221e6b", + "0x7fc1f45d71abad09820dcb51945ca0b257c599559646e065359f9b3a3a4de4dd", + "0x6cf95458f0d84e6b1fc13972aa6d16657e145371a4a5d10b10d2b2c8db9c485a", + "0xb9ead63ac0d675f109b3cb3c56cc00904cab848138c5e34a5fdf9b28cee7fad1", + "0x3c4599f40f80dd2dffbf9af35e7bdace33afcf14767018bfff72c0bd124b48b2", + "0x5fddbc9c693522dbd92a7974e416e47c57704df291ccaafb715e6668da0cdecb", + "0x49fff9e6b2975e3fa946478a48eed6a26015e9307c62cd2fa0d13f0e7c3d0273", + "0xfb5b39eae61f052d505a5ad4f05179080d8919012612544706942fdd2b2a2921", + "0x78193e1539b8ea290d314dfa1b57ddd0e3d1ea7192bd74b2e2bfcad6a0f9601f", + "0xc3b71b75a34a26ececbef49860b80a54cdb1f4e82034ba86c0b56a12506c690f", + "0x1ff7d36301a39d411e2120e6f89b567764b73c0b85f6e070a15789f082cb2358", + "0xc0dad9f3f6ae2df1c4d4d5f0919da22db70e51d77f430890d786304ec34a8e16", + "0x718dd93647d9b28ecfb8ef08c550771e4687a25d08057a75500832f45bb0eae6", + "0x3600a7d31b6bd418bb6823a906465bad65648c7c621439417ce5397361a9bf12", + "0xfe0f9ec29938b587616c4e6c73833e39bdc315f3f41dfbd297ca62b59767870f", + "0x256cd75df6d91b89e8c6fba8f49570c0f8c693ebb83eabf2f1349c7314cadebd", + "0x0ded04c4e1ac137d5a3c25d795ed8a689a6e5227ce6547943c2d9033169d2117", + "0x13df4a947c62b5ce377ae02138bbceb631e670a9d6e5a8ae6a705425d08253f9", + "0xc8b7902ac61373b6e414f67f6bfae142e46b394c2f1342a1a0b2a5e95ed06c60", + "0xe9f1a882024742b352ead3946184b4fa29aef82dba8c915a2aefa5169a44c0e8", + "0x0308bfa96a3c2c5c30f899961bbf1f4f87329244cd61a8687b7143d4c08b2f30", + "0xd7c9488eab02ec5485845f0a54169987a0f13dde9597cbad058db9587d7e7f5f", + "0x017dbcf2d223453dc80c2ab03fe0d7ce5e018b2e6788546e8e076237cd467885", + "0xae51a90aca14b6d4f0600c10b34da39c3b12a726d4294947a48886ef456a116a", + "0x4eebb4e350284b66e7601829d3f52cccbc359526f546682c0033d3cc660efb71", + "0x977ddd9b435b4a4268ac12278693f8781512b8ae0d803baae7312f42b027d15b", + "0xb51c4449d31c4d470faa0c5958eb25f31c888c970cb76febe3acb95ea7ae35b0", + "0xe2d8b4acb0eeecc48c808a58cb89912dc06ec24396003675afbe20940290cfcc", + "0xd462f24692d7e482c5f0115b0c76bbe3e6274bbc11cd1e5d5d035d6f87690a14", + "0xb79b838d3b3b2ffb072b67eb5d4c2cbbe6182d781a2b6b2a3dbfdde1f0ddbbc7", + "0xa7c60c6b6b0eea9504f61152f885f08708c370a84eca3143271a5f4d9514a2dc", + "0xd5300453bebddebae9aa4a7da948c6b86c40bb6bab7b4f4a46a36803fa310a84", + "0x2ab00f188092374083d7b31d76206131a4031f2c46b8f8869b659cb4c7ae25e1", + "0x8cc2ac90b817b494a252d5952759fea08c1f02819ad28179895ba26614d5d9b0", + "0x72e8e91ea0e4c78dba8b9f6d3585610f6e81ba683e99e9af62a9e182583eefb6", + "0x58f5248df165271103ead323a7f2b6f625f213ea15393efed67fbc2782ef66e3", + "0x5c8440f775703ab04e3cb701890f826ab2cd8cd49636dad40f7ef43e2ebd84cb", + "0xc2dad83d39774b0557cfa215a7390f27dd84e85bad21a177f4b1270176f6f679", + "0x6be07b0d10600a91110073571ba5f4273127024fec12b50a341a8dc563e64d30", + "0xa7bdeb916fa9e277226ccfb5a91ece1ef859f0ebac2043d620d82ae7221c4d29", + "0x4245c0f35ea77362d9a95073dd7e75f44c1932eb2da72f52aaca3ae6d899d958", + "0x1c54de641b4e1f230e3448ce83b8b05f559b95c4e34ef7caffc0821c7c834965", + "0xcf218e582685f2f38a22b03ce3d39b62be782b76d9e73be053273225c8ca6254", + "0x1529debfee6deebc3bdd6c0725aba11875cc4558e5d75ea5b5b46827d2f49b44", + "0x2c8169202461bd2a9befb1f447c7d1bb57577c253e6d9254e96b5fbfdb876b91", + "0xfcb73bfbc0936b874e34aaf98bbc6b33653705faaec54709c708fc225ae60578", + "0x1222202b19183d0f4652256aada93bee1528e5579c5678f76d281ee7a074affd", + "0x1c547621f81b4cc778bf6ed29b521b507b424c87b245528d053963e49eee2fb5", + "0xb8bfbd004716e0adb102d38e3e6801e996c2b51336aad90b6da297a05a5a4e59", + "0xe8f2cca25a946535b106a2876b475d428493a8cc1a079b90c8385338a11ae61d", + "0xc9c13a0cc78663bc9800417378aa3b295610d2deefd6ffe1a5645058823ec3be", + "0x465a8d78915a7ef7596daf6c8da8862fb82215c372a513917c6d40e8673ec055", + "0x35f496b8a0baa60350bfce7596521dd9de0bd7330d547a2bca3df629b63090f9", + "0xbfb84d44e3f5404462e6774e52c7a941fcfde55cdabc7577dde94999052161d5", + "0xa86f6f61efb44846d0c004bd2709feb8955d10569d505e0fab08adcfaf309b6b", + "0xe59e2d3e17d97d5115733282f58f105db71a940ff5cf04a475e6bd6300beb375", + "0x44e3234460f549ddf2b5f4b02361c530bf375022c6f431077af701e275078821", + "0xa55ef540f1c70bf5f2c12eb04144a783e75171b5cbdf3380f1961f621881546d", + "0xbe3b298f03db77d9feace24748a3916a83f7ff047551254d17c3460066080151", + "0xc80e8ef13f07a9ca88eb37cbf415b393ef708201a38474bc28805cba252367ee", + "0x175fdf8570065b7f613a857226a75b7f1de83a984ddde0bbb1f5b749e6b2cb49", + "0x8d0104822154096c98e70d73f9a79b2da7fdc23c875577478e124bbdce4f04a1", + "0xc4caf8cf9e1a78d6f256374947708f97de48468940e2ee6ccd6229a73420a2b8", + "0x21c78af70ba80d9cb4ae8e0ae578ea9ab7203a0dcbe66a27dfe8579c24bd922b", + "0x64e451863e70e0353eb85ee8f5e827a2bfd0b20ded9cc827de7f628bef905ad8", + "0x1bb00b7a3e33b40c749c4c376bcf4aec937afa6e7b4b1fb9149832a7441993b9", + "0xb54515a36a7c12dd177ade6b7fd10a60be6acf1c171a93f29bec03aa1de2d6f2", + "0xe8e1db2262017099e30bda6ff78b6dfcd653ff64d047bb5253a238b8f4d354ec", + "0xed7104a0601d7351dc8c9846e519ceb48ad8c284753d05c895ed13fc5243b7f7", + "0xec21ab46d8723724a7c96ffe9fa8ee7af9c901227ffc849086d8a78971efeab4", + "0xc6340688b043cf4740a692c9545ed9b17c289096f9b04b43e6113b9f6d0f810e", + "0x74bc9be39c273262815ed0c879eb30c466a009e81173af4f80d5c97b785c3ea6", + "0xba5a2fd1ae374b9754b44c09dc3755539191ee7101892d3b69310aa6b49adea9", + "0x902c32f7615c90b6929e5395532ed198b1f280af3cbe98c9cfcf9dd3d3e21f41", + "0xde509d63f3d443b4c724b206cf7b2f1a0e8add5354fed97a869cdeda5d8a7bfc", + "0xafb9ff3c8d428b8c705ba2edbb8ff8d5edadf9cdd351bf9518d71863c1995192", + "0xd977935fec28b5c978cbc1f30c7937dd34fe9a1d332accb5a94d7fbef938d642", + "0x8d65a229a03ef45d5afa313144b5ce22cf14d6e38e40f31694a3262c282c99fc", + "0xdb425d18644d0add3b43355872d654c3d69cb07fe3f4a311929f319d0978a169", + "0xc8e1be38140fa78c5675405dcb468064f5275dc058e09fb48aedef79a91fb9eb", + "0x976e6f096bbe810ddf0db175eaac329ef38aa57178843374f1dce49b8ff6100d", + "0x073753f8a725725f2f0d8f1f047aa2d69c3709b81af31f369e347d6c3293418d", + "0x397e6fc26a7bb8b1afc923678d43eff29216dec9411624004c63510c097f546b", + "0xa56ae84d4fe9a0639272c39da361b2d2921efdc5f4cfef51399c92140d9e6063", + "0x1b6959ad9c9c0ec277dbfd45476eb004c56ccf9981128853f75c1a48c39d4be6", + "0x072a54a9ea7511ea69207e6e3e81dc3fb9ce2a711a10f1c484e1031e603bbe37", + "0x3981030d7f074fc7de320b5ba1a9bcbd778ac9df4eabc6818cc571dce3b7bce8", + "0xefe6978c56cc5e0fd2fd32dba45af9657cf16a070eb96afed8fa9fbba2352876", + "0xbe86fb58bb4fe122ace62b7dc965f0338edfb49ab4171c7545a70a946f3b8e93", + "0xa7ce788bd04de33d511ca01833eab84f36d1d770d7bb43fd5db685b9277a8511", + "0x53a4d535a8236df3046c50c314aa9f81436c29a1acdb57c2bb4d9821100f4101", + "0x2c4de2c22ca23d4650a71541bc5095d7b7a6310020f10fdde2aef79f964c7ea8", + "0x120cd3b22857866b812770434fa5e2d12c969ca6c45dc4751b41849a78af586f", + "0xa8290cea08b86c27fceefa2e7ff11ec5407b8ab6c5343b8435c9bd380a1b8a58", + "0x33801d7ccae1128c9fd5c149328534addd5dbd3b30d9c4915c6f83b7a773d269", + "0x5c2fab9552671069564adfa4e67b02872c7123865bc7f082955bd171ff4b406a", + "0xc9336892f3bb13d02daa075ed8fbbbca7809f30d5f26615c4cba7368a41cf656", + "0x3d12bdea6bdb6235aa1b00b26517603c661292bdd49ab238e5fb6036afe5bba6", + "0xe4b195020755d4acc5c3574b6b26b33dfd26cc55ff156cd486c7276f82f84ec7", + "0xafb9121742ecbf5592938c6c8952967113ac2b58c6034278c4f1e0e7a32bb590", + "0x4a066d8b3ec18a33e24950563520cdd97c3dc86f86f62cf262dde724a47e92a5", + "0xe5113ba1f60a02646c757e7a4d3bf5eaa7cb4ed1028864966015278610ee4606", + "0x057e804b233ba6906d9b46f141df3d30cf6b4faa079027dfd7c4795a6e6400e1", + "0x8fc5d75929cb5c5495a05d474a9d45581f67fe4bbfb9a1a84ec731ca1b03d466", + "0x762bdabdd47cbb7a484ca319fa3976ea08bd5decb19151e3ffc2a0b5d0add404", + "0x0aacbde1953c896fbd30b7943d1f91443c8640695b8478ef4494a3067175b923", + "0xbd9bf9f527f7de3b897a8d364bc7f7d2a6f0f58b3ebefa8cb76ea12974ba72fd", + "0xa4ca49ce138213e61d4100a8076b729933b0dac173dd99b05a860d1c07a27320", + "0xa3a2e8ca3f412f7092945543507ee19211653081de2cbd2eee33c2e1cf6ec69a", + "0xf71b48a6979e1e62475379871d7509ae94b5a15e80d20152a5592f443f26dbf5", + "0x70464019f5b1e5376af43bb6b0a2f0dbababd29e300b58f21eb9a0fffafbce8e", + "0x60b3da9a35fe971d239b3576bdbb9f53173c076e8c4384db6d512da7df23ca93", + "0x196fca8456956fa2319fca34a181b476b481eeb61ace404fb030f7310c9ca944", + "0xf2e8d292739115f7b56bbdc80d784b90bfdbab5f5d84ebb00b49826d0d0668d9", + "0xeb33e8ad7092ae0612c72baeb8c95f6f25e396a31ec5d845fcb9a7ffa93c31b4", + "0x7294aac7bb4f549c3a516f2eb3a4f9885974698f42b610634a2ce2000caa486b", + "0xdf752686aef35e244bd8244cc50c8e1cc6ab694f8ef73f0013780243312eb86a", + "0x5fd1a1b0b9c7a98c8e86b8bec61241ddbba19d1832415d52abcf3957c05e82ad", + "0x99ca3177931f434fc7ec4b577af5d2d6ed41f1c94fbfe2de0451a3c3458a67c2", + "0x9dd855ca17d154f601b5567c13a07224126d37a4f3ca0368b9c51c3fa37b7847", + "0x99785f2bbd19634d25db510ca7a2878c68a21ac11663d10ad380241455c738e2", + "0x13cad98d5f4fccc0d003ed0d7ce04061c133daff8841eaa72519997a523a509c", + "0x2687ffae797a6d8e04689b91011e357fb6cdbecb512dbe2aea8ed19d9daa9370", + "0xc70948b0a9a47b4bddfa8678a1e64769705fb5b1eb2bb3235daf541fae612ffc", + "0x13272ebe5d976ca819d5b203512356593ea075554b5207f2412f00f54eba4da5", + "0x08b586852e09a3d5e344d2bd0ab1e8a3a1749637878ae38e07f906cb5d1e9066", + "0xab4497fa4a05a6a9a03e985100f4c88d00baef6d2830344ac2bd6a716b69be02", + "0x460387f9e05a8134d691e1a6a1ab5845ca7b9c9d9bedef2dc48ac5dfbb53cb1c", + "0x131492b15f3794dfa3168ed66550e0daa9f4c12fd76107707354d9bb059feb44", + "0x41560f312e1c56019f6cf08f2cb8b03b19f608ac8a72d59d2c81a90a937da16b", + "0x8172cc355f81c243e240d32b1b3c23bf23087797bd543ce3f954ebdd27810b0a", + "0x7ed8c14b1708ef84ab6419f90d0c76147e9f14e929d1f6eecf7d875a898fdba6", + "0x20f0cecc5c98a9ce3167662b0fe5cab27f454998ea9d96bdb4f456441ae6dcad", + "0x6d8f6a24a8b6848be4adef96e6f2a4bf7190855dd75d5cdc71dbb5c1c91c4139", + "0x1d65918db95e5f5ac0a6509c24cb0f1ac17434d5d45fc98a4161cb0ba4826437", + "0x65733d0fa35c71ff761dcc7e87b11fa7d066edd5f4b71dbf0c66ed52fec28e6f", + "0x155db5de9f9095f2e0810d7e000f9e1d15aaa237b9f83288e7d477bf2bf5a08a", + "0x4f956a806ebd98e7263ed1564a30953cc390aa5ca6627d81f167f3510ace4f23", + "0x93918e5de1602233bdd6b143de44ba4f0dac71e92c3a50c95cf1076d8a899edf", + "0x7c2079f229cc9f3e9af1ce3f6ebcf62c2644817c8263ef4a4cbb298fad5c7d60", + "0x5d7936b3d27a9dee1b6316229d2cbc1e46e394f8d2f95071aec8c17af291c9cf", + "0x30f4cd92431bdab69e541ee067636d0468b827eb66af9d365bc91d8a8863345e", + "0xad09a564aa6e4b804d6a27257cede19c6d8aeb8c1c0b7fa8731e5733b6ee2bd6", + "0x55920ce9672a9f8573a37e5191d40b6c09f760c020d07cc2ef26f0273fbc7031", + "0xa3d6b311c72de3441acc6df164a843795885b07adbbf222b9ceec77dfb51b67d", + "0xa57c19c6f76141678219a8a76d76dd4a007f03f9b70faad7fd2194351b673ab7", + "0xfb1b0121c6a2fcf675848e77e33cdf8506a551918e3b233074a42196113130a9", + "0x1424227cabd23dee8df2c3e8b4dfe49a8ff1f8d29f8904439a25c2dd0760b6ae", + "0x453a3a29c9ef7aee368050aea97d7f897fe6e8fdd410a798e861fd355978fe88", + "0x8a6befaf2708e7852cccb459d39a1c15246aa02f0624a665c23c40f1b14b23d4", + "0xa94ca71d9c7b13769546e26ab9033c731e91f4f39fba724267ebf2eec498cb60", + "0x241e86a7b11a3e73637f254e16964b2f28258dfc633d9bd245f769525f66997c", + "0x7c17ec882c5e4cd76aeb704b9694b70612ef3e3781cd7b873bd662195d6e5005", + "0x3313781e283a40e721af845e09a46532ed1c8a54eb28813065683492ad23c6ca", + "0xd43be005020f4b7e233bb4a76466848b74bef2956e2a106b56ddc8fd456f513a", + "0x42fc5e83169fb8695a33e0c7126a438c980d43bc2e9978a8422e1363083bd209", + "0x8c82a3a78e19ebc9144a8cde289fdf59af3cc6e647656227ca29ae8b035b1a41", + "0x8990f367a365b5296584937cbfcfeb49481bcb3f127ed33bf508c4ff20f15a9d", + "0x4c09c7be68beaf20570c92525accd7e0e3d961240572aed84a52112cb99c287c", + "0xabfe61369fb14f61ab52ea15dd97c198c5bb6c721fe209b2350e0d6b84750f03", + "0xc7d355d9a44745dcc58b9b2c4031e5a5759d700d56a8f2b71d41a85a8f89b8ce", + "0x5b7d7ea0c054fb502f9dbdb54d01a106e01831fec50b967894e3c864ddea6ad8", + "0x07fcd0741926cacc8bb4b079d6dc310827d66b75a15c0c6ddb53e6d65cef96c5", + "0xee9487396ee3c7f81d5470e65a2f91f527c5e16141f24234f8320b321de0b266", + "0x07a84240e607c3b5d812de846a88e760a98f1d4cf238aa33cb71f06e79cad5dd", + "0x364bcd353eb9d94627218928eb703a55281056debf253f960520e65659d72155", + "0x7ca0647227a0a97e8c2f9618fdb3f2984b95ce3671ebdf1cbb3d4c9559fc567c", + "0x446c101e70dabd31b69dcd0cdf554cf96d33b572d2724f4d9ef850bc45bdcf17", + "0xe71d69530cc17fb8d8e3cc7702191ca22e9e4369d4d28842d0c692bb925c12c1", + "0xaef7cf7b0b21a6a97908ab8d5915771eab7659a51f25eb28091b51ccc8cd6d3f", + "0xa7aebb10851791590ba878359829875bbc487c7471a5e634b833941867620984", + "0x82800d5a7df75e18eb6893982fb34d753aa5cea23ee436d1436a7cf006594874", + "0x5b56e7b9a6c38f2990f900f6f58225228da76d6950f06414114b24c2b76b6551", + "0xac35d3e6f4a1034dea8261cb90f191e2e8c725749a6a1bef0ac35c3c6a2f6460", + "0x78c22d81c22d088feb0b33a6d6f29e952bc4abe7d89bca0e5635d5bca67dbe84", + "0x260c9901db78178e6788feefabc0cd9ce48eedb5c0e1d9d4b82570fd27b74be1", + "0xf0f8e623b640ce7216bb6880e27d9571aafc54fc4bd6fb1bef2167b40b2aa505", + "0x577cfe5be6ddff3029eef1e1a2c0d8363ad1e85e11c67c13e7141fb79ee4532f", + "0x41f94db62d87523cdec2ac8d467593e4b970307377578843164842cb11dc3c4f", + "0x6be52acb2ca0ed5b9cc5c6b9ba0e50560a508d471c95db31ddd85a68637ca6bd", + "0xb5c21de3a710ba59df00144ad69d01fa62d20beeca8cf69c672caf5f0805372d", + "0x44c6f8a9f6d29089680521f90af4e433e83ca886cdda0092cd1d3691309ee2aa", + "0xab17d3af5c34ccf9a61b4e4485dc22144a2572cd827278b7f2ae8afb8bb925cc", + "0xeeddeee689177391410864f4a088b6e838082eaa3f2198f8215dd03fd7f4c8f2", + "0x704b9f1ee0c4e5fc8c21f48f5b1a15612666234ad4c96ed6a21b240fa1d4c97e", + "0xb987cda42dbc44514fea28f08ae0251da8a7e5e237bdd1965b0431ac6f1e883a", + "0x32ad965a0218bb7287c9ce671e8381fe43c82d36a47cfe163caea1130531a108", + "0x9787482723d05f3a6ced25004b4905eb9ac337376e2557802594339bfe54a876", + "0xfe5b6aec824a552b65be3dead071fd6776ddc49b5c2623e3ad4c77b4e6b58257", + "0x09508185b547c6b38b3c85dd14b15baffe4c22e036d19c5b81d605ad23b31967", + "0x4c0e86bc9941489147dc1945e963192c1d46135431fcc7eb9dc738916ae17733", + "0x2c6989feb34e65574d4eb140aa614f8732219426786e3b5126420be00af270ad", + "0x48314ee6800c8fe20f957f54ae2cbe85ff1fa6a113bb7b334a5da208fb663de7", + "0x58ff4c9115faa70edf2af35a7f0ae47a0c4547d4b4caf83c1e9bd9bae9ec5ed5", + "0xd6488f1ab86f431ed128a3b0b72201720eb4a4889a47ea03fef8308179481b69", + "0x2c760865cd8990d6c493029f3c18ff839a83e626a0beeca2465f084fc178d251", + "0x2ca8434757d45ba6ee8f565848723ac5eb525e788dd3010bc31e7ca1c6a16bbb", + "0xb5aabe120a2096c4fb97379287ca17773aefbfa8af6612359587b04211507e62", + "0xdf113b7b91e469be247e63ff307d9b65c0faf3e8065ff9e30afd4c5f8427f64e", + "0x6b59212b2e390392bab77c5f16eac2f517c1f75d209b937a5d74d891b44dba06", + "0x9270cb0f8d0d41dd1b480f4692bc5b31459866e41cc695636f9d3ffe336b130e", + "0x4c526886103aa0dbb655a10b2dd0dadf39b996ef633496b4d587ca1f32653d3b", + "0x00d5af9bcaab0c3590cb35276e1faa6dd4e5d88a745baa37c015380fa398a194", + "0x723760adc070123cf727aaf66999af30b42d982cf14cd7d3f5cfba7b95adc849", + "0x71724947267e59e969e1f5e7770b24a679faca915632ab4894a0dcbfec874a77", + "0x6764882573a27254e6d239541757dadd6e237a3f6f944064c502fd82f7de704a", + "0x9b253837fffb12b50e21a7be5453bfd99e8e056eebd5fb0847496347bf27d2ff", + "0x5b06a9acea7c3362bb27812ab28e9fb5ea92994945d16999531b75b3bce5811e", + "0x1150b7f48195a57897a2d1ca0e0cbe364a236a085788f33c520164e942f7f72d", + "0x564049ded0c58f12c9b3acf6a7b5488173fe2861bcb271ddd37f0af8a0189978", + "0x5083b3173b2a5f2eaa30a3dbd16c97a564b793a50eacf9c478f6e355c8425e79", + "0x62092f0c5347f8cb17d509f9d6a70d1cdd4703302709e8e9c35d6f76a9399bad", + "0x2142d1cc9607680b1efff31f00c25cea933817c04cc3fbd27b343848dfb61e77", + "0xb0b153b83413f0f07699d26cd556a3dff6ab1f7c765667c37db5c9cbc527a251", + "0x9fd414364531c609d6e1b78475e4bb26d5fbf987b75962e2e27755932f135d16", + "0x29de41574a5797bedba8ed9b0f8d3bc6c3d12547503d4923964bd5a6621c0cde", + "0x08d3cc8baa4f7fdb68e415c7946685e9e96e3c10a06bbf06b62c5561ff07b8ad", + "0xb18f9bca6e15b2f5e3e803a294cd2997b3ef775dfe034ed44d061f78af90e85b", + "0xda86eb0b81a265dfb8ed1ebaa09999d92b3c7475243ae025de5943f23e6f1098", + "0x8e73add9f8bd6907684f012bbb7effedaa90f7c8c7a969efa7aeee6727ccff5d", + "0x22da590b96d8b8ad62de21dc1ae36b732d9d72b80cbdedccf1438805bbc85b29", + "0xd931d109f7b88e8e45a86dcc8e85f7c7609ef4cf70641e547af19e24bdd763e5", + "0x207b0c6b2bfe6b1582004ff3ad5f3091a1170851736ef5955de7989aba960756", + "0xe5ede6dceba22c8b8191a5562a0b27283375bd3d4049eebcc96b8b0b4a3eb2e9", + "0x5c50a6742fca302787e3f345849f7eef748862f57780fc2aab075973bb86f3b0", + "0xe0ccb02d5ea0848a10370cd24526dbd893cbc775f2a1575a0793d41d7156e609", + "0x7a52b036db72085ed0affe9fa2a83d624bbb2c1aec6477cf547adcf7a8c8342a", + "0x27409ae8bb2068a0b40cca991d1c7295591599e24be50faaf3d4d932a5ebbbd5", + "0x2882e9d33bdc2e1cdda10871cab94b003d61264d9d9c74a01936bd8217bc65be", + "0xfe423bf1891da0a16cb741c67054266098d5957cebf5d3dd278b103a614204f7", + "0x15fc0247b598db716262726dbdef041f60e6365ece9a6cab3203da7e16351309", + "0x22c53bfc722baf986fe1dadbc0239997940bd50e65a24848c258be91b5cd9a15", + "0xaf8bea0b7186056420b1e39c2a558aea06bcdf4945407fcf7a6eabcf51549fb9", + "0x9e506e3f74e6afb9e902ef5ed7158de1327143ef137c5f20160d912e8aef5467", + "0xf3779ddedb54b849036bbe21fb6fddfee0a1cf783ae43faf98bd600d57aa634b", + "0xa84b05d40cb125cf7247a3512209447272022923ca2605b72247349ecd3cfa2d", + "0x7391afbf889e89ee07c18ff367db494fbbf0bc3ce2707b38c8f269952bd5cc1c", + "0xaea1eef9e7631594a1325c7726514c09e048f1735776c5a83110ed149db79f9f", + "0x1a87ac94043b83635b5422a8eece8b84b6ca7343378695435c3a9150d0660d65", + "0x93fe1da2ecdd78b082402f6570e36276226fb6e5b8ae27c4046cee7d015014d0", + "0x3da809f735232817f886e6aa9dd84d3a56bfe7ea8342d50fc75928ecceeb5773", + "0xd9c5867d47ea6e538c4149038a4bba59a89f8d9aff66bca78079c43ed842ae4a", + "0x738221e2dd91ad5e00721a24157a50e771eb9f8ae4b4f859c7df26ab8483cf51", + "0xfdd4008a1eb31330c6fb110d3102be875842b91123709493e58db8b01694a261", + "0x315e76424074fc7ee3d49f168d0ae46606a65bc2a66b02dda2ee7ccc2ba1ecf6", + "0x93860f912ed53bd2c4c7a3c355b9a3b94d269510a9b7622797c2338361f31b6e", + "0xb4ff0def31fc40c0f16dcfb4b63bdc34e5435059c43cacc530bf21f6b0bca87c", + "0xb1e970ff7fec3dce7841f1287e6012c6fcd315fe48d9f868cb12e25b5ff28ac3", + "0xf3e4ad0d660d3cbc791f96163384d53d34231a28dfb13ba7962492320c022b32", + "0xeba82dd4987854bf322cf02a7fc10ec7c8525888529cf0ed96826fbba6847a91", + "0x0937a5caed9cb63f99ec07dff77fc76a93c05cd69311f1a1c10c716ad020246c", + "0x5f095457e2c82bfa20fd1a114007df8c1258e5ce5e0ba02484fb7be2153030e3", + "0x8c882d90dec2040612d32d783cc4a38e47cb9aaced5c43ecf80e697015d5bbf6", + "0x045200953cbf079448a5a846b7adf80846ff20ded979283bdc2118dd924ad178", + "0x0ab6857fdd8eb4ee67fd89432419a05d0dbf423c6b7f7039e17a7b9f4b16a736", + "0x6411d0232aa0c726ea18fc55b1e644c1439e8235c9a90db0bda14217f4cf7063", + "0x2336b4aa289e8ef999422eb37e56fc9cecf6f4272b18052df6aad6a17576209d", + "0xf6986ddb2ece1faaadc0342d8d5c17f67b3e99b0c63c552f260c7bfc313d6a97", + "0x134777d1ebe65a03b8af3c71106ed5f7f2ce8e0988b1ad7812f6772e70e07fb1", + "0xdd1a7602a9cca1e61ec3ff5edefc8e6d6539616e4a5e760ac6536cca4976a06a", + "0x8d045a5b28da1d1dd4ff25e9001ffb4a93d3ae631ff66954d3dc9b783fb69761", + "0xc6c33e3b9fa91b1ac18e2fe535d15967a4c268d9fdb64889431117371af80cf0", + "0x0d2dd95700e5909f4a813f00ab828c876d237dcbdd23126782f77dc82cbf1b55", + "0x333a93ec5b07deb275928037457e8334bf4ddb78d96888869bc384187e8acc0a", + "0xa330ceaab5b5ef11d39f9ea0a550ba9d5c7cd2174a390f173a1106a5696e4021", + "0x9140deb3adf77eacd4ecf25a2e9677c4999832124e269f245fec8d355bc66c95", + "0xf24a3037f81b584b015706a8aed86ab3ca2fb550b90fa987e5a7329539021a43", + "0x3c68f82e312d7f12ce8190d2bef21bb2727c6ae4644ad461b08f666a0b0174cf", + "0x2a6d4a43ceed30ef0ea5275e8a645b1db9af1b3e7bbfbc92119e53ee7db5b0b9", + "0xa2020d96d1222faac2b2d1e25736610d6dd32c1903b1e6a3ac5cd54b5efdf444", + "0x234a0b57ba81fa71b48ab4dfe7d86e0fb5d246f2704d233fa2204a6b93ee20ee", + "0xc3deea82fb1c9aa0f2336e2360ec904aafa49fb72abc421620e4f9eb8a4bf9d9", + "0xf7f63ce6ab1f4b60618f599843d34a1ebb969e0fcac08ba6cd78d5cd202e3b26", + "0x6bc67621101837504659aac54cfa9564cf74e860edb1585282d80b13a1690a9c", + "0xfa8440ade522606df1d7f99d084041ae59969554447489c4953941c4efbd48bf", + "0x56fc6ab7e15fb2b8930aa4dbd733924a03fc5fbe78eb4ca064174415b9e7451c", + "0x56fc6ab7e15fb2b8930aa4dbd733924a03fc5fbe78eb4ca064174415b9e7451c", + "0xe2ef645237348dd77bab2ca3c2a4499f49f502ab087838466fbbdaf85e3692d2", + "0x94be78c713a94ab0c53a6915178522085863f93d57492aa8595f12914863b9af", + "0x4d1e97428c3eb035f6a7f7c96603e553db4f1c6c5cec64da72a9b45fadd2e6bd", + "0xfea389ee0518e87e9df8b4caa0cdb2b956d8ce19ddf8ed8b6473f970c06c8fe2", + "0x64aa0cf026d8c7ddb69b8cd4687378024ffa52192dfe65cabc54fbc04cad7722", + "0x33d69f1b6fcd63bdc4d3495fc80715ee000507ca912026312d050c2abaf18040", + "0xf3faf9644dacd6f2716c65914afca270a68434878bebd869ec38d1c5b34de360", + "0x4685d97c1cd4d9bdd03117b70050ff250a087725b67a4beb770b1986191d8517", + "0xb80fee2bec882e58a217954c2ac9311ec643d916d9edb54947f06ba19b715ed8", + "0x5d5d815da42c501a8294a7eff9dc159a2875e54abf0d522049c23c783f06c5a8", + "0xee7254b06c750cd9a70fd59adb6deeade61a80d6d834a2ee173f65d89e9fa9e1", + "0x910a90ac58da8fa75cfd66343e34422813cd9e1c2467cf9250b084219fc2f8a3", + "0xaa36adc8cfd41f8263053f324ad8acf658eb0ff9c10210c4b4043fcaa0d0a4e7", + "0x3e5cdada7c46f4485e462a728c73ac6f07ba45c5e71b1a426554db0008d1c6ea", + "0xed5b5826eed682e04c9be457631ead5fa4f5173e1ef0b8f8da6cb015c4988d60", + "0xeec0b48d7d6e54c50b48016a6fa71d4bdfb2d6e6679467634bcb6c5c987e67b7", + "0x7f7fdef2c19f6afb90f8d77fa3c4d2548bc0638895da10174f7294f5fe7de246", + "0xed6d45b4b10806bd19888a6caca9634b9c3ec9be111d439faa7df7d70deffb10", + "0x074564435085bfc0b5c5f55b9c85b216a9c7a8e287269619142e56c5091bfacb", + "0x839a139dde9e675c08cf28dc71e0426c95e1999b4414502ff0730aaa7a8fbee7", + "0x52f2efa40704488672216243c475ecf4715a75b84b1450b35c531b9710d9c47d", + "0x307d972c6245a9c66f9c3a64530dcfdd133859c99bcdb1371f4373354c753d33", + "0x31ce27d152523f1b7724dc74265ca3c818a6f2d11d561219bd0da8913c6bba63", + "0xd1a9bf54851eac2bb00b64d5e7d2c9f18afb1910cfe910c74db5380d1ceb1f11", + "0xc8290b4c138659663ed39d70f3e768155d7d39b2b8df82ab317f00f964757bd0", + "0x26d3fa338088cdb7ab69e043728a2e66bf447ab570c758d0544c087b271e48dc", + "0x26d3fa338088cdb7ab69e043728a2e66bf447ab570c758d0544c087b271e48dc", + "0x7f3b8dd2ae18acaa586dbfffec3970d7e299c8dc35924001f8d6479f7c256eee", + "0x357f2217d0fd54142a953fc9c4f9bed8ef7e02ec54afee6aa35a2ac3ad83f89f", + "0xde449704edb4f22a467ce240a970487bb7a033a4342994e5293646b6da6b9b38", + "0x54d788eb926fba685991689304fcb0aff8d0a521b0bbf3041132cf760ce8f807", + "0xcc4a1dcd624acc0f6b51d610af134a94b6f09d7778d47552028db1748ebb3ad9", + "0xbb39053163426d4c8a87cf32f0071b2d44b52beacd4a7e15ba0d02634dd9a558", + "0x0361b7eb7bb864338eb16c4d72bd4139196b06932260f40a9f7a10f869da3b19", + "0xaa3cced9f3d4cbb8b5bc2bad52574b89071ea06ab01fe24492734513e2f2b767", + "0x1da344317599ea84cf0b7612e321beae9baf9b1a069fb0bf82234cc7616973c3", + "0x9ee5c4dc3b9efdfe4eb54c851a3e4646fed668be343a8de6f57baeaac9c32dd5", + "0xce608e859719844e4bed273426f08f15962091eaf3c94235f312b8732f2a9c57", + "0xd36b7a4cef767e40948960f58bb95b149cbd195b0af084c7cd017d4caa462f9c", + "0x2edd2810e50f37c22510c43d65275fc7dd47489de44e3a1e09bdcd483aa05a43", + "0x429941fb762341bd404fded140523331f135b8aad2ad92dd40d734afaa613c3f", + "0xc006478be8e9691ff218981a2290602b90cb8f7670f26e201b96c2ff0cbf513e", + "0x81e7450beb9c4f3dd4ff709a3ad11df9ec169c933c64bb2744148bcd6b8a4a96", + "0xfae642ddda22afb5a995a8588df322e6288c221dd8131eae42588f03fdf26c12", + "0x163bc6425807ac0655be5adfdfd40d7876b43e50d97b3f7b5461f78573e12418", + "0x3bdb3a2e98d26045093d7c52f6d406f60811128f73caf8b8809528a05fd64af7", + "0xa8af7b0b6c8f99ce4b771ebae43cf471c426fd5eff621dc500742521d3e38220", + "0x8080ee41d9cdf51abdcb8d19271ae47a132c0e10eb01c4d43e17b15e58235bf1", + "0x425cc7275d09810c33fc822c5d4f61396ccc9b92983c1bc43efedd72c149827d", + "0xe85aa293bf5a14e6cb64f17cd3ad17d201c03918d0a132cc25991152800ea9da", + "0xd35af96793a6b5c1e555b0cffdfce48fb05d3d8c82b6aba7570373bab2ca00e7", + "0x6079f3ae806d2d5363960f576299a2bbfb37478efd80bf2a4c8f37cbfa6da0e1", + "0x4dc17ccfc1b74a283f7bdc9f1c355b7ded6696a414f98fd5e1d585cb775edbb3", + "0x6478278d1149e6fbe967aebfc496fa2bdf463b4f2d3f12cebc0ced9927f6e21b", + "0xf23039c9892265e7e62c0b6c2f5f81509aa84d806e094e625f55447146083158", + "0x9abed8ac7b0bc9d8f1f3e434880c99bd0f21360193fab02f1ff903f57db63c94", + "0xee63771cf1ba7a73b640c0e7af5ac55eef31dc74314a6852523c9d47f9db1abd", + "0x1e523b37a3450da717e92a646cdcfb1dd7ac34c3a04c8280617c307d6036294f", + "0x5afa0e1170dbda1d85e28ef4091afbb685aae867ce7811b2309e023f2d2d3ac3", + "0xfb7c23a4511222a41bd801deca6b086b7a9df9db69875525d770b60bca9a374d", + "0x6e4a4fb4fbe4fee4c62270ca2a15469ff7c82a9a7e0992d2dc61c95600ffc594", + "0xf8e05acf23c2bd454fdf84eb570309b86d5cf062a5086b52da8755bcfc555731", + "0x7f590ba40394e57972e508ce195672e32cf685f6ff05f7f7cc79062336746737", + "0xc91f5d4494ed638489206809d5a4db769c041bdef3a04fb3e7b764904c979cc4", + "0xa7efcb9615f26117fe1ce26eb3d561cab500f77dc34fbad3d300b80ed58c0659", + "0xc1aa65bdc9844135fb1ba5b8b6aa1d8ac8566f25a1e154d1dc0b632711b002c9", + "0x5a1c91fdfc273d56da3a140b33a53bf807bb67b37bdc28007c2aa69fb2a7c2e4", + "0x92dcfb43439ff2112c052d7b680fc8cc6921d877818ea31c060b7acfc7d50736", + "0x141d5492157b7fb52f65dadfce50e5a3412f55bca6d2dfec592d401d46fe2405", + "0x23507c7b49ded75831212fdea019b9716542a9ecf4aea0e6cb28c39d179083fa", + "0xe97ac77ef1a892cb3ae0b73368ebb259cf6397199208099958df46c5b67cafba", + "0x0e83297e8ca8ef98a3860ceb9fa7abbcdc67790bff6ba39a4012a0f18b2b8616", + "0x74a82a09ac0120aa4b9f5710c94c4355748bde27263162d0b675ee7b67bfd828", + "0xb2daa46c3c42a4baf43ce05ad5e2f5d4993a0323b851b6aaeee014636a96f871", + "0xef5d49abc72ae866240580cfada4be75b2df109facccd64751e7113e3bcce80a", + "0xc8402acfcc50ef8c8bdc158cecd07a40acced7ccf355675f6243893f5636e21e", + "0x4f59e7d55c1a8dee72e70f7e8ac9a57a0bf35e4b01553408222f828ec9244ba4", + "0x0dd50aba97a80ad7945dca002efa100c020be3509f29037665756fbf80e4f9f1", + "0xdee0ac1d4a51893538b9e64016166117fb85b24fdde3f2dc9f7ca9cf54f74bfa", + "0x4bf9ecda9023692b78e960057c8158cf0fbf7a9174a684f4dd3622359c8a56d7", + "0x2a54770fa84f90a312a8e675485bf491ef091de76c90e7fefdeca70cb5e949e8", + "0x2fa793f3e594366d6d269568ace0a8011831e8049a4e359960440bb547837c47", + "0x76369dc6251606873dfa4edcb4e67fb645f3a3be74ce4a4c84e9e1f90ab8c1bd", + "0x0cf50c329600129a20b06300a70c19f7114a9ced609b118dd70dc2be85f52e4c", + "0xe8a3728e7a695befd1defbf0daa7864a4bdd8a1fd2556c27f007740e25ff4f21", + "0x287b572f4b0ba2f51daae4dc06fe3ad85ea9690411a47a09db35a53884e9d175", + "0xb191f78de7c30c655f4837c9772130b81ea0f04aa360873cbff61791b4c676d6", + "0xdcf745d8dd68906f7fe5686541f12ab231428eced46a2298b9bc1fcaef5eec57", + "0x2d0b1eb8370d5f4487ac80e11944a0997e484138839da10e5e5b1c4f01c4f3b4", + "0xd3a9801add883839338568ba976b58fba59b46a9d59d32887ae95907591b1de3", + "0xe26909750935c767c007c4bb3d036cbbc278a3df9cdd36bbd137940fb4350cb5", + "0x1fdb52f9286dffd00a6bd2e8f533ee3c920390fc1244a18f1b8017004f802dd7", + "0x91d0de9390881f313f184b6e5ba721ef91b31d070828ded6dcf7a919a1cfd537", + "0x36a71c6e442b891cc81af284fa79ff9f1214baf243a5e8317324c6e8fca60267", + "0x17da203581580dae334035df81fd6ca4b7c476168c5554b4336d982c1abedbc5", + "0x1318b2a7aac318382f5501d4c781b84e28fa530c45ad8e2c477abb1895ee833d", + "0x6469e56c6dd070f0a65afe351871869f82f4432826d987545d40201bba1b34d7", + "0xaad18eef5ea26a950c6dda7d511576fb3dcc66bf6ea96cd940fa5cde1447c5cd", + "0xec908da0b530766f38724c97f884668bc4e1de92f8e844ba76b55d8a5322e3d3", + "0x9021caf69beb2468dba4211bac55af1c8526ec146b5100e44e7659b8832ca944", + "0x5fc30cd551f6439b2fd3633170b4d0405f44d5230d1751c69b3dea73a4db6c26", + "0x733425c6dce3c7e520e507005c63fb5fecedb362a7957e88ec31aea6eebec74e", + "0x5405ed8a38b45eb7f26d83be6ca17deb884de6bbde143d2f96902b860bbdb42c", + "0xb54ea43023eea1d4f11b74e71bddda7645f486b70c9d121bc982443ac9843998", + "0xbc3aae97e0e465cd6c3bd4e57259fc89783a4adfe725110858b14b25b910d471", + "0x7275732deb80c2ab85f821b48b63029e8cffca17ff36e9e5ccc284ee922151e4", + "0x03ae14a0ea490e5d94ad6e5c49510182b0501c5fcfcb7883bdee4ea6fd5d4efa", + "0xcb2ce2c167665c93df9717222c08a3e0b82c9a352b66e8473abddcca3fad0b9f", + "0xe5dfa3773d9005da8afe3caa6b2f9252f3c4e69baa26faaf4847586caa1ab649", + "0xe17bb37cebbb81dc6fa9ecfcd52b6e90a79f906295254faf03ef37f19f854b22", + "0x7a838accf27cf1fc9be9f9c4a232ad55bb69cf2d11110c0d8b74744fed4a89ef", + "0xa6000eb2378431affd7b092829bf2d00c8e3292c37f61d9797270baf9b10107b", + "0x5efb875b27b3cfc97b5dfb1a3344c95ec71bb9785a27fbe67627069a8c1b24dd", + "0x07839ce3a682b522f02af343e6e3ef3bb9b796ec50632e7bc0ef8de69b717dc3", + "0xc938ec3fc73f12d45fccdcc833d3f4517b246e83cf26a0724d1a93b8cf109b38", + "0xbac3123807ab022e8ebb721a6a9ad79d6c6fd6c0394fd3518dac52e409b2ca9e", + "0x0874317b01ef77082bf17d1e85bcf58249d92459757333925a6c85a7cb1bfd0e", + "0xab9941934d9702b65d5e9729b0ffb1e23c08f73c9c04ff467beb778536770e3e", + "0xdf3bd9364da504a87811365e3540db6110599f1ec51be6f94da24d73454cf0b6", + "0xfa011e06b84a859be52f6d4bdca21dce0fb9fd9aa9d46b136178c92bfcd95125", + "0xa49e137e16873d13c5fae4fc3fbda02f73d909a1ebda6b127fb6e16041cd816e", + "0x093583f043650afd4dc2d53c266f6f71547bf01c0283c0012eed7aa61734abc2", + "0x1b3ee59d0f5605b45e6b438ff68637dc1cfce17522f588fa7d986718005b46eb", + "0x918a3f118ad704c433af73edc229c30b8bd6c51142bd4c7ab0f378b9e06fbcbd", + "0xa4d116470863bb3d25fc2e3336fe6c121637685256b21525d603d269a51d809e", + "0xb609ab862d1102c71c706e18c9e35b0328d63e6d0224e185aca3908e3b2a1531", + "0xfce53a2efd47d00a32324f473f010dec2311c85b6ff6d41f8b1e745e24ecc04e", + "0x7298a9d5fba5960c4ed58ec2a0a6dbdeb23577fabaebc297e8137ab998a16662", + "0x1f70425b8e76790a24601d1aa7b27031e43220052a7942d0a577f30d6f8ebf92", + "0x4c5db54704c0f26aa8b22734673ce3b0dfd87c99520219f837febce4fc22b674", + "0xcb8c7520c667e1624e5a61b71c5186243b48bd1d605b96a749b8c9d9fa76d2cd", + "0x845103e021186b915b280bc9ff1ab6cacc41432e538e39e499d4f1ad0ef95f8a", + "0x47ebee6d92ba12ca050b045adecb37f1db18c7cd4bfa999d29b54c7700374f69", + "0x5b055313bb5868d9871bef818cb1373b39fee5b8326fe63497d14ab403e44625", + "0x9d52e3e632038971a897acf26c4fe5b0e2d2c7dd01ab03bfb2c906e57b6c2999", + "0x8df4c7c182eb906c06ca35e5f59c8db6ab45086032ebdc62a0b60896b8607cd8", + "0xd283e5f936a979c75deee34bf1f480b54d4227ec74940b0682f1b779c20711f0", + "0x12f60e93db5817651b6dc385722b49dc8c198829d99152f7923f28f840115bf1", + "0xbb34bb125c8e74f4b44f0c5cd2c2951f2d1113aeff80e43684a6742825533aaa", + "0x84269e2caa7d917968ebc9dc51eb1fe7cc8b44f86f16ff00ccda6cdf9beb4a90", + "0xf9a9bc8e2c2f7a288ef4db8692ec10bbd214ef8d42e9a327c1409aac6d13d2aa", + "0x016e0c89123f72548276803241f2b8ac0816678c649215d62374d0a5324276aa", + "0x981a9426d0e80bc10d6a11b3e7e667e4806cff09b64415c85b79a0293082af62", + "0x5359cea505ea164433632efc9c760fb68eb43fd28ec3335ec9272bb7e7c268a4", + "0x094d87687f0ac1e05c97fa6819ac40e36e825ecbe4a4f52543d3a1a95bdac0b5", + "0x4254b27e0782967994b4b2fa41776bd0bbb897bc8a85bd7a81580929936dda7c", + "0xe72cdf7dd82e8b9bdbe2ca04e666968945e0b220a921e5aa9b74a26e480028de", + "0x23f6642d75c23afcf7bb8480bd1fd541a3ac5d35f9bb85a0be96d0aed5822853", + "0xe93feccace6d1fb90a558dd9bf2e1dc0aa77d3e92821cc0ca6fd70dfd1184152", + "0xb3956047dad5ac0b6e0b07542e0c62d9438c24e99520f7e2be783c7d8b1c1509", + "0x7483f8f3c58b38a7b472dc1e500f06320a3441056b2ee392eadb197f5c31349c", + "0xe66c920226ac1f0d1e3b70b68c811e9d4a7ab565802f7dc13640ad35d24c0d57", + "0x0602581860b3b1ef0a789e4e4d548ccd1cec1f8524bc6d661ec84af12aa4d0cc", + "0x07e168fd1d767b822101b25288d2615f99fb3753807ff583258b652a43cc3bf0", + "0xa81aba4e8053ff11fec40869b27a34ed96eacf3b11a22282e54ce90cdf1fabf3", + "0x18e164b2bfe3c2fe6b56ed7ecf42e0c5e0cbd7250480900a739b005cb7e8d5d0", + "0xa3585c69863d357ef2b1ca1b9b8649d286769db8b6479faef5d6a7652f935707", + "0x8dd3a9e0c16acfa9c87d75f5721e22204fa9e6d7b516fe0cdae5ad422977a7c6", + "0xbb439684c6264b8f4d31e4515a9fe07e386ed2a5ccdd8154fde0fe3ac5664cec", + "0xa8d5a07ac8961feb44965531a4e53107016a81e2705e7a7ab639505e7ae8b341", + "0xf24d80703f5619ec373b8d7948d45e412b7ae671c032a37bdf64faf0c2be250f", + "0x10b52564a2b39790b55cf858d34103be715a0eb80ff0eedc47bd22f576a7b2b8", + "0x051881a38c4c6a1b7c78085fb8f1a06e3336bcb9dd52a9aea5d101707f3a74fc", + "0xfffdd678439a2878fd14c8786e10cd976f50acf31cfc20173c6d64eee0aab6e9", + "0xe1f51990e32bbea1a9c9305bdee0b17e774aeef31f76731bc4c4f477d254d0a8", + "0x3514dfbeca6344913abdd38ddfab82d3a626f1a413117d8d41471b2c5d2e1129", + "0xdd93329d8f11b50159fdcfaf67ec16731f92c71e8413d5f3d7a8acb0606dc0ca", + "0x9c32b84c7449888a06b7af01d1569c2c9e35db36818cdf00eac25098a5823f9b", + "0xb3b1c2cbe06b418d4d4cec788d57638df7da295533ad6e6d7c75340b8bdcaa32", + "0xfd333ac3f05cf5cb4e9df23c14d7ed906883d03f681b7c1cf600733d0b5a54c5", + "0x85de7a8f19ba984035efbd9dfa87be913606bd04bb066090e0bccbf3fe9a6f3c", + "0xdfb6daa93d3ce55ee7bb02322827fbc42e7d8f484767710743269a9ace0baffe", + "0xd4f306fb23745d03035116d9037e676e9792984ed4ebc875e5d6a4932b344c8c", + "0xba49c04e27742e9da44417db8365d170197e3ddfe69170a5f714f81a1dc3bcce", + "0x293d6b83c8d324a8ced12f4941309c5649a06382d4fc688872e7b09c11fc137f", + "0x1a898313b1ed481e41b4d32ce1920542c2012b728a35df7db92f77a59b5a6fc9", + "0xe498282e24bcb8a0f5478c5a675121c5abb7de187702ff979fb0a03a5563af2b", + "0xf2df0c4067a369330d265a28161f31fa7955f8255e7e78283927a6fccc7bdf6b", + "0xc7ca538641c17f6f0a682ae43a17fab32eb697ce8d622c84f5c0da683102132b", + "0x01be5cec2a0aba78e01a8ef636229c39c03e0916d52e5f2e14948302c9767836", + "0x53736c939cac5b85ab63e8eac380b23604adc4dbe6fdf124b8fe8c73cd237229", + "0xdc25baed7cabde1333a5eacbbb2e0888e63abc19e525cb67ad7a4bc472610e6e", + "0x6603bc0cf384c75a11f9bbffce2a3305f02434952b90cd9d880fce66aa54159a", + "0x6603bc0cf384c75a11f9bbffce2a3305f02434952b90cd9d880fce66aa54159a", + "0x6534a48bf856535e77f01fd017e143fcfb72d85bae0e70db6bac920f124161d5", + "0x773c81a8fdf72fb07ecb1e648825ac9d7c518659af45b7de65108150c04d0b9c", + "0x0cf1b4fb9e2005e5bd442807dad2a59a44365385deda0e59ce6bae532e7eea8b", + "0x75ace782dad4401868758c8b50bde603c8b5adb5043cca6b79ff825c3da82cc2", + "0x92130cd9c1215e9852c4560118806119fde6693ec5a5dd065da8352a1cdbca0c", + "0x6df81cef581bc67409fe2f4cec7084c8cbe81154fb4d6ab806ebfef044b278b3", + "0xf11af97028c4398b9252de49191cc8bb8c6c1739e2f29e27919de94b260d249f", + "0x30b0d20f427ad8bd0f5320171c4e649796fa9adf0293b49603c0421a47c9636a", + "0x67916f6f9f469a3456513229023452246377c65cec17a0aeaedf14e7a644f7e0", + "0x325b78c1e43d47b2a353f894f3556a635d37a9ca050bb72dca3dd28759e02b56", + "0x33419283dfcb059ee95e96a4f924ee6c8a843dbfb71a70bdc1cf97e1241e2cce", + "0xcca3a91c1c98fe7feba60ca2009490d831ce0ecfe99a35c23bafa2113f84cc2c", + "0x6f01fca34eeee721f8504bc031d1e32c2c03df6d4ef003ee284a1b20902f9899", + "0x1d009e901a6bbdf5c60baa6c8d7c0e39b87c5598738983b2f8361eac0d8af050", + "0x5657af51832ed28bf5be30391dba2e6e4268bd222ad9df553d23ee5126f4ac1f", + "0x3dea1d6f9ee340b17f552aaa1dc327c7d275385575ec5721d33dc901eebe12ff", + "0xfa62f43b77601174be644483bf168e8ab16ac9743c2a23e183935656915e29f4", + "0x322b96ce870e3d9a3cffdca8eb040a81b22fb8cbe6f61810d6654577aaf603e3", + "0x26d143846789109d3b27c9742738477902a45766f337f25520c89f8429d6b1a5", + "0x99e6ee48e3352b96f2d33fbfc30c578127c1b6b818b3d72d2219da64c252fc60", + "0x73795495b29b2c1dba688c23a0d09e78671472d37834d02dd3fc4663d25ebf0e", + "0xf670715c3e33ff6820e1c8b411272655d6737ed50c9ca9f208c4f214a12f3040", + "0x5ce205a37a7ebf4812b348566cac883c981a7b4207ac769b6a1c722b823a0c32", + "0xd785ebe78f217522d6f87b770a2488cf33b65396f5b4962e62ef81a9892c5a35", + "0xa78342da029a78cd99d1e619679d206f01fbba437b9f2398f307f56e884a37f7", + "0xa78342da029a78cd99d1e619679d206f01fbba437b9f2398f307f56e884a37f7", + "0xeaeda796792d40e64669756504112241671801cdc9a6b4833e5e15a2e7872131", + "0x23296ff7c132e675816edf617f57b70f53112dd3086447678faa8f798e66e01e", + "0xc601e6c20355146e24a49cbd6aad10ce518ba10aae38288f2454110fa0d38fdf", + "0x5984d354c6b074524756fefaa19431c0074b4ab0906539cbe6a473bc6af34ce9", + "0xd59fe29348c43ee724a7d6099f26899f9fc5e104ca26b74111b3ab54831f8b6e", + "0x79a729948babcccac0b8eeba72eafb0868a175bede4248cba239901ed1c8ddd6", + "0x92b97484f6730f58583f5fa4293f04edef48ebf4c0ce4b73cb46d23760e0ad4b", + "0x4be359937b6c3731a1a025abffddd592aaa2c87b3bf4a7398cb5098aae9e799f", + "0x7cfa0410307642b0de61640fb06afb758ef5bd31ad2be470ffd85067c4dc5c9c", + "0xd4ba77838e63bcd377e72c99f4a24f4487268584778f80a1f31e6fd58b94c00c", + "0x614361470a8ab0e161a96cfdcc4379167add3a335705241a9eb7d5186a47d849", + "0x62578f574f5892b44f4bd24f81ba629c37dd288db4f6a709aa6fcf600690ed7d", + "0x81d08f4db3b60ed33f854a89ebfdb8b577f278d0cb304e12d916c6e3953c1d0c", + "0x525be98c456be4d36b48ad709d4395dfbace9ee7554463a765bc3d3100163374", + "0xfb976baac74ccd395fdf6ae695117b5d258037f2723854f9a3f1b6f9f87b38d5", + "0x33ed2ee9306bbdd3480e25ddef3187b33a93bdfc51181b50c640943d78dbdb86", + "0x4ceee07cbcc8371147d1266d6b1d1c1aedcd8dbad6dbe1027c48b06efcecf56f", + "0xa6a37b4cf46cd5b035456b1dbe36474089170917c94fef4fd05b1ad1c1f6baa8", + "0x83c82b0f014fd2b44ee717cfa5042ad01ae81ba715c090ce0224edd10e3dad4b", + "0x9de430dc5c240c82bf64025d4dad9f1c5390cf36c2a1aaef23d7c99e534bef6d", + "0x51d3f1d93d05b74b965ae93045db6cdea13c458f42b958afd17f69613ddf3c57", + "0x67114166453fc3b68b1094592555036d1ee551f77f8e97dfef1d57c4f4fd8ef2", + "0x19ac2aca96d7e7cfe9369731a059941c542cf75dc602ee54e2ae73d57d817179", + "0xcec8dc158cbf660b7b00a299f17b11ef9389c6b44622a0e81ee4b5d2d929017a", + "0x3db3603d820d85e76c78bc0b9b21243643951d2e56e3d5b80bc6ec22435689a2", + "0xb3f6951fc072041fc72ad9d7b8ecbe8bc42af26941058530fd32af228331f7f5", + "0xf70e4ceed5ed00f3065912de7e320fa287ae41b325b74e9d16d3773c57d104f4", + "0x0b3735a24d6980a576a04096d0efd6aefe5fb1a7e3a79d12b5f116afdae3bd7f", + "0xe9237029c6afa7e6cf7026e5a90be08dbd2c532565792d46ff7ffdc94c7d5709", + "0x4444e14c857ccc497bb597d5c2bf1d72d334f23a5eb3fdc6d281a70e829911e7", + "0x4e868963720372a30447814432b3e15c13a7f74f7ad6ae15a5ad4cf7aeb0ab78", + "0x003f38a042295d0eafa1022fa9bd0945a9b0c4663cb32ed01b42363e8653fca6", + "0x38431dec3942e276cfd80f4af4b5caefb1573b7f943dcf72b8deeccd06d2f0f4", + "0xc5a1ade1e26f024208f38ac80bda76532f6e6e653f169b1215954a46f6ad2476", + "0xa5586b55a35243470344fcf75d073b19826b2a5f90290bdc965ea80fedb3401a", + "0xe01a2f4c2f0c515ee8cfb2a0ab1f7f3252842b3973b04b325ab28a1e31d2a84f", + "0xb0c4c5e170525b6c0dd06c2caf1e986c770f328d1d57dcd5fb52a4e74ec7421a", + "0x79906cc3edf3eb78e696a2d68e8e158a7d95adf89ec44e8baca0d996ba3ab077", + "0x730a3c87fde4880ab6e7bf5e7ba33aacffb2e69042afea387b7905198c7ce333", + "0x6830e0b93f0b4dbbcf8e161b278686b848d03962dd5d6c5841a21df54e494d65", + "0x7cb99fe4d59afb826fd2ff42668cd2699fb5c397dd5e5bf59bb2247736f3517a", + "0x155cd22f4c64591f3436374c770a094bd38c0ce335ac8ecdfab7eb2c0d29e02f", + "0x71c868d660eedbf2084233a8e598d078d66ba1a8696ae6f4e0bf95a3fd8742eb", + "0xa91eee7324d92a1e4c5a0be257ae9a5cbb56164181360e8be47f077270c33ef0", + "0x2d91661ced896f84a7389c3bebfd232391b3d7caeaee3d6b9982facbe55f1b2e", + "0x022bcb42a4ee49cb7ded1147a8b358a170fe9f7167e1d63b5d1a4965b0e2cefd", + "0x4c8c878f8adb752d3a19fbeed39cc97566c860d13666dc8cd50bf4f3aabc65a9", + "0x61eb9458d428f38a50e724594d7f5fdae6d7a4e0904a7e18e4282bc4140d5234", + "0x8acadfd0c8d16aeabe9062c118f7cbea2870739852c086164248309b888d564e", + "0x0aa86416476257ca67589eaab43de5258974bd4c89d0f0fc6f6078af8c0b972a", + "0xd807cbd26d1f7f4bf22cf31910cc6943b558cadbff175e9b8bd0b67df6f2824a", + "0x5037a6aa5a55dc3b200e6e2cf28e2b65d1ee0af82419b36a0f54a3817b565d08", + "0x9f22a984821fa7345238c42bdabdaa76bc8f878b93c07855d91abcd063f88579", + "0x7001821fa3d032cb2fc500b3cfdf5c355a2ad6aefb42535cd963f601f188c1f8", + "0xee68db7918b2a17f5f6e57764037fe9558c5ab83913c0e1d5c6eff028b85aa93", + "0xe79000dc0ca807650a33c7e54134cbcd57528ab131b6ecc6d89b313688250c89", + "0x616632f836b1b529f5079ff2ab20373fbd35e3d1cfba7741c40e44f5adf0fbd6", + "0x992ceeccb557b7f25b83a6e1bed9e7caeacefd52732a94baa6a74377ad568ca7", + "0xe394b097a053b51834d6f92ce3e3f910841bc34678c79a53ed4fe0706e4c9595", + "0x30edf33a319973d45440e6277f2079652693144729ef1283fe16273dafe84d12", + "0x6125e13aab7ae53018d8c4a9010c3c704c4422c495760d4f65c69ac3d7f494de", + "0x41387d30078c163dd7eedd62f66d5e5d0d0e001465aa13581d3e7b4ea8a94ada", + "0xa198c8bbe4d24259d9babca57a2897c1223824457f2db28c99eab4962c316323", + "0xce172bbb3a189ca6bc74b1069611f490b3e8740df8fe68b295a85e3c7ae1680b", + "0xb08f2ab8b08d2101914556086f5c74e1ce1592b442b26036a290f02ff734f3f5", + "0x91386cedd2557650d18f1ea9857ffbad76367ac4a32c48398e7c9dc267c93ae9", + "0x6cb4dfc56e04de0be56c6486a508ab4617057d44114868bbb28dd3bb5be0a7d0", + "0x24960f2338b88100d423575957e98525b8916ecfbc4b1f80f5a74bcfe5f101fa", + "0x5acc1d6063b91ef764ef0fe8da2a342420c56111a72146c06e5a812dd44b092d", + "0xe2e657e157cfee2efa0b38837e951ee02f4d5232246b2aa68d81716559680f4c", + "0x4d11fce2658ba4d2bca9323024131fe62980be09761296c439fe4a3df4fb5e32", + "0x7d9d71a76188ac9b9dc3345864d16d5dbf3556009e9e61fc9100da37f0e5c5b8", + "0x8a1f0f87d88c39004b2eb9b2be6e2237608aed09977a693933034168dd8cb798", + "0x349b19f4547595a4237640c1c26dbee68210c551083228f957b5f88f6789fd10", + "0x6ecb05530bf4bb4801914c69f26223eb071066855e800b44998f8d237242fc65", + "0x818b71de4dfc54471f3161f834e4fa0f773be54b49bdf898a047a203ee387965", + "0x76baf9a125b10730fad80a9d68cc4291647ccf7162ea713822fd94ba73227a11", + "0x909186e91959ac784d63778b29fc7cdfe6994a8b6d8eee15826dffb48840ea41", + "0x4b7bf096f8d19ba6ba14626c842b599033d7af89e9db792e8a1bad01d812ccd4", + "0xb0d0c38a935b5f1391530c2e6ac1f7145004449a0f37f1b652021afc4f4a1cb6", + "0xf04de5d13ed564249a98c502ef6fb4343adbc423dad2d38d081470f372190e36", + "0x2fa992f309a376c9d8c6b7ee7d439f868d8cc9c131125c5745bf5d98117e0378", + "0xf367a34c08d5f9ac41d5dee8dbe06f1945697d9e39bfb0f20536c4e6c5219efd", + "0x1c5385e5011ef54aa2dbc1a65a54540a6387c2765f7dae0d12964e50c1ba585b", + "0xe61cd9fdab094e8eb75359f09588951401679478dd9b105ddaf6da53f03f65af", + "0x52d94f3597e988c4000be5c25eec223c3b81e24e8c19f114a8c042fdf09d8560", + "0x068da303f829941d7fbe5cdece33c8ed36517d592dec527493a6574c06501afc", + "0xe9f8f2044b464219e1f147285beff0de0b3737196a66735d6a7ab9f60ae186c7", + "0x00b5c9db939085a0cbdbfd6af0ba4d543c52aee83e73e6fdb627d3b6639ec2a2", + "0xa736655811616529736c08e19cb795981cc647f680891782ab7da3097305490b", + "0xf9fd19248a37d993e6cbbdac78987f2edcc2a19215a2d07b568245207fbcd0c3", + "0xc9e3c62282bfc5c33bf8af42e939487af7b58fa41b28e10bbe023081924477b4", + "0x6058e1f0aebe88583c366e1a904b159af17c530f095b9fe7fc939bc29073babd", + "0x5cbeb015eedf36070e7dd33491392218ec7eccafc4ca206f62e5729bcd9f6d7a", + "0x20a15d0a2ed2d66ef58c7305c5d3c20b033d8be56506b7d61b3621bededc6608", + "0xf9e88dc6585f81753495681c4317956384d56b8cd36a9c6ecad5555c84622dee", + "0x85f177eabd4cecd2ad65baadcdca6933c2a4f328019c008c9688de6b188de358", + "0x191b1f504dd4bf69f7fe9b0941b7428170c202956721be39957da79ac313e13d", + "0x191b1f504dd4bf69f7fe9b0941b7428170c202956721be39957da79ac313e13d", + "0x667761a3061f59fd68f1debce3685ce5aa01c9ca572b433d01f6cc9ce6ab019c", + "0x473fae1cb6f409fda96d3265c7ba62362052d619344bc0710cc58b6df743a78c", + "0xdbbb203afa08f34371b1f2c09cfafe1e7a8ff176af8897f03fff45609dd1466f", + "0xf64efeb665f4a82a5ef783c53496eeeea2efd58f10a3aab199dd459f159d8119", + "0xd41e37624b41f9ce23bdb377f16276a1f4fd46625882234837df7fb3697cc028", + "0x85c42161393ef825a723b8aedb77373411c55fb97ec391c907ea29eb9575a964", + "0x4ef5172b538eb13c3934073f62994ac1411dd5efab4130cfa510b73e817eed72", + "0xae1d3ec6d6773522b2cafb9f28ed4d697a43e3da4748f61c17e8d138c74054ce", + "0xb5b313c8f616f1ca218a2327ad1bb88d17b48115c2d5ebb1082a98ba888532fc", + "0x96408227022d26360205e9836c9c65c769ae6e5b788c71db4f71d3a4650089bb", + "0x89c30f973f08a2fb8e8e44257111b75c470d8918daedede739c915fd857b44ba", + "0x71d1e6c1f0086a3fa8704388f47f66c841b6ee2c552441cb3318156a29cd4709", + "0x4be153506fba71ab948a59a46c0ac6822c5dfd5054e342311037060203292bab", + "0xd8644d5ecd59c99ee9ff105934277619720d79e8af0f217e5b4c6bf3f5f2e6ad", + "0x612cd33c69bc8724ac55bacbc7c75a8df11c56e7a99e726c288da9b90f0d94b4", + "0x34abd763ba569514d5c3a08a53cdcc75e389601b9f2b226962da349cfc4d19a3", + "0x509c21eae723e55f31655e44e0e8e71167cf2df888ea5f39355c240ec4996af5", + "0x1b5d9af2edb93138f324abb5d57ed914b14efb8877c778c50b4c3fa15e202e6b", + "0x442e9bf82fe0abf5b48823c3001f151c8dae366e212ad97c55dbe320a73f652b", + "0x2e91f5be83a0151c34c39f85537fc1e828175dafc34448a2c3bfa08ebaab0b10", + "0x0ca61a60b2897f06e1ce40d9afc9c4d8f3623465d7cb53b5aea6ff1e52dbdb73", + "0x5a1c7e915ad42e409cd12cbbd1a8e29fcf950bf7ef24391fc1ec16f23acc3fa4", + "0xff83e199a267a58e3d8d28278aaacae073959408c28f2db6773e288e4effd7ca", + "0xb2a29b643d41ec5f9e317efb2020dbe43acb1cc8b322fe11a0711b81f5b96334", + "0xb2d9b6f60d6d0632fab621a36ba6eba4956915bd1ace2cd4feac3c1bda8b30fd", + "0xbf414e8d3241d3fb7d15b113e519dc5152e4a2ead9fad2ec267acc63517a134f", + "0xd7359955e88610ab676583356f78487d26c9d3263d128230747af3ce45148c20", + "0x7f7f7f0ef80bf5fe9b04654d456c4eae55264d84510726dd17b84a79514593aa", + "0x2c7d174506329e2ff4491833add27cf5d7309cb9301bdf71c3d9ead264e8f46c", + "0xe82654e125e16faa02cd40bdbd900c6cd36bce9855070194a577d5132f002fef", + "0x2d4c7621220e1441c0bef303bea6badcc93e68fcb89ce1a0fa67300310766a9f", + "0x123d13031b129029f41b9198e7cae4e58a5d137cdcb29771665544736fa4cf10", + "0x2bdd42a626aff0ca4d660833bb0d47f4a23a631ccd52b9c2c1ebf6f5750a011f", + "0x228831038959246c0b3c98530f40b1a87e7b3707852e97a3eb0d64ac9cb17c23", + "0xb180050571637c2a4164d05ef74b4afb6421de49f15bf143b92308cd434d21e6", + "0x7ae7db3e71c85d33d7317b7c8ddb74d99c2a0772492ee1c08774964c40a193a7", + "0x9ace88ed80ce3b1dd752cd43f9c363a75e981918291f31af6c2aff90d95dc635", + "0x75623ebf66c223f23042b68f696f3ad08f3dda1551ee7ec4729fdeb706e53066", + "0xf34628b1aae956efdec2c1c4183f20a4c499231ee42b0daab76e285a1ab1817c", + "0x698c5cf288bd2f144ee7f58a74873391cf97512421c0dee20032eb23ae186029", + "0x99867b3b458b6bc4de1757a48c9b55e68ba251cefb8c7f6659ca76180b19525f", + "0x87563f1b66f66d861c3e1ee3e813357e3b41a2b2f2d9ec51267351e2556886b1", + "0xfd54a9f340ddbf8eb1a511d78ff0c972c4a5eaf015d7e7e8299eaf9f4a379409", + "0x0a87a8b26b25a47a45f8d72caa742fc054b97be4f0751ed0526930fda7284c1c", + "0x45b3f6a7752ceb5f261f6fa4b6b67bc2679fcb71b1dcc3eee93a5f26eb5c53bb", + "0x2c8c2886b34e8fa4aa44df22474e7eb2d5faf4624c4d074550182dbaa0d09425", + "0x735ed085a4bf2259a7425c71f733afee7564316eae8e662f1269810deac37285", + "0x5a265b377f1cc282f5ba0cc1436d4a887f83d3c78722e238ccb6d9cbbf8d5906", + "0x72f936b76ec9abc3fcf90af6899965faf7449dd7793e5e664e53865d9bf3ad9f", + "0x6db50a2074bd8df778d2f8b3ecbc1ffa257d10379049807335e127581ce5c860", + "0x501ec96df840aadf09266718a92be930f57732ec3a97c1472f602e07f9a1d69a", + "0x5463d1d749ce3d272d72f2187be6f1b0c5512701c06e2b17e8496a7f051a5761", + "0x6d259b525ac1b7d234a61045b35f947e456a7f99f06843954291785437594cc1", + "0x6d259b525ac1b7d234a61045b35f947e456a7f99f06843954291785437594cc1", + "0xa0976f72e000b58c4b4d57e16fc1300d918d5c4cd1422bdf7c76909d32df0c15", + "0xeeecfde172674e4d6ea2c8c38446cb0db1e166247c944053715cc62357813827", + "0xa74e28619206d56093e9e18e4fda1bad600891a9175c28d3c92fcfaca533080e", + "0x52198722cab1615ca8ad87f5813473732eadb26da8c5ae36bc3eac6bc10cae8d", + "0xf088e2bd1956628b42bdc04487238190020e3b25e0af5e1c83f5ce4c9aff787f", + "0xab4df02b8ffaf58a8d0c6214bd1645c37a51767b59655e66bb8f84ab12d04348", + "0xa84d0da2e4473d984a69708f73bf18ce924527032cc47e5bc31df342c9ca0db0", + "0xa7bd36afe356c0281e5caac013450bf01b3ef19bae9525b861d75eefc4100828", + "0x221397e819d2846f20d6e1e9f3e6fcccd0ec5bbd3d33e4c55fb6faf588adbad7", + "0x0811c9d39132b91f14e55282c1d9fae23acfca464883f9a578dea5bbd05716fa", + "0xbaed8f30b833a1b0f972dae74f40795183648c241519f5dfcbaf9e69ba86c673", + "0x08f20801c8fc3b768f395e84de0108206109af09b1bba67851d5be784fa26887", + "0x294b0974cac1361d4ee14b7f8cc48b5b547e9c02ccb6ede44ae17f8409adbb61", + "0x053d86dc87018ddd5aa4a4cb7175ed5be14e84622e3b7bcbf44c74f4ef374199", + "0xc8229858ce4c606bc628485d77e65ee465a7f133d36081d01c80918a25d20fb5", + "0xe2da2ccf7f3eaa4db0a17ca31ced390a68ccac5d377fd0cdbda23b414f2eea21", + "0x924220bca211c1beb188ade4fa9a2d7bfe992a5a54e2e2d3cd7356337d80747b", + "0xb467a07fb0256cd0f8749f3be20e2033b4de40efd3cdca667adfa5d8ec195cd5", + "0x7c22ff0cb936be3eaaf84a8edf1216a2bbe64225c3fc52f3b496d9a6eada729c", + "0xe028b1d1516fefa68638cd71f8fcabc43dbce9243fa6164c88a85184c134bae9", + "0x7e812882930c6295afc18dcd4fcaf0265d62bf459188cffc952578db660b82f9", + "0x8afe0a5e42beaac8b1b4b62fbddc820fd74e5ef86b2b96c4adbc7c3bcb0d5bc2", + "0x81f4879e3931111d3a1e62a42a65ef8b997736c360590f5eeaa79aca4a157479", + "0x4f6ec3c6074311c42f7cf11d1237c9225b48f94fe11b3dd15dd56619c8cc0311", + "0x3d1f5450779ff445eee4deaff9cf94aa96ac5e25c81a3596b0db67c5bb94c961", + "0xf05f09737368dc5155383fc9812683ec50b2c34321b4d967e2515ccf5d8f4e7f", + "0xa297977b57a073b48cceddbd251624814ac3568a6e9689d6f7a2c7e4e2b25cdf", + "0x8cc02e1296eebb9675e8bbc3f2c5cd089ff810f2417194838077d45a720e66de", + "0x0956c9b7645d701b807d66b927bb10f1434a46085b81434c80880069681f09c9", + "0xe284d311c0b58ed9de0129bc11e15517d2df59c7b5446c627e6e2ace758591fd", + "0x998cd2ac29b956656da519c652034de72b54064c360b2d0e8f8e9d08826572c5", + "0x9fa7eecb6eeb6ec94f86fc266a4c6d00f793ccf542c1db15eee82853fc60766d", + "0xcc0c3811341d6009bb3ea1db1ff7f7fe089bbcbbc5568d7b0db7547427713d6b", + "0xa1ea11ced61ed129035435d765dd654307b8482019583149e74ce42bf5fd9bf8", + "0xea1065ff50d7330e1ae7cb3293fee010862842fc10d803ac819175fea73b660f", + "0xcf47a31d5474e962aaf1517ba8167450234b1e7a870c7ed49361e7011634b4b2", + "0xdd952b2ed6934a2dc6bc2a8deb61880e720e35795eb766bed472a980a42cdc0b", + "0xad5c628d5bd3a94f02f0fe6b7c24a8b8ed258e3ee178a26b658cdce3d65d2296", + "0xbaf6afa4790b6c0e74d82bf160d12f8ee689126aff3edf9cfb7faa2af8f5c040", + "0x41b7c6180487268d38e01d1623e2eb9c89961bf8f77e8a74595933397cf7a17d", + "0x8fdaf62c5a2c5e2bd603a24176341e8c009292f191f8191686ec977c43bfad75", + "0x0f81ddff64badc8c385b7a126265df4a6c329819970f85239c182e290f8b4757", + "0xb3bd1f19de2f9c4b2cc9aaf271737c7265bf76003a61c118576501cfe2c9e5e5", + "0xa17113acc48f97f9beff375e95fcf871c5ebdea6cd7ea0beb189511216e74f57", + "0xc3fb2b247a52f310269a77d4f3130bc19b1a30563c41a1cc904c6064c74928a2", + "0xc6b52975454b70fe3974b07315f584fc10fbd0a804c2af3bef5f64aca950abc9", + "0x49978c256182b2c630af9a2a6a74a4917a6d59f8badd7823fcf15f7dc999e7ca", + "0x72b31f5c9c44d273256e9c41192438a2e822d87fed9cd8d82ba53230319de42a", + "0x60052977f656f4a9ff80f855cc55555e086e81b0c4694670a80d30e2fd0e5ce2", + "0xd9a7b3499151ca25fe7573109208e0461a298455a98f230feca93c05872e5377", + "0x1adde965224dfcf5f4ee7ec090527279ecdb63af1b574049162692e75d9198d5", + "0x5a985174e52e6226405ea96ff4ec53444def349c6046c210872107fb4cf9ee2d", + "0xe9b55d51fc86f927cc03cdcc196b7b29015d8762c0d842c0b634c1be776bce9a", + "0xbabcdac9ed7a6cfbe53f75d86f6010c30381c227ef16ef460c0e60e00b58c109", + "0x4bddf57e33977c85beec5db671d4b1c927be28d2e9203a4dcc6a8cc9f6163946", + "0xdc6b8e760f393d2246111972e881bcaec03bbf430063750030e6e14336e34b55", + "0x8e3be0939ecb5b1cbab21be23089c581ed616163c7c39ba58e13d499b589d359", + "0xc10eb61a853998667e9da4b8ecdb03f8d6eaeb82ae15a88a2722a1e8b2a7eba5", + "0x328ee26b20e2269a8eee667c132f906a4f252f105a4418481cfa58eb229e400e", + "0x1d881a83c8c1725b7eb217e9131f6192d835ae6060549fc2bd81e210b1245d68", + "0xbc998ac06c324e4418afec28d5cdd35d9244a2f3bd3a63a240b851d5c2fc9c36", + "0xe981927aa917a2925af23a02ada0f9b7d1f78fbb57db4c63cb23279aabeaac50", + "0xfe08e96a332ef814cef8366db95dc4f882eef37b62350820e8bae13410124aa3", + "0x0b287b999956b945350acc60edcb355f7ab377e79c03d65fdbc747172a0ba2b7", + "0xa6febfeecb5d51b474ea1953ede6dad8308ae12ae3faeeb0a041f0592debd42f", + "0x371ce47bb5cd02b918637fc3fc0f4f193f4387ebe8f63e74cdd0ed33c3c23ab6", + "0xb58d7f946507b3eb7af6db8bad1a551b278e12243baae22209ac50f54d99ba15", + "0x38acda15dcb1a5d6f5d21f2aa20890c3e17c6d7595fb71dd64346d01305baa1b", + "0x2f3c75f4d99be43a1726fe5d2741f11037c174d8c8a00b0d287a213ffccffd66", + "0x5c3984badb6eb78154fa02c3c58470cb42336a055d25a664c19b522d6438f3e8", + "0xa70e09e5398ab574f38e849fa3fb784f751ffe4d41a754ecf4af6e5427aae154", + "0x96c5bd9fd6ffe4c9ea283894171749d8a822378e496a54d2fd26ba1d80df6d9b", + "0x0651a8737e1b8ec21119384771ecb45ab6bdf19c23716d088408d221364abf4b", + "0x1930350dbe8c23d58faf232e25b22f6a7dc7f8d370a1d5be2f4bc54ee1c4969c", + "0xd358f029bf2f2a358e63fd4dea68b567d2d4b4db6b5bcb292b45459fd9d4fc72", + "0xec2cd9ceede582270d3ff603f823b1edb9b7add3ec1448f14bc71fd95f3799fb", + "0xb1ec83227cde968a3381bf122d60f761feea36ef61a145db6f906b5d7dea93b7", + "0x32bdac79e2f875fdeb9f24e570492f415b747a54f523f09085285cc9d7b0c055", + "0x5863ef8cbe213e2ce98bfa938ed004148a9a586602d6bbb99f77ab46d4f65f3f", + "0xe2255015dcbaefbc77fd59813ad2c6404118b12d0fb801871a31a23c96c36fd7", + "0x549095a15c34c1a283697160ca2557cb4da61c0804fe2002533290d885aa808b", + "0x76577074af6ffca845a8dbd2b4a4f37a04276ec5224a090a88daae164c72e5aa", + "0xc7517a110f93c37c94d47bd11122587ffffee03c0fa9c25a777b15e00cbf93ba", + "0x42b35b38d8a7a4e85937ee110308fb3f232bbd15df154fcc8fac33f4b09f4c95", + "0xd2d7100f70c37f78c9fd915407b651c285316aa8efd2ce060f5927b3fcf9de96", + "0xdbabc930d7b53e7799b53a04462d1dd4783b178f89c52c5bdf19eb35a4e2ec46", + "0xbc3bda792750a12039ba227f07b351777afe13fe137068b5559cabe4b03ed27a", + "0x1529d6b5d5f310ca0914289e4addff8546e801b9e9da28e014d4bdd95e71388d", + "0x7c3806564e54374915d7c1db0130e58bf7b6346318ff38eb4d1abe2e0afc674c", + "0xe34b996e97a986e18c08d57ec9e4d9cfbcbeb9c17fa1ec9c8b5d8e202dd3c471", + "0x0e12e0b4bc3a7593567a4bf17ac858346c487fc95c8ffbf6e8f29d397cf97d42", + "0x267654ff9a45ee729ade8e0cb6ed08546b12c2ab461dbad857abf5d58c96cfa5", + "0x93b59315e7b1d5359c6d1f162df1386b5995993834f2397a30c353761eba8bd2", + "0xd78e10d665cfeed8982512cfa64065c1300bcab440ee9b4ba254f881d50051de", + "0x64b7eabe2a9df49fa7bd56ad2118942ef8dee763e83b4cbd008577e3d6146289", + "0xfcef1d2d713f7245bbd4e00404ce683051a515ebe331b098f137ba60dc342cf7", + "0x788465a9f9c3ce30d512154eb178e6642ff0ac12a895e871c81f0d05478c88e9", + "0x788465a9f9c3ce30d512154eb178e6642ff0ac12a895e871c81f0d05478c88e9", + "0x95d5131ee7137a071c31c413d22e1d9f44c64f9e53393dee845fcc6d429d716e", + "0xd5989a47f7f75af421274229c4822e5180b22c4e051822ab2bf3db671f6e088f", + "0x8e8d4ec2ba4633d3d02cf18c7830764ba7c39c24c113cb8b3cbb559337e74928", + "0x714b5d6ba0942420178cb48b5d711a3336f6b8b95e206f25d953139ccf332391", + "0x9b5c57f300774cc8204434337ac503b33887164bed5953d462967fdefcf419d3", + "0x53f11d536d9890b097f609aab32cc223ae994f6ff2fe75127b8a997822458298", + "0x75571cda4357730c00209cddb3cab48bb392fe0615ae66b7136c8e7a0108148e", + "0x2a49751addc0d54342a7e8b0493c08454c587610f7607fa735e75a0640fd0f89", + "0xe6defc6c616f1e37a2fd8ad125ed16ba17a797250aa4ec02a64ae82e894e39cb", + "0xa997d38a2954cce131e31a29cf2ee986eac773a43e085d5e0e7551d1a4cc6553", + "0xb7e9f01f7204a7ca3d0861a3a66d330c899d8f365c5584ed5e52acd4c6b125c9", + "0xb4f5aecd2b3828f27226a66928e2ecf3de3e98ff42c3cd4886bbfb9b2e26161e", + "0xec0437d7bcbc001aa2ceb4c20b14bc0588063f3877c482d0ef2c729281e91fb5", + "0x8dd9d987f26147b125deaf079a4271c7b91796e2a5c0e1cc639481aa4db9b5a4", + "0x3c9fa5cccc8c59ad76015d6f183ed359285dc2cca8cfc3a2563c12d3e04e64f1", + "0x89198dce375ce0e325105982b455a4dbe60e2421fcce099c529de69d2a9ef0d3", + "0x492b64f9eb8c8970e3fb28198000de35a06685a3afeabd4aba66d9e84d0a86d4", + "0x40656c943cf2b59b19f659f15b92c0442dcd0186b1c099804aca459b47bfab76", + "0x541164c1855b1bad8cd751c5aadd61d9c3750114d6c3e477cd729f1a5bdc2114", + "0x4f15b86fb38e0ab63e34247179291773dfe59d7479c44c6744116e8c35e68379", + "0x5d6ede304bf26aec7806ca925c8697dcba930abb01348c7540b1549f45276ccb", + "0x328e6ffd1441e58352f4822fad716e8c110690d5ff7b29282585c70d8c6cf1bc", + "0x0a233c84444d5587f2831bbd693e824e76439734af350a43610233b493697f07", + "0xbee6626428c31cf4713eafaa497cdb037df955cd45b4a771d544fc2589a08af4", + "0x0372cd7b1609c1221109b281d645e1d868692185a327a9dcba3a213afcf5abbf", + "0x5fad517005885ee0cfa0c30701c77299aebcf01323a6928fecf885165ba2ed96", + "0xdcf545d48d762bd880e59dd2d2992a0bacd3b1c7739f98472686889f22b2afb0", + "0x8fec471f400b1fece6daf837a831bb2686933b75de36549346bdfeb6cfa54c3f", + "0x09dda771f1cea5097103678d8e398042bb7e21d2a495b0a0a7210f3a40ab75a6", + "0x164bf145207bb7e1b625aa6594c54c6f1d1004aefe78a46b8d03cc28c756f476", + "0xd3deb1dace2e55f4223e9a0b4bd99588df4dc68ec093fd8250142b4992ea2af8", + "0x17247e43ecff3bdc26f2b0d0c46bf355a5b44f74477f9bef8ca3463de1b23b53", + "0x496145109a9dfcb3f9146f0b5f513328518354bdec62945db0ac6e59824d9036", + "0x6fb4b05501d10050df76484aa7ef774a2597fc807a5f5ea68968fb8d9371f8ed", + "0x3d46f05268c07f90b875ef0403352ddc55339850c2b6f27f26bafa40ee08a3d9", + "0x56162878b82825e41a4cdc7623511051dce4064f7ec153045181b339bbb42ebe", + "0xd7d01989cf454950a455591f14657894f2dfec1f6876e8f782970e31fa775990", + "0x6a1c6c987585dfcd681f690568464a4a0ec478a60d669ea85b2734674ca925a9", + "0xafdd973fe3066260295a6a412cc3c2f88b63a0e1029fceaf256a796dd3e619a7", + "0x1137208ce5603a3071d5fec6354f2e88e17dfd2f4092b0632fa7ea4c104732be", + "0x05b2a91506a60a883fb0b225dc0a1778684f8d141f4913b62953e4b0f644badd", + "0xbd63d5f13a2d5ff33eedc2182d112211c145493fb7610005f60125ca85a276da", + "0x1220c6882c22d5089ed1977b1b404451b1cddc381e9d3924b6098b87ba3f2b2c", + "0x4d55963c4b65555dbf80769d0628eaea888e0dac2032be0edb440fdeae8ddf1b", + "0x09d13a3e8d4f3c03b15414ef1f796ae6bb091e717f70c88f4284ba68b7c8bf65", + "0xceb92600f065096113e000bc558129f0e5b1ba1cc75c61ac4ac4deae70ca1ac9", + "0xca70397677b1a0b3945e1c479a6b4206991afe3da94ee5af1a89bff20728dc5e", + "0xc7cfe8b24c5273afc2cdf1cb698a6c249090d1437e642f8b92d808b272f0ef6d", + "0xd2c6838247f8ce190701cca92d9ef5fee3589bbf7dbd78aeed9f5996edbf09ba", + "0xb4a942b85d4de9869a6da0f488b768ac381f3d9b9fe2b08b57c44767f8f3d4df", + "0xd118cac3c6bfd6b369c0c934c50508a9581e733406bf14d609667db8984583e3", + "0x15bb7d1032a9edb11e358b19989a056d9d5727ee0132a8d0d2c43244c619695d", + "0x1285d77743de12b0030a0ae0f42d42cb1666fc6d20dcb65cc94f32501c20b6d4", + "0xd98f4ece7de7d66cde12cfb1896bcfc73bb6b6fd02a3d671fb6fa71b6bded31b", + "0x0f67a600210e2ef8c30ff085aec0a7291cf3e761eab60fbd69aabd4b55b7c3ae", + "0x9137e5a018309252b6b4b049d358f1e3eda3d845e3ea3f7d79063a895bdac6f9", + "0xcc45b487d6b145b4263e2bd0b7b544d203a8d8091bf2171d9f79f5af9edf1558", + "0x57a224828864019cbad40604eff008d2340a9357398df21d617488a4dda5c1c5", + "0x6b2e82d2e2271263c8d656254443654679149f23145ba5987cc0829737aa1052", + "0x760f32e1cb8ea1cebcc553ccbb615cd68cb5da02ebeac4ad79e19e2aad4cb561", + "0x2b20b36896155c535a1e57ee8b2be2915cad5c0c6728c177057d120526897405", + "0xb5ff9c0aed447875c4cc34167e194a57a55689d435d1a7a1a72e1474afda6497", + "0x795b9fde80de8d92f3e8f8e828007b06e227d1ed9efb5eb82beaa9c4b580710f", + "0x556e133f0d982260f9af6b6398f4270d3323b18c795f0ca94cd9d5cc76034cc8", + "0xf15a5d7324c6b0c234b58585a71a3d1bb9c828b3defcba5c458ba54d6e639748", + "0x0270d2b8c73d1d568286e60214a168d22ff41d89acd5137962f36d990d728914", + "0xc1d6c1c1ce177e0a9f92df971c1cf999839ab8c8c17351800eec6264fd8a64d0", + "0xd5f979addaa7d474597145f2360f203d0b8b762e298e9b87cf9e796765ca68ea", + "0xfe58a7b4bbfb2e0b0c1baf4397596334245673213cc9a59d801f92ac550f0f00", + "0x38c7a4add145f3626fac9e3d8f2930ab60c65cdc27e20f8877c73a7670bd7fc8", + "0x2c5f872dcff671a637af7928cef071a1fe3b7739fd9f073d28b34f7b17c5f674", + "0x04552289124ecfc2af9fadd5f04d93a919d11a10b189e24a8587267e7b944b0d", + "0xcdbd898f734e081e072d0516971d61abe99e1fca227e28b545fe4dda9ade7706", + "0x8c4c85a3be1e333de6f50a0491b7185a33f1bd0a9477a5246f7f58e86e3efabd", + "0x5ee71242efcc2dca052d36e987250f375e923abc7a229c8d10141aaeee22e9ac", + "0x0cb918fa528cf67d778976011a886b0b7f582e772a7d9699dc14a0a01544dddf", + "0x6bf13e9535abefd03a6f33bc715c86a68761d658bdd6a7148a3fc36bbd078837", + "0x5b8174eec4e58a598cd558c92dcb04c710c002691c6302d3b6fea0208f245ce9", + "0x54acb42084aa2735e27077b10699977f9a08691a82d7374c318dcda1d1ca569f", + "0x6fae56762b041b8423537650eeac4d09627f28dc5994fa3ceb01bd4960b12ae5", + "0x4ac8ca978b63059dd6a46ef9b3bc759ddb514ab4bdaab0247a559ded2516ff76", + "0x5c9423c67f384091e0454473a7a4be0f356565a461b7b661780b3040dc5a9206", + "0x2599f68d5827867dfcf44cf1ecfd85ab6c69793157b40c9beab46e4584b3c830", + "0x3f0cb2062a28b42b5bc18faf8b17e8a55fffe03571b75e0245e2e396941bb322", + "0x2fe25ba46ffd5fbd35ee2468b91ed866e0100d27423bb350f9b742659d20c2c1", + "0x393f570046348846a9786a59e9514255cb84ceee69b5ef676c16e15a1604fccf", + "0xd5fceb3ff27349a0f6d51be9fe437f84ffac17e8aab523b7c0946a73ac944df6", + "0xf7aefefd221c08aed93fc9ab0dba234ba289f0d84a270dc6cebf7f8c56198b98", + "0xce45419d5058ba2fbb5b0d4faa32d7979984bef6bc1abdae66ed090eeba27a2b", + "0xc07e683009e9808df7ca33f87fc29af706c252b2eb824c52bfdd843b0c6a8a74", + "0x8f122c58a148e318dd34f8da1dee34962cc5fa2cc2516882231216937e2c1b5d", + "0xf376ce339b071546070f509ed8f271f02cc27e697affbbdbb7d64dd1de57f1ca", + "0xb81d8a9c43fba8a05d189c066de0233fab0d30fa78abcedddaa1d401303ee602", + "0x12a6d805fce0bceca9412c8fd47b05ac2bc93121d488e482c0d30d20dbdd1989", + "0x4406159e8fbacf31e291620794efd5f14b79b63df5a16a1dfc5669569a885bad", + "0x729cfa459cdfef4678dfe345723ace8d508cfa8ceecf6c248cddc636b50b3e90", + "0xa327489bbbbb728487bfbc7c45d05da331ede3186bb480e594fcaedd81fcec13", + "0xa34096df3b076c0cedd1b7dbc8ce9e45b60df20279e5977298d4b3306e9704a4", + "0x93c752e2f287abcc1f8405f6edb3f9b3dc5881d2c01e81dcae5ed11b280e4a7b", + "0xce7eb85639c6829e15c7a994a1b6dcb3dd4af044b022671ff7e93fe15293771c", + "0xaad485fc8bbb92f6cdf29d067c557677e807fc76739b03ed5788b43b317efc98", + "0x9135a58dd645172db9ce660ceef6430e566175d4988af2175c7b682e373d5619", + "0x5a046e962a4724f29edb4e467236de0d508f8735671cbeec287d1a519f6326dc", + "0xddcd6b61779bfdcaf044215c66e78a911be4353d3eb49d9c44888297e1805bd4", + "0x26a4d0ef3976fd080ef58051e4fb2b0ad26be1006ae005210cb9c88c83e919f7", + "0xb7b9c501d8dd4869cde896c190ebaee52548857caadb4e4b1fd7014b4c41ee4a", + "0xcb0aa311f3f1286307f93a8a4deae88042a92ad2cf10752c446e8d60186b9785", + "0xa945cabfcd33f835d172ab9cae4b8fadcd870d3fc730f9acd498ec4a0c582b4b", + "0xcaceabc896178ac9649e5dac7486b5874b622fde32bfe782579c2bb95c79117d", + "0xf1418814fb55915800a0d5d1cc2fb787fb80e47469d39e04ba9a32fa11090e94", + "0x86ee7417275519d572d08b653401e1b89323a5816d11c6a6c40f86aacc2bf2b0", + "0x040ac57e96ccb2ba69707f1380ebd0cbe1d38e1f6f2aa78dc126e3bf1a3b420e", + "0xd38a6c48434cc498dcb08032111f135b4f578c51ce85a508532cbfd70160ea4c", + "0xa6e8ff7761c1dfa2311cf11fd7fd393b71c78577ee2ee3823db485dcdf4dd626", + "0xe762c4acd0e81da9207f3c49abc5c7cef95c1672cfec1fe09f1fbddd1788004f", + "0x8f726532b567e65969d8c46d6a2241c5faebe86ecc4f7752944327e8dede99cd", + "0x50b8031f2d5e65d5df19dca4c07b337275f73b5b02bd963d45cfbb11d940b0c4", + "0x693beb43bd15d7191824fa90a4a90bdb378841bb3206bf1744b924fe04bed790", + "0x553bb1175b737f4304196672de6e562b2d0de8175de0410d31ec3a23af827c4c", + "0x60d53e9c84da2683246e515c6bb78c6c161a98ad2bf1c5ea3a7b0c4048447995", + "0x045bfc5065a64bc23b310850682adde4362cb54b8c3818bdef8615f165dff5e7", + "0xc4af027ae4bc6c44f4aa6d7b70d534b678c3e4664a571386a9eda1e5ec70462d", + "0x3c2cb8f50335215c8c9d74cc0c79953ddeaa5f99275e921a133006f566cb2940", + "0x7659debff86ebd4d522bcfcf802c053dc74aee099f85d1d20631b163ddbaa7cf", + "0xb03eb145fc560e243a0fdf2ef3d3a4e07d78defac96ba98e6e93e7419a0696cb", + "0xf0f3722bb2fef3076792a3ab45b356052f0df39be7cd07e5ed520cb66daaad9f", + "0x3192f78df6cffca3b8342728f4a9ed93bad6a4ebf86904413f75c488c66083f2", + "0xf4ae6cc24b43f801b80fdaee0dd3d2bef16a6642176b17113b3bc37d1cec808d", + "0x0abf3c4a039c86b69b851d1ee86925eedc325a163fe6edc3ad95a04fa0b74b50", + "0x62790c6f8b80dccd4e51f6be8af6cff5a227c346e8f1ec97dedb1cdfdf9c8135", + "0x9dd4577a2168ffa7e139fb4177a43ed8e35bfd2c77196a2bd1caa217ff578ce9", + "0x06ddea386e82e18bc11bb9cc2f4844cc64d10375a08b865c16342351d0c36c8d", + "0x4cfff3fbee74a68da7528f7c3947dc369b03c3389383137cb4ab3768ae2670ec", + "0xeb8283876eefd12a766280d2cbe773c9b2bbedcf9d161634369b8d0762316288", + "0xdec6e9a0d885e808fa25bbf2af79d4d2b9d4220509112228ebc8641339840bd3", + "0xe81789b8555d060573e1c45eff21a12a2071c288d9033a4c45346b4d6eeb87dc", + "0xe5e3d83b62ee0830795dc7441db90b9668872b241ae5aefceff46a2063038764", + "0x0d5dcea789e829efafe6ce18156b6c7b88c62de6c4c4919a87e44f52f31805ce", + "0x8e841b50f481bc10ed04331fba49afb2c3270979c01bcd1c44161658d079470d", + "0xad4d2841a4fce57864c9e245ae8b1fd3a2fbeeb699132d7aa57ceb8d9ef0fd6a", + "0xba7ec29b0c865504ec22d30fd05270a058698528981c5f2600572f8a1f57c036", + "0x41b03f2b931ede2c9402bde382daab4c7b45c976ae2c03a3f501b4c9489dc547", + "0x0639484728d3c90560955a3da1f5c3d50fd79d8d727c49a1d53dc3c50109c59b", + "0x5e06827315656cc5b81b7c7d250cee607885b8e7f631e5e10c3dda6a8edcd8c5", + "0x484649c17396341704c55854961c9963f595e2b8a653b5c623cc832ff2902c8f", + "0xf35addbe06eb83151fb04ac67c67688642765e569c5f222db020ffcce442e242", + "0x3a72b7bd470b2d0395d2fbe87ab7beef3766cfc98d3c684bf518bcdc46833992", + "0x2a639493487e226110b5d32245e24a3566f5a85e797acdda241842e9c63a10cd", + "0xa0d6a83be0646557f0f4b12bfcf24b760ea97a041fe7b598dedc399d1577a7a0", + "0xe3e20103a662fd77be8496c39a432547d99e605d0a62c31644ce7f85a7c3e409", + "0xa7e1027c98b233f35cd59f38efd92064ef32f3144c2e02181faa2ec03cbdd982", + "0x83978a94a2a0943a7500d5aff02405e4372b75388192ead85c1966acdfa26459", + "0xa42ea9c439f6e6d7b67f36bbe8ddf75d3471342839e46d2edcf2c3d8cb72d0e3", + "0x9e3c7b26c96d2d3e966b26cee71e3f6632a524e11e9876368cf424881cb5f9e8", + "0xe3a57652c7a86dc70e466945377ee3c7f74b0ebbe6c452fb4eacd00436a38be7", + "0x5f4d160d361e6d619828906076389dac7818a050f2ab7914a57ee7a8e15e275c", + "0x4eb12cc0d6a54b5d333a074f04df092f51978a2bcc5e7cab30a9d5d51c2d9d2b", + "0xadb1eb0c571ba467d8a0c2dc302724f3fcafbcbeeb33362baf520df6ee116695", + "0xb964612d7fb4b845eee38433f64311e0b58960dc45d81c68ee6236308a092df3", + "0xd41a6f460d1c7b7cbc4eeb67707f7d03f421be2d6e0ec4e73d48503380a2f5ac", + "0x8f6546b563859556ebc0e7510a829fa9a4fd29164862e255ad9304b0c8726f8a", + "0x16012546df1c46f8336cfe529a3dce3014fbc443964eed2bf19f80f9d9c7981c", + "0xb3759900c24cdeb5aa27f2b7804c744a35b9ec12924a86c89e0b8e6a014ffb86", + "0xd4c6f1bf6bb81c21b80f2036b2b0eb9f8ea8daadbec11e67dc7e84ee0bf4d3b1", + "0x268681b1fd757bc3ac049a77917ef4ceb4aba855369b19c135d438cf93755ca5", + "0x16938d2a01a4dc37c8c63f21b2be538e85d1963ef66f8089c2c4939ae0438b36", + "0x841d06f24d9eb99a0e91aff97e272f4e0e700a0ed833a84b14c1283569687a14", + "0x7217eccf16d1ff1beb9e5cb42bacc76342bcf2181f1ccdb347119cdc804a1ac3", + "0x0eb25005d55973c06457d471c87ad2228427d812112bc4c702b363b3511d40e1", + "0x9bc02858bdead9061bb6a6289e81ffedece289276fdb1424ff4c44624fb207b3", + "0xa55796fb2896cd66d12abab1334158adc9aabc27049f5a60ae6aaa719b285f19", + "0xc2de44527a8de536800c3dba06c91ee10312a2adfb5205307d7066135465a0bf", + "0xa8ce1843e0f5a17273f1c48a0ea5f5d45951a45581a374fe1b693e9fbee27c70", + "0xc089515583901b3f1181a9121651d80f7f50014877083d133605b01420f0656c", + "0x897f8a3cfed6fba011502d08ca3f056e949bb932bfb509454715e29418c6d7bb", + "0x7ecd03b6adbe7f2866fbeb322f826aba85c86bc4b9c9c6e838ac2878b15841cc", + "0x8a1b965daed6d4bd4292f6498bf7802f63731d6a1ececad7263ec7e32af280b6", + "0x1049f5e948bdcb32b484916433f2436f31d48d9d82a33376692bf9277c5ba254", + "0xef213d1b39f4cbb585eb2eb8ef22692a5fabdf80d8b712ec9e49159730f37558", + "0xac404be67c30cb06c7d6a95d0f1263531817b336c528255920cccf34c383a557", + "0xef163f51f8c01837d52615a536aee5c7cb0bd6dc65f3d091276a3b257d66a631", + "0x5f5328f2587a280d893f615f521cf4c72483c19bdff0e2876325a561927786da", + "0xc061bb8a65efa05ca7c58ea524dead035709e04ae9ce54c718704ee9930c5691", + "0x7bc01a005767b0a81141e742c80ee14cfe50c914c37a6eb0e8324da7f61b80e4", + "0x339bc803d5eff8a561d4fccfd7013a6658d839ae15b80ef918e27c5c42a7a019", + "0xa8011de4f4068cee763ff28decee90a8ebb48110b2909755de3d4e725fa75ad9", + "0x79dffeafa709423c16b2d7a1a24fdf5d45d7fc39dd39b7e834b7e806e653c069", + "0xd5e9d52e645f3eb2d901d35400f588517a54e3b9fa7061bfae1e6fd0f7a8f4d6", + "0x68c18080f84bae57ab82a064e7fb8150010687d46fd8486110f2c6621cd0233a", + "0x318efe5316578e817e14cc1a61a84f6c774de62d6c5561a7e04b811d7a16f1ac", + "0x6144d813c70afebdb7189cef15b374642900895b12de79ce9e9b0f277909b701", + "0xebc309d1a58f07947a4e53ab9b3d112350ef8b01b48e304746e973c388177dec", + "0x83dbefec701cfaad69618c587f62f9b8320a6c33d3b4ab3dd14ae5281b31c219", + "0x9f00564355a68b601aa0016007a33acac437311577da6e4b433e84adfd5f16e0", + "0x5776f26c9142f4fb9f6dcbc8f9f1030c9aae66f4018623cea10c74487c94ea37", + "0x194e49121e0ec7f057846a842eaeec477c97f714111ad26b53a54b7dfebcf299", + "0x40e6cb24fa6939806addf979317708738676722821c10dd9ee1cd5238788f6ec", + "0x5e1981bfd161e53195a7fa398c89f3ea0c58d936ff36289157cfb8d5019e3671", + "0x4ef232dc817605a27a32da97585ba9df1598d135746802094038acb33daa56ae", + "0x3937c561400f2986fb10ac182d10c92f7b9e428cb4c50f0600e863c9e82b8b1a", + "0x17ab95b232370e9faf9f7cd338e80e45df3ed8d3ecdd94a0f63747a2e361d44a", + "0x199178407dd0d6d4981183328477fe3a947aad6209e630edd83a40914ba06fc2", + "0xb06a884e3e84d644f6f3dac98a5e0aa7db4994fc09d137b4c4629dd6663a0b4c", + "0xa424f4b2d658054ee01375a7f511a355bb1a7e805c5df97a3b5094402fb0b2d6", + "0x4d68b63bfef2dde7da605fb773b0e13788c77da84c86a478b2a72ad59838f90e", + "0x2e1a24f6262e501a657257b406f1332a10701420158b0f305b307eb8db50eea0", + "0xc308bd5fa0f14f1fca35e5caa76564272e56a8beb03fcacf57e405ba1f3619f9", + "0x8b1620017f72e91d0e9a1d25f00f5928d2499a070e15950942cd5f1373e5e8bd", + "0x2d5af10b10e576c67d4f95a3c16506bfba8bfc748897f9640f9b6058921cc31e", + "0xb5ee902ba9f4fb4cc7c575792b0a7982a9768b872ddb7a0aab20579bbca6a971", + "0x214e1afa39c01f9af1d4fa1f7747387e7244dd3d4b6f8294cb49cd4fa4651e0c", + "0xfd2ccc0bc2f64e6ccd4bedf1be431f4c08a4da34b01411205eda988b9f6c39f3", + "0xec7f5c2f3d14e5cbf1b005613887e05cfdd4dd5c034c7c5441b52db54b27009a", + "0xed6186c04935755c9f7a5f78d21b557be65c6f61741e9f5247001993c5c689e1", + "0x608fdb4909cd0e51e14efce9dd9375932a78920e9f7758665d88b80dd45c0e70", + "0x18f5d4a40b46c1db4fb370ca6c4459b176266e44e8a076efb75cc582b37acf45", + "0xf42c0c3242b0b03acd33bb474bd698f598d72024c8b0e426d60cd8294119ba6c", + "0x76898468e3027d9ecc6d42c3b858404e738f9906a6413b31e45a0408d6733ee3", + "0x1a16532a3c3554b1a28171a37af1868d8cde2911c07dd97858dc8de6d91369e2", + "0x92e390feee8027a424388685ef43ee4177525469475cb599afe3331269c30cf0", + "0x0c761478795b2add2bd7006d8f87db77ba3e63987660b6174aa94b8d30b07f39", + "0xca620b8cea8714e917be34dee25cbdaaf3f5892a61b5c5029d86deb85a27a231", + "0x0f39f2fecf5e81fb57018a083c9e25084eb5d943ae50024f21d80d1dc0d95650", + "0xee26ee8d781f38228a1566450828eee045e19e53c789c3674107b50a019325d6", + "0x3873672911c61c18892b634c5d47c7e6fdb5587b89535b26490cbcab752f6bab", + "0x9800f825515b10721909a3394b9d7c857e754d5f2f10b4e05c06b254c39b4e71", + "0xb3c041ee5d5a5ab481247665d863152b9ff539fc53bc1adfae89786e4a0fa41b", + "0xceee21817593670b2cdb28d1f4ece2de77a92f604850ca403a6e3ecf41a0dc07", + "0xacd740aee7ec0b0f7ec2433c67d624e9fbf2197e89b8efb44e98590db36f2693", + "0xf6f120a92259b4169dff615312a4631c1f4f66be0cbbd34d898d2b872724b327", + "0x82bb1af99e470036351e20509a05392a794c71b3d4779cecc5479d4d5a30c6d5", + "0x63c0c01140cd1f3014393fcb84fedc05f44505aee7083157346939a5f74b7117", + "0x366b6fca66a8c5f09d0ab888031f6fa3a54f0a516c59b364dde8c18ba12165d4", + "0xe7c87c5f4ee4b43fba3c7a84ac3af5fbf51593272d3beb1b315ce5a41c46f36c", + "0xfc04744ad378853e4ff398f91ebc489a29af6e6d51468c79947cc5685a619237", + "0x5cd242ebdfaa2d5e618378f9cf0b0473b7330efcd33870164945c714fd82049b", + "0x62142fc3496d78761d13a55943f1ea0c320d2765716ea96f0c03c255a53392b2", + "0xc668469ea945c8216462f7789f71ae65470362c6e9ec185fe899d95e8e0bfc76", + "0x17501d7f1df58201872467eeb48966540a7231ec6edafe6f9499bc5f018b6f32", + "0xbf18f4666ac225473a36b74bd8c22c037d333a7d4cfe37edf043539bbfb28aca", + "0x5ad88375e82627c4234295f6bd811d95607f20c3176ca71fc09fd1d3cf98dfee", + "0x091a4e00ab255a4f80942dcde75134d71812d02aa75a61957ca2e6922d156c22", + "0x9b0045ec6b632e0c7ed5d8f1b9ac6860bbc4b86080951ff214bfd55843c38c37", + "0x50bfe0c2d3afd8c3e976a3cf671dfdee76100f9b566fc7f3027b708d1114fd6e", + "0x8f6d539187a10d5afaf1cb775bf9f31d06c7420d9275361361f17cc6558fc431", + "0x296ca640b796e6f537ef8da17ea0fafdc907e30191fe2ca23d36642c545dcd19", + "0xdd707655f811b8a3f5b0cba5100675caf8a6b445671d6d038a2b847945bd2938", + "0xe58648ca23b17b6794265a04c5265c6d96bbbf30fb17242cb0c88c188dab3b63", + "0x40b34fc3868bd53fad42779c49f97b4ad9ef199f0cc1cbe0e2b152137246d737", + "0x7a5f05c1ddfa2b47b3f98c9169933e6b1bc43327a3f4aa68d20463d5117ec571", + "0x41bbcce504505c032a4cb950af528811352faa92d4fcc19a0c1283272d89a592", + "0xd1c186ffbf8d563eeb0b28c184b0caa88becba9a636420d85d6e85b6509e0602", + "0xb6550b0e311a6c3139001662a9d5c0874dc4b788af66886285dd99869ec597b9", + "0x28a6db3fd4cf31cfbeae5cc4d62d50f79a060d1359c0d99c1afdef825ec0ff2b", + "0xc33b53f0844c4e941687357ed5eb54e1c98999c4bc3c313c28489ef4ab7cf08d", + "0x174729d808fa1e1a7430e00d1006c09467e2bdff7b5fa8ed2992a4686d6c5d83", + "0x123404a0075b7f0ab71748961cab2e680d94c8fdda7be6a87a38587dcbdd0932", + "0x8e88906f2218938f9b772154305cdd84ed08af88ca5637019208fdc308b0dccb", + "0x83cca0a2b86ea5c2d5ddc421d50501d686d3644d7d583d75867c976bbbee5a92", + "0xec507a332860041bf99299130ff393fafcf5bc62acdf7f88bea0b99f684f6c17", + "0xc431f22439df7dec4ad42fe153c38b79f205e874a4e556e8b63f39ff0ddbcf81", + "0xe2b87cdffb327ac7ffcd7a298faa06dc01e3746c512e710f52fc403191d9797d", + "0xa9133be6635396aa69c389f809a173a6a91ad4f297e025a0f9a9736539f68ce4", + "0x9f983825ec3ab9674a2de7df2a32767f4b604c624a5db35817d817301f56105a", + "0x4c7ca85471858ba949a132f2a282472d40a0173b3f95c020cef103969850f706", + "0x97f7101f7790cdd49e1a6d57235db3f404607a40d1f1ce10d01f76ef45d0007c", + "0xca99d39fc0d0784dc0432c9420be5289d4aa0ac9ed77683547ddaa5d95531aef", + "0x20a5b22650919a9ac9190d6c6e6a854bc97031b786a68242a4e6ee318777f06c", + "0x40cb60a14ac8f173e371455f07fafbfa4c6443a727de0e9bd6cd63a9cb500288", + "0xdc0a4a7551c8d84910f74fd4aef91e3206c5f573f7f459ed1229f60c85023a2c", + "0x160c3e3eff72578249fedc120bdbf9e0d0f4705354829d5852829368659859e3", + "0xefbbc2c3518449f0a6d5be7062b6a947ecfef10ed1ef4de712a27eae9ba2c962", + "0x772277e5c28c02231416aa1c17e75da37d6e693495777cb20df89f0b68d33ef7", + "0xc7788bd0dbcfd54d27d6548194c4cd26a761b6b83831fc7031d720332986014b", + "0x7491998b12e5bd4437596ae8fa29f273edc2213c8d19b101df697ff14190fa58", + "0x68dad723111c1ba62b95175ce820001cae0923fa3fd5dddcff37232a0b80be71", + "0xcd475ee997f96987daf52bedac9b381ca6da255fd872770a6660b1e2cc54c407", + "0x50601641b40b976f04284fc7dbe2eaa7a89e42ba0930dd8714de4e72f8c8a39b", + "0xe552c1e165506206ba00753d1de4f5343415b1b62165893056a7ef4556174591", + "0xd3b0e9c5ef1f819d6e3a5c47e92db947fe3e3914ac4daf8dfa774be2f4ba5392", + "0xd51385ecc296883d30de08c92d764ce89a3a0ec29b60272c02362f5092fe352f", + "0xfbde6e3cba95e55058fb28f19a7edac1e5a640fd2368fce7c8e622c34f986e00", + "0xec5376221b522fc9d837d74a68e2bbe3ceb3095dab15c691335a5d354bca88a5", + "0x81b09a6eea106238dc65d8787cd0ae6eda83b192b041cdec006dfce47adf54a6", + "0x938b3027bea7511eadfd08d8759aa6da5df93c3ce03b5f9cf7e29d6879aac004", + "0xd86fc8fe612928c02cbd717b00f48ebfc884e0507e2fa2e9688ff7e653d06774", + "0xb7ade2ed344d7d5a48e195c00c417decfda056c83d63263cbb234fd361ef689f", + "0xc85d79dfff1f9eae8d63d1204ecfcf1a9aa39a9d3c5230ee442ff581e6d93f2a", + "0x114ff3d480a893a5b03962228e235fa1ff9f3c11210c8471197b19b83fd33911", + "0x0539cff07cc0dd1eab07c0f4711ba34c9907e92f72c24889a13b6e70a31462d8", + "0xe509007860792394df6f6d0a77ca32dba87b522e6541c48ee9ff3e6919fd45ac", + "0xcdcb7c84e677a146e0bbd4e1cdfca829499d4c99ed33b720ec1a58bc714ebf57", + "0x6b61550a3a3fefc5aa1117d758d6b32a1377f6f0d4738e5a6d584a816309d7ce", + "0xab5e88b614ad8dc0f79857fd4dbbd6dc12cbb21fcb36311f300c910520c2a752", + "0x3427c73ebc82936a7123231b5027f23ed669558c2fc597bdfa63e7692fa31e21", + "0x0ac5b741373a7dc6168172d90669d1445f3b94bf9a005ccd1db1fd4bf354e0f8", + "0x55677d2adf6a8a266b5f5e87ca272f7142601bb9369a365345a1c1fe514fad62", + "0xf77361d1edf60603fa9fdabf727f5a4649ebad02090a3157fe5264c4a94c1414", + "0x0bb697033ca4dab8222008314957d304460ee3279d03aaeb3b62505e06ffb94d", + "0x8457dada30d55a99d865298707cbd800c2703a5c51e10303981a952fad7a7de6", + "0x194728402f99335647b89479d4e7463e1308b2869aa4d4a33c7cb9e70f9a3fa6", + "0xbc5497dbcde5f77999708726fb217e22fd887bffa52956b357a6c63296b2a1ed", + "0xc564d44c8281cf0c3e750954f9182c35cfebb1b668e6868b8379040d056be228", + "0x441b279f73123018c4e1269c4867e4f3eb0ebd0d9af46c7ffaf11f66638c44c4", + "0xdda705b43ff60c0d79f4e6e843c6c90079c7c59ceb4bc95b45552938d6229113", + "0x2f9b96eb29011237e672d68f0b72c45d90805b7f9f4ef5800cf29bfffc217346", + "0x34c6f6f895be47c42a30171ded01c2db58bc0bc1e52e00aa99ba4e5a97adb88f", + "0xda23814c9b2304e1e0fc1ab09dd5dbfe33bf0c59906a9ecc9194dbe3d16ee703", + "0x311b558870f1f52e5eee490df3c12f944f235904d39e391ebf5a9bd69ee0761d", + "0x021d0457450326b0371ded8529433b0182858d6f8f6a69e94bdb1acca4e2e895", + "0x443753ca271ec1d55c8aa4893d076393ba4ba307fd3a794e9f33c17b29259d9b", + "0x7ff8b8a68170c7840013717dd325dd63a6975723e9a900211165ea0070c3354f", + "0x3064fb225509c5f71ced5762ae5e8713b0631c6bd89e5c2b250ecea41b47b2d7", + "0x9a20155dce84389e54d09ef66d570ca9e85c8b7bfce2dcea3b527e6e21437fbe", + "0x7ad625973e06bc3c0a04c50055e4702f770aa6b88ece55e90de06a3144b23c76", + "0x50b8d7a35186d13fe0a183c205d00094a7d18d2a232c82a2c935ea5b1e8ab3fb", + "0x8fa492d10baa095838dc03cf7d348fa1a707862edac0620376de4e96815db6a6", + "0xaabffb31c321373738d30f5d67030654f4db1a35f88226c115bd3466c6a1ba07", + "0x6c5c0a544c46cdd18376b5e7382a11c17a7b7c3fad33bdd16f1fc78dac5c00e2", + "0x00b589bb7df3264c64a1d7c7ce1a60ee9b3ca033df5a44ab30230eb3b4fafdb2", + "0x993130e2e589d0b9589386a6a3209e2bf63bc742181552b1e8c3fa379fee1758", + "0x68c90499307e40e7e7e6a65f6c9c5e5bb4009d2b08f0ba4356b6f25c447d089f", + "0x7645a35a369a2c5fd68e744c2c7afdf1cee1cc4e1a0a806fc3a05e89128e3377", + "0xa8efd1451c5134f9ffaa5b670db17e89377cbe25e003e8d99bcdce5077a45238", + "0xc06517194fb19fd8e542b5c636677e60dfe0f6a410f41e3ffc53002972320503", + "0x2d83ef3b556a83a16def8845d421b1a56e3c595328b66489ddbbd10dcc911a3d", + "0xbf40b4ad3b19f7abdce58e604443e2ab6a2cd3cf515173717c756eaa2925d834", + "0x54820640e06d5af47d16ff44604c509c50e10dcf9b1476920e0933557086ac8a", + "0xc9824ea22d26bf9469bca271d70edd4cbb6ca5453376321e3e76153ec08d8e9d", + "0xe3e29365722501c9e88305daac00f73a6b5abbaf4330e5c1b01923b83f7c305f", + "0xc24a75357523ffbeb5782fd13193d87bb1258218724f99fbbc16327d0f00caa1", + "0x07b6bf4a45ea0fffff0ca0a0613371dfaf87bf1ad672ac88fa0eba454ecc58e0", + "0xb5344a85ea3d7316da49c51839c192a484ad5cfe7ef8d2315df5a96ae313444c", + "0x8b2e1e3bafb585f527a6694766ca8e5c56460c2fcf5f203d7381c269686790f0", + "0xf212233f65201290e9a216d2acaf5a1eb7bbd831feddd63c160c2f79b5e8d20b", + "0x187418b60e8d40c6e5ea9b611d0dc711e736f51c02cf836321b0f73884ded6f5", + "0x82f90a8629dd8eabc6738048eacfce6e100fb7978c661afeb5a2912cdbff0f6e", + "0x09e4d59d85abe33034a84caeff30c0549b0b11715fdd6d8d6b06995f94b451d5", + "0xabb040da28c648993160759c93247827fc4478b5a6a419eb5df8408ce61db9fc", + "0x9831d8bcc1be2d44e356ce993802dd67e1d90b0e93a20192613e6f7ff5aac109", + "0xaa2660f92959905da73a377d3947117061fc22a37ac4ad1b05362b34983b8de1", + "0xa170033b7fdeac44bc09b540bfd77e50c1ee59ac876ba34030f37b073043dc36", + "0x837fd9223601f0fe316c84f1859f6ae0cb50c8e930eddb3a61b1086bf288b8b7", + "0x0d9606943ea8c085daed4c9b474174d1d5bddf698606f9bfdc7ff09ea882b72f", + "0x0ce19ad4665a756ca526f79247a84b4cdfbd94a5d768c16c4e78e0f967d70b55", + "0x5b50da674fadc5a39e719c1c273f82d14146da365f28f2ee075168d7d8967411", + "0x7580e711c80c9836fc73e1f320ed64027afa552ca201ba5c5a62cb51c730eff8", + "0x60ecb141ee78734d6be0296889836128763af493f386bc4037ddf925dc8d6d31", + "0x62aab0e8d94d0111483cb44d1421455eb46f5a4105a7b1c8547d16d41e148b7b", + "0xd2d2a874eac2084cca95d905c226e9adadba68721a3ef4b6514580c31bd028c4", + "0x39fc07b6b902744200d5367d3a0b6aa4fdcc26a8888267b92c24050cef357750", + "0x2da0aeb7251f65073a510db96f22d9d063da6bf5cc3820953c2d286ccc21060f", + "0x0fc273c17f825553835a753885c3fbf6908f82b29deb89904dcbea8794b348ad", + "0xd09a835daf89c1872b85da551e2c5196b963b9929fa492642b46558a230730dd", + "0xbc88f73af08e8a6a4800cade95c68c1fb11058f76326fca11c5fab4cc942ab13", + "0x8272e4df5c09e8661abe4c39e42eea6063d4525369df961bc90afb4a0c16d50b", + "0xc07050d08c383a398f08fa553405e2304bd630380ea2557dc07345d5ca3466e1", + "0x15d7f81a0088c5e13b0819be2e00a56031d193d2d0d831e4718e8d92d3747690", + "0xe15a1a270736f0ab02e6c0bd47b30e2a219faf654db3c628e26cf7a2b370ce93", + "0xa5025713c43406548f87164c1457cc45ce09b18dce3be0332ab63e4521152f51", + "0xf9ed741de2147b3d0b05910d1f701b2d28d51da10f5393108ad6ae32cb420c18", + "0xf012e25eb6f578ab90438967b8844c562fde59a9c51000856afd605c38450b0c", + "0x80a7c6d038a1b06d5c7104757b65cf6e576c41bf23837053b732fe0ab7225b3e", + "0x8e2c3dde3e3bd4199b05ecad08ff8414cf798db87cdccdc3049d8ca44cdff621", + "0x8a03aff4778f0ff8a3198778ef264df56e6ac0b74d35bd2db796e6a1ed602a23", + "0xd793469b266bc975d8505d6a5ed6956824915650f3b1e0bb0e21bc04a6af823a", + "0xa8cb9dc9ba64f0e0e9f0cd4aa44b5dbed0c4ac2924073696ee71eb8ca9999422", + "0x313d43445eb120fa9bfc4da2c213cc331ee7b705ea3875b5c972b9f3d546b54d", + "0x46866060f6e94ee6d9930d5d81d33c7dd4ce9a456c81d7dd5dd7252050c665f8", + "0x1b258cd4908c29148c0bc8b3b70ed0207e6283438401d0df24f030a5b578eb49", + "0x7ccca3189abb76d9f47e0f696269a04113487f730ad5c0d18606d134b24191e1", + "0x2de2c03873ce6cfa7d585859ebd284b5dbecd7199c6211acd9fda75c9d9f1e18", + "0xaf0e381fc13cb9af6702b122a9890527bc26d4b61ac192ce1613ac5040426c74", + "0x4cfbf2a1581450fbfeb8beb4d9eef44899a28ba52b527f4ed52bd84a4b195e23", + "0xdc1f85e55d05c394aad1f767b1b124dd6c2f2bf0990ee15dfad1bd929e7853f6", + "0xecb38710e3f59617062c881843574e3dce2a9b72cdf9c9fc270d477f841b7703", + "0xd98a2999f15395cfa95ec6c59dbedef4470a3304eae05d0784badef622150cdd", + "0x97a0c6aecb30e8181fcb1ec4252412786e7d00750d94a6297084c797f3e69a06", + "0x5e7b2629ac4ef2731348456b85c87afea12d0457389fbb50ebca1db5744a9455", + "0x9cd045b8eb588e727769146d0cd31b47847801896bfddd71c3b48f47b90a887d", + "0x3c409da50ad3cf32ea2344b053ea86c023d1d10643a965030d0c3e3cfb689c5f", + "0x91456eb422b12a5ca48e93b8c8fa20fae335dda0c180569cccfd6c74c9210a69", + "0x90b79abf4510ae5ce779d6f8bdc850328297a1862c01a5958dbaafc01cc0dda5", + "0x07f2c6c7a2bf37fb8245b2424c98ae96ea853d8120094ac7ba56e07019f290ef", + "0xbe4e32419095d83d7f220b1d9019794233c1731c3ff91251895bcd88ddbd1464", + "0x110ecdbdb0b4bd67eb1de6dbe1bbb6e100d590678c909d4649fd37079c3096ef", + "0x4ac4e721f592fc08b2daa647f887093ea764a9039b7e43a64a9cc7458ec32fee", + "0x894f0685a4af7c3165a1ea207093e49531003903246872a6e8c117c548042dde", + "0x918d0bdca1a5651224b19c85e6b8d7d34db184e01ac7271d9470ee01d41ad0e7", + "0xc36b045d007f2210a60aef962c6984f5dbcbc6025dea55d87eac2ac13d1006b2", + "0x2dca313cfed13a575abb416ef088ea46fa2811b2bce59a033b09df1ef0ca30c0", + "0x74532031df8cb2aef27ed91e6c789a141159baf3c553631fc71fb12bd1ee57a7", + "0xb4ee6a428af668e73e3d050fb09bc02068ac341ba212bac853c735854a9cdd80", + "0xf83f72030fdac23bab377b4d9df74e136565063d806afb411146c53462847ad9", + "0x575c304f6666038d303257e92c4020edbc1dcdbfa1693ac1025005fe0f18e41f", + "0x1bf3a3a10ad667780f4edcedd7b8ec76fd249418f9cf62f13a33ae401cfa84e0", + "0x12fb511ab74ae3094cb9967ae873b93c4470c4a1229bfadc41605901d440b124", + "0x4d4c04fe0523ecbf57a23e28c87f948c8f640dec81ae2b3ff84cfdcaebca672a", + "0xdc7e76c0554f49f0e56dbebd48899374bbc4af2d2041ed2419507a96e2967c2b", + "0xff12b6d050377315c67320f6ac5885b69cb2ed546c266ffc1dba7e93e726667c", + "0x21e8f54575d16ab36fbfd72d4772b90573d23a987be533ae0c97b4aec07fc5e0", + "0xe833a03a75d26449b7dc8acd14caf8f5c38f118b768e19800a4cd22dd05a5b20", + "0xd7cac860a2311a9a79696602c2302c80366dfac740c8911bf2a5e3189d9b0abf", + "0x9142b826e3de3854704de66be102e5d031387645902b5b2589b55025ab79be36", + "0xd3a88d7e29dc6b41275e60d79b9abd51ca6a7b23365ea90c37c572d1fd9ca020", + "0xcbad879ac65fbe7468aef304dbcc5d8469e06ef947b1bf6a40e9ce3b4b3e5bc2", + "0xdc30f8af0cb96f2edd51ddda74815d0fc531ece43daab3f296beec70481ec267", + "0x6a607aa7617b3cf004a68b69f3d42ed7c3d1360e86d414e0aa7bd73da4e96cdd", + "0xf894c4d48d697a2399004e994bc30e695ae0f8ca203275ed2cc76b3a8536b15e", + "0x6ebea0a5256a58a2a98e446ffd569414d5edb4e521afb8444c5245a8e1903116", + "0x3387fc2eadf8a298bb27e66384971e4ea905e71e38cff9e1b63e5c7114c496d2", + "0x2fb0f1f7542894952f19c0a72e82056a0af833d43ec9a7573e916c24050326e0", + "0x497ef67d16b2f67e7ebe35bd0ae492b149b9a74771871df328032e6db5af1f80", + "0x7824971cfbdede221e1a7e9f93517c43d4550fa3058afde286948c356e503685", + "0xcf6deb5600abd8651ade6a098d684da0ff6bceb071d7191c15b503f22b316dee", + "0x030c5888a0e4c5ad084491834b9edd252e63c49a903f2baca8c33d82e85b78c0", + "0x82a497f3c5f39be92b536e40a4e800c4da62c5e5929b8280b40833a09b318cc1", + "0x1075bcfe955dfafb37a08ddfecf2b9ff4edc967ddb9af8cb277b7797295105f0", + "0xd4918dc5b839cd2c4cc1aaff18bf398c7719e47097ef4262a787a8fde5761bac", + "0xaec2acc4ef5f492d6dc951243f304e935441ce72448a47c63d2ea50960263641", + "0xc91489d55adc60283aabe20905865e1e5e67295e5c7e76ebb12c1f7657fff48a", + "0xd10bbdced22db3c810e226acc3de713f7e1bd032a6eb56cc328541bfd0448f33", + "0x69f8fe6be9227d88c9d3917cb421bd44ff6dfc96ee20b29dd89a0dd3f7ded978", + "0x36332879565f293f41d34df73698e019ae95613235d770ceb84fc45ab7551b85", + "0xacccd55513f9351d1c9a59a67fe13c7fb67535c6847b342497ac6548a73ce6b0", + "0xd99ed45bb1b8792895a29f7fa7a0bcff63bb6042e0381d30267e25ef24e91840", + "0xcacfdddf3ce77dd348fe5c436a843165d3ec1e502d20ab8c5da719fadcc161ff", + "0x3be2796ba652bea101b7f79f883a3ade34dc1455f645b6508f520e3d82fffd5e", + "0x145b4ffc3312cfc7baf1c64be9cf4392e68532b98eb7c388f04f57771b44b050", + "0xcfba7db1bbeb9d7d98ba01ad4ca69bbf98f16a3886f9c106cd00efaafdbde583", + "0xb508131f9f507f744004094ce61628b1e944dae9518373a063828aaa80c3cf1c", + "0x040c5762ded828fe78d6923ce3468dc3a82c8ecf3ae0ed97c9ee3424cd6db94a", + "0xf347a61462cdcd1544c2c4ad097182444ee4467a51c84561a4fc88b0a05bf383", + "0x53c650bd3dad84ae3a1679817183ebef2b954987af78850c978856d2ff29452b", + "0x63111421573b8e28f3e7d886a7ff86fecc66ab6e5efba8e06abceaf89880fc76", + "0x5bb0b9ed995b5e54f59f7c3a38c84a0750e5b9bfb8ecb788f376a544c0fa3ddd", + "0xda4978ca788e0a9fdf5f19e5ccd73c1774f8da9796590089a5d8a4ffd25f1078", + "0x4d23c2c9b9050e1ea84bcaa9fc7ab4bac7bad6fc3eb711b7299ff4f1baf8c2dd", + "0x382c452c375a60fcd68936ac32afe4ef4c3e21a72fe4970ded1bf4d225c0ab96", + "0x5adee3ff5ad2d109611eb39fdbba6bbd5a827f640175377485dfc7e2c9707c90", + "0x0454680c9ca286dea83faadf09532950517ce9f3f5cea417445c82b6056bad43", + "0x0273bd8f865427c652d78cff403b1b8f658451bec1013e9998657e81efd6784e", + "0x808a583d06b27519698027a50dc258b468ad291a7bc9d7ca4ebfdbfc1536fa54", + "0xa132980d40093686d29db95aa061e2d2178b4a12a5c5ccb9464b726ecad58015", + "0x8dbea4c7364b888c8c2f00545fd7709c87e365fa0f383cab669ddc75307d96de", + "0x32f0a703f6193031f096712f82c552fa9062311093ee31307ca3d4e7315517bd", + "0x580139b044bc9c85665434bdb560621e5c5e83a47ed8598d9d8f8bbccc75bd88", + "0xb1c862d65ca05f16fc301f9347344a36ce9915bf277dc3a6589db30e6a369539", + "0xb1c862d65ca05f16fc301f9347344a36ce9915bf277dc3a6589db30e6a369539", + "0x222f7d5b4e61aab860dbe736e1817d2c0d8c8617f5e67caeefada4f0af895285", + "0xbad7b94031f74c1b95b49534906a46a7f56a1b9cb29494550e8c41565d684e78", + "0x19a9baf038ab3e830645c9cd5b0dfcce1d028e7856425daef92c84c40978b2ee", + "0x0b5452ae1346335593171bb91048e36482cc02adcc78c98d43f1227a5a0aa8d5", + "0x187f1f6fe7fa320c0090635947ff3a9836c40fa44e88695c84495c5e57385f6a", + "0x21f15d9ce461f37698dc4348c87ee9edce7638b687d3e70def8d9e5ec264741a", + "0xce30d91b5caac930f735e6f5a1add35c7283d5f0b43a243c65dce9324a381880", + "0x08a21b8626ff907f109950320ead7613938e0abafd6519c411ca32d939e70755", + "0x754f07c5623bbb553e864b2eef0642f5ab14f34b347369039992e410530781f1", + "0xdd0424a475777755dbf52aff87dc96e52619ff3f5933bbcd173280d55492e9f0", + "0xb43f4f4ebf783c726f473b95f4a4fad43047b897f1a9b38d3b5e30faff323cfa", + "0x6da62aa153101c837873f6ca9c05cb009c44a36c07402ea1b8281e6014993c65", + "0x0945d5f02a2b7cba65a338e5082fd0d3215df401204f75de8d8ec78c907eabbf", + "0xb2160cf025c5f975b0107fcfb2abe454e0d17ff4cf38c1f4732196c0ef38220d", + "0x56af8f670d528bb7323a0ac7559c25fc6101bf700b132ad8a60841f7cfdcfbce", + "0xf909dbf70e4bfb1ed07e7c7b155b43be38a7ada612c19ae9f91bf1a19570efe7", + "0xaf27dcf77c2debf174d44c59b89d08f197ad22bed769e902dd0e907d7a32ff5c", + "0x1f1673472edf4c840afef0f4491a0ba23d2dd71ffb52e148384f8759a95e6974", + "0x3442dae9d9e8b50b0bfe00720499207dbaf10df6c5b92de895ce25f379d54446", + "0xe51dbe7ed4d6e1446024bcf85ac191baef36cd390e07e8a6e37ed090e3449698", + "0xf204f2508406f606fbe123d85436ace31ca31b367381984816a34c939e868631", + "0xf59874b8a7f54d7c2f66d4f90f4ae6eedb2263aeb3f215898a97ae8af91c7653", + "0x8b67b6311ed03f1c1ee108cbf18d51e3d7b374c172dc1a1f9188b97a1a0ef45c", + "0x79d0f5e85c50ea7d28649c02d8140d45b70aa45ec568aded4772799cb5bf26e4", + "0x70ecaad5f86d3823c9dbf989b5d9b0ea560825e0ca9b71e3ceb54beff1e9d4b8", + "0xb603d83178a25bffca2c191acae07fb68032c0ab3be40aad93c98da9b6fc52ba", + "0xac8293118dc0c202c77441042ad089d0233e90dd18ddb7defc88a4f518c08575", + "0x4d53e5f57fbabc0850b2d978f8f964ce6d23f578f4c19f7fcd93915751aa1ebb", + "0xd41f250ddd4acb56e06705060966589a2e21ba13ee1752728f1c828d82a34978", + "0xa46e66abaeeb0d0f5c628db7470b4ed1e97afc265ecfde08f87b95e90bdc30d9", + "0xdeaf0a1165adc40f1ab5c6f9e57ed9cebd66b4a45b7ef26ec091bb6b21f83fc3", + "0x0d770140dfa5d5eb89ef9bd85275189332fefe60f644a254492bca120cf98875", + "0x293354d6f427c04d587c4e35896465b877b63e27d9e18e4eb845dec1bee574c3", + "0x39a847dd36df4948002c2a7f21a6b4ecd58edf3abc93345e37018ef83b835d87", + "0xb3f0ff6e84b7dc4f71a20181cb485d21c938df58340243c07937b8c0228bba07", + "0xfc3e14eeb96a56d5b7c2d2f18ea72cb435efa0171c50ad41e0201bfb78f21cd0", + "0xb563da13b4e202e98e506d7bc480edd18401818a4d138f326238b7bd405779e7", + "0x7719c00d5f0c2740a3563475cf704e985a9805360ce939d68116d29584c58175", + "0xcdbbde4f1b47fa684a10c7d3407309d2b3053cf57ab8c79343847b1423ef9a52", + "0x9a0c27f618f9ba456e73d96615a875c20d5375e6a9d16c3857bbdb80ae78e69e", + "0xb313ccb4b8190583dcfcc97de21a4e81bea72febb734f40a5b3eceff6f816c64", + "0x978cee015a0f91627addf20a2420746ab1781436989f02addf90204428b5a6f1", + "0x736f409392ec5ba58bbfe31f724703665a928e9d4d882af2958f6e27ad7692ae", + "0x607aee91de6eac40a1f724f94e24aa7907493983b077db79b695e6c5813090f9", + "0x0c864d64cc397a223840c33de5675475a6d347b3f7c5ce330f89208dbbd9498f", + "0x3af78a64c494a9b538e0133d9185b0fddf2a6af05835097b4437e4a9c981ae89", + "0x0e52187fa1795bb90f5f4273c53778575cda35cb684a229d6be8b830259b5902", + "0xfd17057b128f8d286e5e7cde3d259c21907693c38cdaf09b279860732a510491", + "0xb5d5b1d004d77513b546bbdf54d285e7a69302a75d2517bc0d789ea947c28a66", + "0x759259ce785bda5b8d1286db3ce4ff46e253f692c70091b0c4d1117baaf727d7", + "0x665d0250c385b68c84db2584a3136a903d4dcade3eeb9d649105434a13676406", + "0x3f5aa9a16209dffbb49544cb0b34a7f1cd0a74f9b89c2065472fe7323b08c55c", + "0x7694b0c9a0317227d45e88094b222dd104f8b5fc95ae9b64900247cc3ad0c834", + "0x6542887f967328c8d7602d6fa68d849eaa038d3f21502202d2fa72a9343ae5d6", + "0x2845d60bfc46254c6ef497d61937deb33a6e918eaa285ea9cc382a9f4f7f0a50", + "0x957b041b048bbe0c208d45e4e3868a2ee8bb3e1fb4914aa8d9ab3248f8db5b25", + "0xb5ee10a65c2258c3e9e6e2c964312ee03522c024bde1f498e74279c0e7c8a43e", + "0xc70c97ba11c9c87f8e3874f7626cee6c04f0f11833dbd36e2bd971495831015f", + "0x8d75e2d03c43c71bf2b9e8f79c25d5d8f6e6303bbf10cbce0812ee9c7a4aec81", + "0xa41b57bf06dcd101c453735f0286744b11e713a439a127f30487e2446eff41c1", + "0x290163d0df6d1cf9bc43da673d6f466834fff385211fb45063f97759f271d98c", + "0xa7ce5787ce2ce6aa7c27a0dc542ee4567bc0997d51576ab8f17de18c478b601f", + "0x39c28054cb009b4ba95dd14bc7a267578dc4b9cd1c6270816606c8bf52158630", + "0x5e45ac7e6f0e35d2556e01878d1a675d924093e7110e1aab81421482ff05f854", + "0xd08e0fcbf16b402fbb050b36790151fc392df1f07554868f8f3f8584f81bddf4", + "0xc2dd5b15cfbd8ad90f3edde5bf55124a635ec62ea8c830be542db69577320e3b", + "0x4ce0063311cecd1da1e5ffb7025300290f97d88d7f65fee5cd3bf97931539395", + "0x7e5c5dbc6f4fbfeda40038f4a252604c1ef5d8a7dd870ae66aa4929ce6a32308", + "0x26ac7169f961754e573635886c10150ac7c0211f3408706ef88091ab98a2e2f9", + "0xc35064d25225dc2cd15ca79648570062a660979c8f04e420bb623490733d0b03", + "0x5bbc335fb8369194ec61e879be1a372891879468a3428f73be5b69061a20c95e", + "0x80cfe0d899523286311927f33bb65754c0c967db9fa70fad88c32157be2265c9", + "0x137c8f1c0d3de0cd8db9da874dc9e99c3b86a7ad68f8c6658278681ef650d9d9", + "0x766e9af231d8fce060e4cb461268e64d11dd3191cca61acd2625a589353b312f", + "0x313dac00150c1b45dd8448e65380f9df0b0f339bb0ec98a0643335c0a8d1658d", + "0xbf6404f5ac34c92fa97fcb6d928eb56855130641cb5b53c62e54c2125fd5a2c6", + "0x30ffa53cbf3a561294a46cc5ed5f58f413aa8d0c8391bb02db34af8066213b1e", + "0x15ae34ad82b69df94e8767f6c964898f8b3c0036af57fad7efac2ef19c7e06eb", + "0x7fbc379fcc19135f28d78f47240089b847318e0e334d4381431f335d01226b7f", + "0x5246cce9713f3826a7c69790c66a4cea6d2d48c1baceae4775c755db5be48e07", + "0xf9ae74d2f3dce3e2d29dcfff304c2c6e3617d67cccd3948159624d1c406d9cf1", + "0x247c70251af805648026f68d93f5896b724d7d6f0474125eae16574efe708656", + "0xc180ce3e0870249d70cc504ba3d591ba318d9a45c7384568178e59b90b4f38a3", + "0xf085ab999ab5dedbb4500aebf433f70364f35986e81a39d1a046df9bb4f4ebe2", + "0x83082a356baf84501d42dc0b32e759ddc0c03f75495531e146fe617c2b882a4f", + "0xdfd27078b1490fb2a3f78f166dfbf59a0cb238de388b30adc69776d55524e5f7", + "0xedba38d4297120f0380053d998442cb3409789d1a17c673b6e682293616ca39f", + "0xc308a66c3f950590cdfb671c4f51bbb74fe78f5b219a7e71beb656c9d4f93640", + "0x0ed8cb6b4ca8add4fad2e070a16ed8c2f3b642c87d6f6cb86e8f5744642cb0f0", + "0xef05527e34c6649625123644262432150a76c3e8e10f7e8783ffdfb98c927787", + "0xa4a2bd6a397a1ab6abb43df08827d373addd7c8f08a1046a0784a41b1eb9a779", + "0xf184de40c0a24e00ad739074edb7d2a2275a7040ef5740e3b1bd64a1ca7d4e17", + "0xea04207148efd16749ee18c7a3c93a5f20a65f9eb0fbde07856aaf16eca85afb", + "0x423b9e055ef9279a5667b1d2ed343b44b244ea4951d3c86873819a976c6a24f5", + "0xc5be1837d232b0b8cfda23364cd77d6b8f84d547fd9deb822e57795eb7cc1e88", + "0xe0311c3b2983066cea34c54b9d3c43e180fd9a2b1c6c0ec8c3b476673d55eca5", + "0x3bbb939b6afc097025d8bbaa7c141eae8568b68a7c66ff0891d221caf6f1cf20", + "0xeb1cf3d082442a1ce9525441d34176c29be074adb7299b600a71ccf8358621a8", + "0x588c232a07dea773e2edae0bf0aea1a8f41717b0e0a445fa72feff828bc25f01", + "0xdaa87606168f5685ad053fb3085d242c859110c9d3e0cff61efb4949c38dd505", + "0x652ce22cb172e48b217dda7596ef0b0d387015dbb3391510953190931e613980", + "0x03cad16993d1c090be82704343ad50ca55acc767bf82a7291337325a5acaf2f1", + "0x4e98e077e60e172bc073f9859adb6f1f21894045cda1f9f3740f6e3bed07c351", + "0x323e2be9645c2f4e22f132b7786430ac266c564af1566e60b82b1e9572a215b1", + "0xa8ebe7a74d438ef9acc160e8efcccda1edb8f42f78581b4d34a441cf4539eac6", + "0xc79567fc5ed8583d6619438f8b766f75dc6c67aa4fbca237d8e76acb1f082cb4", + "0xded04569096ddfc300c975d96e4df69384eceeae37eaae8d7c2f54e06ac3e377", + "0x263284cfadcaf2ffe8867be6f8105978313bc2ff45fa82bdf00726c72194789e", + "0x6c3640f3483ba4ab6576207b1c078601b14d0d576bd4a7fbc5c577d3ab201c3a", + "0xf7c1cea067fe78861934e1fff94865602c8baf7ce90a4793af1a1d54d689fb63", + "0xa7525e2c4b750925350ee372ef302c7f85f06e5bb510643e02fa7c97a3c1372b", + "0xd47cd1aafd4e71a60b06c2f557980cb12f07ae49bf6595c36629db6f7043b555", + "0x00d61bab19cfe69a83ea31eb8f385342c9067295f60aa46b3c2c5aa800492b6c", + "0x265ffc1f620cb892264c82677e617d49a2910e06624da8b6b86d972c7fd65b95", + "0xcd60f911cc43c7bcc5a4f0f72d96fd08780792940b22aad27c357d091cbfb72c", + "0x16c52b4d38c8f884c90f64e0bee25cf87ad84adacdfbdef89843500a595d26af", + "0xff6f2cd7cc0efb3e0ae559bc722103d4152e4d51cc5811609d78c3150169bb09", + "0x339406673fcabaf26c739125defe0498a5cc3ea36a71b9aedf5108d506923e92", + "0x30852071a5d386c37f5ec9d3c04fd945b4b1edb7220d071f191892405bede9da", + "0x341e5c86e69fd8c396db7bfd6b2ddb5f0d9ebc5f2e2ac211639a05e5f002cd6e", + "0xcf0d28dd19e99b4e05d44c57037f9ef3b6c2a1d24942544f0efeada3ce17f89b", + "0x6e429eec1dad39f0b523e8128d51170cb828b0ad865912623b5b11d358ebe0fd", + "0x067a352f18e804df43a38a277f700bb098ae9dddaa4a93bb00b7fbc1e786961f", + "0x224337f2c7013bbfc8c42d8196bf932cfcfff0296afb20b272e2494932c92d59", + "0x031facf9b923f7031539574e8c533638f06c5943fda3ad628eba7710e956cd5d", + "0xafac1aa2d369ce1dd489ac5cc705cd8517654dc2cc535838cb4be2adf17532a5", + "0x082a0812353fecf3a81fed5f3734f6dcc83c32f7c1ce0c4640b16fe013364e89", + "0x86f0da1193358824ca90ed41f87b7520b1726620a9b48b0631ad006163a5a14f", + "0x1309acf7ab7ce3178574193fdb80a847fa9f2b01fca43eb25dade376553cb520", + "0xf1be85985c8cdeaac7d71561238329bafbc58cffd44a9f782ce1ddbbfec31cc2", + "0xfe4848e237443b969a08d73141690f9edf610f6613cf1cbbf43532bac791b0f4", + "0x3851af761d43c7139e921aa8996c77b59f90181f0bec3ef7d7742a63ced285f6", + "0xa1cf34270746b360ef2532204740e1188af514bdc815d3b8e7b0cc9f13a48655", + "0x1323845d43c63e1999c4c1886f60873fc60bc56705c96b02fbea467435e31c19", + "0xc03457a7ae5f781d8a0a604bb663485a2a88e007e914044e84c86f78b789cf19", + "0xdec07d36f696f10ec94556d827e550c417c4f58b253fd95725cc449e1834b3c4", + "0x41a329fa028b1cc44d69189ebba345da277fde3876e3f298c86b50b001935521", + "0x2c1cd40427f8956bc140134fd1a3319b28770089615912cda71c415ff2ff813f", + "0x1c39375cbc74a3d18fe7f51ed58a051084ee21c55a80f8635eaf48b841f139e2", + "0xf8a5002aa8973c8e90367cec37fa800ac06903e2a5b95ac5253338ecf2a10878", + "0x279c5f77c0e79a7fe0be869213d3304f8ffe712fd9b9588adbd90c5af3033993", + "0xa890c92bb13b3a7dec74b243bb907d27985a6121557e07dc458073c0e9217470", + "0x40b7822a4d5b56ecbb54fdcf31a51d3c8cc4d325bc685cc63058baeda146e68d", + "0xaf25647b0d79ac537df99c82805a08c73fc6180017a4d55a9331bc0266e201c9", + "0xeb9edccd38615661c3c63a44f558df3c1f7ba97245fc3547d5909ea6564f4192", + "0x38070a9cc3fbe38086efc181d2b848e429d5082722beac7fdbbb882d71def1fe", + "0xe43436ac2c9d25ef15e1736ac186f139c1cb730b072b12effa048337fafc7660", + "0x044ebe5815dd2cbe30ee7390b80082c30483dc26809abdba2488462862436915", + "0xbea64ad3affb20a6271f82f7825df1098c45d005ab48e4318e81c9d2de8bd510", + "0xb7860cb493502ecc59cefd8f2a8541e8c596b0290806677c616790ac304b4a03", + "0x23fd7f22adf4732704a1d308dcdf5f32896da6d2a49fd61245ded0fa39b9069b", + "0x8cdc92c9261608829389cff1a6ff82a7df08f48e1259abbfac17a47b6da29088", + "0xcb79bd3a65d7dcdb69946a705d2eae8fab0e7cd1e5337d85049dc26f1417f9a9", + "0x622c257fe3f9802e079e524e6b008294c0869a5f02001ee5536fe7126e614fd1", + "0x0a075e38f81302d38dbf0a0fdb24061f72b742de10c96c927d254df3e1d10a6c", + "0xbfa8cf66b604908de2412d9455a4c1076e66e1274819d644329ca66e199f6269", + "0x4f6563dbeeb1d213dc2e65dae0bf57655fffd513e82212fa30a76bc9e69335b1", + "0x7700fbb859456ad8d0389a642c88c6203043417289aacd492803c757803b8f35", + "0x7a2b3eb33a46c42e0ee02f0cab4e896c10be5c133e9b112c8c812d12f3fa98ca", + "0x48d055d8e03317bc892ba74e6fe044ecac557b4a515957860f474f26960411cb", + "0x8b0edd8d0d85ad3d2deaf1d99503896a21d7ac2be0f3efb1539144f18a4ed9e2", + "0xe65aa022bd834df9e8d9a828f39b302a50de94b0d7b6d412e086199b82b9d927", + "0x658c2c760759a0e5848b351f26797d476efd8c4f1fbd70d371fe8972e7629765", + "0xb2782c7da779f187994dcecc3da695cc68da8dfb3fc6ecb40aca239e4cb1b652", + "0xb1105b84e8b9d104dab5bcc9fac93d68b0d18264f97b511ffcc591e928c29053", + "0xa0fc3529405560a22191aefd1f3dddfc540df1a3da8c7c225bb379f60b6e9fcc", + "0xa847d65329e1738512bca82265703c6e14c70644ace738c54252ad174c8a18ba", + "0x0b2d72198a53cb3ce0983459115cb5d6912756673f6569b2cc3de5cd2fca1aa3", + "0x58671a0edd13816d9eebe3d134855326636ba9524d4920fb19d133814b322956", + "0xfc32e8ea013749b1c8439e1adc99c8132bd04899fb9e253bfe4be5a3e2cf4d27", + "0x4b1a287e01c9f19ef3348af5b26099ac84d222a7090404abaad1fd7cb31490dc", + "0x80d6b895af0d78d229ae09a381468887961dc9641499052f31285e73ef9b2074", + "0x3af1a6641c12874a819675e598edaa46b456cc904477e992ccbaa6f0c9ebba9e", + "0x4cb1f39afa835915d84171469ffe9c6c51d0c54a186189719ad6db267bc94b96", + "0xba02037ddf654dfacc40342e124f97060b2d112a3fe82f4c618e231bdb1f3f75", + "0xc4cdc9e6e57648b0c5bd243ceb48975e5afc52f6be3a3ff691426873658ec305", + "0x945282a1d0fe9449a74ba7e94aa469dfa86b768ba84854a8fa8a81c0380b64ae", + "0xd2bd4d5be8c0df1b7c48acd858da4fc2ae4d98be40b05bc31907bc73197fc321", + "0x1bb353bbf6f548bd626775c1d51839d490bbf47adcbcce689ba5cc85c4b74113", + "0x1fbf798307b9bd502c612e4d3f57531faebbd479330ee810346920c183668013", + "0xfb2d7f35bb29e3d4cb1fe4ebf1f1930a45d352226b823751fbc75363cb2f02c2", + "0x2e0dcb08e7d3c8f8356ef699f8322664d4e039464b65c9133e1c9745fe2fe09c", + "0x37b4ea5b828ae928b69f931032a4b56a0dda4ea4aae3a8bb17040b37e721abce", + "0xa45d466b150ce4163fe1a7d98536cd38c289471d4b4cc98b77405cf8978ff4d7", + "0x74a5237ff222d7c5fc0a3c5381d698b259c09bd8e0a2cc600bd8f03f9963ec1a", + "0x7703459bbd7f0aa273234dffdb3c9490fbae8f17f48213548f6a1202330ad28e", + "0xd4840325914c9c2a253700ea2a3eaf326edf8860d9748003ce512b61039f4e1e", + "0xe702caae686493718e45390a6ccf811e2942eb0ed675e4ad9147ab31200b94a4", + "0x2f6f596c9b781e43de359facead7ca50b3365f0aabf3c084d9f4f91a18599cb6", + "0x1d78061e60316fc0ca3839e7a5e799c654458b0c372a7e53300e23617b5f68d7", + "0x6370a4d70f6e0e7c3b96a9548c7d1b3b2ac8d80a33153111d0252ee41566ecfe", + "0x2265470b0c3a703b4f929d82123d06043fa50a6d3557347e9d793f8e8a5fb745", + "0x5e87f04f79d91dc8021c158ec5f7279d10a20bb6fe176318b1723dd5222e08bf", + "0x15d2fce304eda17b2ff145d38655ff662de1a0ac4e0d05b1687bf41acb46f7b2", + "0xc2fade8ed9b0d1f932fcbd9ceebf68a4f95bd938b9e042de42766684f1ff6427", + "0x383281e19dc775f5e3c3268e6c70d571a42d0a7f607cf298970be5303dca6416", + "0x20658ccede7871c4ea2de20f5ad92bfd299ecd93cdb34525178401b23aba9b2c", + "0x83517d23ea779e0c6c03e58ead04b7ea56e47747ea5c65d5165f03e060570f4c", + "0x5a503c3c12eb58ddea4e7b7b1ce21c3c043b3086d72cc11b778b0c8a996d470f", + "0x65f3b927dcfc6d9397534678ceecd573af070a351a1998b5ac7f5a5601ca4ce2", + "0x7dfd5a86c6338d9fe5bfc9c75c36e2b9b59c6e6e510ad15c255e8ed9907828da", + "0x381206d87adb7df9163ade11b1f4fe411e146d3bc0ef15bf273afd484d7a5fca", + "0x04c0d8468cdd8ae508359c2ac6b88d1f72ca127b80261e0c346122dee5d40547", + "0x0d1a5db4ef10cd3b3ac4103f722b29015d52d6994d24181653ea215d31da9524", + "0xd9e023e42e68f9c72ddb0b85b1b213f86320831e5805c3ce6b6eb1e1e42da832", + "0x4857963f0abd7a968a925443314b450b45bd782117c38605feb7d0be47490b01", + "0x5bcee8a91c8dc9077bf73fb0967091e23ce228a0077d716309ca621247360ab5", + "0xdc4c027b3702b0387f42fad485fbc0bc8662facfeed3004b078f1f65ff4aa201", + "0x5c9ee7f2ff3e60314d271ce618c96c9d68d786eb30ace681fde75ebb30ab39a7", + "0xe1fb62a06cd177bc08cb9a8e8255ea199bb7ba0882c8574a05a435e747f1eed6", + "0x952eb5a09fa3256845a7f17d81de0e5e683d1029d832c24d05632928ed88859a", + "0xb11ff41b53b67c7230bfd7daa66c8a26af21c13b5acc71ec775c18a2e83c64dd", + "0xcc1ac60c29e2e1d02315503181b2757ef44a3607483ce1586d305b593225ecf8", + "0xeada8ed57f57680da8c74b2028e9b4bda53488e2980783692be3c4f4f252a4e4", + "0x1b5d3a8b607abb8aa1b7528b7d938b8d43897413cad05e6cef9d8e3caae513bc", + "0xf778b0a63840f4e2c3d85f8bc737efb89d94b1bec0d4e963dc8f362bf1693d4c", + "0x4c426c897fc9f79f6defb1fec18eac2ade5bd9ecd9c29964e16c301fdd029a1a", + "0x92c0ec8bf8825c3393e935aa8b40d998398334fdc98d6088023c52df71d9ee7f", + "0x17cc306b91504c01b4797f3d47a3b1571b82e691868fcaab11cc1373539b7526", + "0x6f92ddd92b0245297eb53a4e24c63d611172373bde2aa7d5fb118286fc56112a", + "0x255c245337918c8882712edec7ce4a93da05a3ddc1fc0928fbd622c5c63f3781", + "0x6720319c67ee799646d42427ec37c6ef1502f74f60bc0c3ed9471079e3ea468d", + "0xd9c90299ece040a6486045add06818a6a95056207f139af4910e6d522f110577", + "0x8df8a9a432e591a88e29f488b6b2a505bf92f3424212cf5de3ccb3f09acbe350", + "0xb7e9a4c179f85fe57a48268bd6a59b19871bb72a78a6ebbefa344f994facf8af", + "0xb11dcf7fcf1403682e48448e2e27979fdc5fb88038c0e3048accf54df517d753", + "0xca3f1e85d646db5cb59edf0ea4a36d96a426aa3b22c8ef610052af4d7e9f0ef6", + "0x4e2f96add5638c71a34b7bdc3b67da15207a5446cca9d7018b3f732111df5a6d", + "0x448f271fc8fe38b74392362b666ca71556cf6e21ead19d8bacdb4e04000b7975", + "0x90691a41b96a82c8251003d3ab00b75b158dac3e93e59a270c7eb2880578d41e", + "0xb44540d0618fba205e3ffb8d15e5001b5c527a63f7e01442c38fbf1140952b84", + "0x9b56af691b85b3e65df60959d2d5c8b723b1a9f872773a80c1aef36bf635e6f6", + "0x32e08549279da21c69105b317b6da0d5ef000ec0cae8e533854684bd3de7730b", + "0xd5d77ac750796b33d0ceaf7d9d57d464b60b6509c057f301d08929a1acee4b7e", + "0x9485d5509bdf454d9e9dc9fdf1e50c422d4de601de281c9a07c6a1524309094a", + "0xcc3e293246430a9a08790a2c0663060a885ec9b517719ade6c1724bdcb9c2155", + "0x8bef554f1e99cde161143873570549946c528a24a25ff9cd49cd006340a38c5d", + "0x0b3591110f65fc14482d5cf9ee5dbe96e2fdc94616f5387cc3887e73a407ef1d", + "0x04209b4ad678ebc54d9e717d2dc164899beb4c6a30ebdcf4c96859ba407745e4", + "0x5c05febe34e422edd9476d911701a6504e36128e3f44c6d1a6af444539797474", + "0xb2ae5a1fe4d6ade54bded06c95cf729565e88dbbac980a611b0016047104d832", + "0xe9215600c73961b837a12b1f59a49472cbe18d9be25a2c592881cc627dfedf74", + "0x999e2e62da5c6c08aa0ae809c33a2761c5652964fe1537caa3005fa09d0683b7", + "0x42590dca9e446a5ba41587b19e48fc8fad6cc1078421fbe47519a0d33c716dc3", + "0x9c595878541b66bd88a347f5b084090779e32ad83e2a492b286d5560c55813af", + "0xce202cbe0505ff12992e7644da1fea73e542977f6483b6c0caa48c566b5bf91a", + "0x63a2b8e61d800d1dba6f230d4afd244a90ae8223fddfc1476a1fcb4b7bdb3a0b", + "0x3bbbf4485ca2b327ee7d466ebf6791bc5f770e1b7121c8a3957f309cc1f8ffe0", + "0xca1c3e9122bf5828cf1f8157fd772a7fcde319cc2fac8970dad83d8f918e1002", + "0xd06adc8177794d5e118015c50af02c8e1f81af4b090144eb732a705ab3d017a3", + "0x84e87d92cbd62f4502b0a49602a4002a01767489f8e0d9fdf1aabedcce7d293b", + "0x17852831fa7bd622b8d888203cb1cc5878412db7a41381e9da539cc9ddbbfbe0", + "0x6ce4586bb403e806ba55bd794894a4eb98313fdfc3b95dce0c1457cd81b84ca9", + "0x2afacc24d32602265c5c663d286419dc3275ded5a5aade8a18a0942512decd90", + "0xcef54330c45741f6264f032d439827e08f9860e0393d4c942564317f8a650981", + "0x9fa960bb44bf71883630217834df90a0865b32b42967fe21795f89c99cc7f44d", + "0xcb341099a6b739e1ef87b5d3a295339ba9ae4e2852d7102fd05568e35525ed8a", + "0x9063933186df464b6706f83d3ad16db199dcb235e4d6a99c5f566b3d48b588e2", + "0xf1d5e6696fe6cedd39379aaf5dd0407757bffd34828fa510be37721d3b4e1a06", + "0x932eb19ff0a46c7e4aa335bf6920c7eb3ed1a4a378bbfd2b85e07fc3c2c5b5f5", + "0x9053ea59c0093846f4f4ac1c9a02b22476c481aa36f5619fb53f2277ade70810", + "0x6b63302bda22124d2a67e23091d17a20960c81f973863a4921825639cc2788f7", + "0xc7e5415673d1e0e4bef7de37df22cd2e434a6e647c65be9e70437bd39bafcc03", + "0xb28b01a62adb4dff3f665183a28dcc0765e8f7985c45ef4ce161e9d22535e288", + "0x72296eaf6071079ff02b9325e9acd8ed2db6f51aa5219dfabaf6d66984db58a2", + "0x8018aaaeb1692973dcecb4927461eea028d5f62ccb1d33b1d2826b791cd51719", + "0x9e41f7836499752d1451808fe21d8603ee18abffae46dca7d80bdef55ebf83c5", + "0x673d1b8903680519049e58292bbc1c041b4941a764ea6edc30290386334e1906", + "0xa1de9501659689ecaf2775ed2a898a77ed2bfa5d50f0f3de0c0d4e6ec7189dfa", + "0xca54ae561d4b603497b1a4023ad02239c5e23a2325cea861698bf1274bf40202", + "0x261b8749256e11b64cd1cc47b8488e3c4cb6df186002c41b9fe29876508ea86c", + "0xfba62cb6c8373db62d1329a1698b62af045395ceeb85e44779e035f849eb7768", + "0x5e0b42403cc783c5e9e853d62152241ab2a4b02715799c1a17e01dd366f35431", + "0x39c575a922ff577087be64046f4468af3f0d1b52752a8c689f6a0c8a9d3ee974", + "0xa02dab3dad8d7777b939387d40da82b8dc53cdaac01c39dad8a6b1e890fe80e5", + "0x3034bebd29bc43f2f025812b6e67ca42652fdebd709798e2df3ea068764cc31f", + "0x099cbc6d9a41387b370dd5ef23fa2519e245d15bf006ad66dad80cffd4b19697", + "0x9746b2802ee9ea05e6769a74305d80500887e2b4f8ec2da1cf04c36b7c4b8645", + "0x4b73821ef7ab9958f010d3748d6bf2a70752590b389a16fa5898924dff60ef28", + "0x53a5759be11402d9213b9c433103e159915ad75a6b5c96da60ffd083e1fce1e3", + "0x8710d5ab08fecd453e599a493cf3b3fe30792a277dd046517aa13af2cac95684", + "0xb1ce27cf4c899d755db87378a32d5708f5f213ac5cfe0093272664bc2061a3d3", + "0x1d9eff4e8acc5148643079c92f0aeed64ebe2455ab0633b135724ef6c9900994", + "0xe70ae45180450463ce4e1e646707d62108038398f445ab44dda5bf74c59cbb0b", + "0xc4cce0317ac2dc3230cc1ae644a1106980ecf7383af8a2008a4676688039f640", + "0x423809e0ecb16e78155f4b93b5631db6da5b507702429a82ee4c7a8da7120710", + "0xad31a21a220b3edc296eb00a00b0eaa8e5981977a5d610b3b70e7cac5920a901", + "0x0a3aa4c8f68fd12c1aa54c872bfcae1fbb9b9f36bcd853d629f0a9a8bd108f2b", + "0xffa8a39eee0919d444d4a7872c4249c7439cecb2c7bea4d8ee3d7d46fedd80ec", + "0x31e663c7f6f67a44e6f9a06c40c007f313968afa8e82761e67e8e4e87eca2658", + "0xa50c1b5adee4b5613126e611988d0fa2340a50a9734968a957a076c4e66a1a23", + "0x13dacce5806b04ae23cdd8d2f22ea394ce9a585aa599286c9e003df8769123a8", + "0x7085178d0fe21f8dfb9f5c7aae1486179c756b1c488c70db03b4cd5907b707c0", + "0x21bb008dee88ff8cc62ca30aceb9c00c38cee73b644fe77302011416502c8adb", + "0x5a12ea785886179a73f57af5be87be57036d70f1b00ed1fba921890b904b3bf4", + "0xef14e6f56dcf1d7f6564b18877ef23dfd15bd271b9e37797ee4191e903ac3e8b", + "0x6ee4978012daaf9732b9bbbe720ded326f54c900ee03454e9a0fc95746f76e5e", + "0x12992df858c2b61c40fe83a7889fee9a83c62e6b4e4ef90b8342a1370899594e", + "0x3325db799c19f74cfcf09280ed73b8cf3905682f12b7f33872952ed92a34093c", + "0x5a81cf9f1c7e2489d964883b6cde4b721f8fa051830a71530d79e88f35fda032", + "0x7f1b74e48ed2d9ba074cef34d6d6879e56655a42086cf7c2a4bc709e1b43ebe2", + "0x5aa4f0e6c056bea72559ba3106e1a94cde68e03d9fa6a6abdd5bffc5f0cc7cc0", + "0x1c92fc96b65240c11b52dcea389740f5188cb41230e3b51833a7dabed98f497a", + "0x68b9911ab58d7ba43b39ddd73c7ba248aee91b73db64d546e38f0ab7321ac4d4", + "0xcca41df06d26a2904aab5d7a39aaa5143c55da7c827525f0daf28de776d2f00e", + "0x2a67055d35bf739aabb9998dbad07d359880c0ee6194df2a661a6e576c9c17d6", + "0x7d6948526c7381a2743074e4c782c0125e7036f99bdb2cb52f81218921cae709", + "0xa724e34766113154ecd3fdc0b6373ffeec22b7f1c08947b74a0adc09beebdb13", + "0x7009f16fa62625be385ca2992698a5a4c55372493f1eaa253ceb118f50d52dc7", + "0xcd2e82bf0b85702606c8ad6860290d4a8634d68aed00223991704238b9775672", + "0x7154071215cbd81a050d32aa485aca2daef6e13c8ab273c6dd3ef0f297c7f3bf", + "0xc548eb4fa1094b845243e4544aabef94f1ddb093db20bf2fd141fdc43e211ed4", + "0x8dc756dd657f7da83393b50783cc63ee21d2b50dfac33d03e9cdc547f6836dd4", + "0x2f635a7cdcc2f6f28b04b1a0d836fa87f5717f4d9b58853caf44fca6f14f8793", + "0x271f41b7926ccd62addeb172043d6176c7727b221ceda498c32a59dd58b66ea1", + "0x25222db9145649bde0284c18de9711dd3adb84cc7de5257a660d8b370bacebcc", + "0xf2435587e3e09caa8a014bdbb76bd528fdf90f57924e0074dbafbcc4216347f0", + "0xb4688df298a37c1ff9105e5f1eb6208e40c5d3c87a785b0503bd12c9f5660937", + "0x0a3a8b41d76898736cce8cd401fb5d2f9479db542fd2690ee59aa095e28ba592", + "0x2894be83c16d5e2f04b154f4600cc1850d7cfca9d63e0d0f64c8c1817cccf485", + "0x460c1d29c8583fde163e646b8ca9be1ef6864afb74f544b22b902baf44b14b8b", + "0xdce2629185a6a720dcd87ef9ef7706e8c897c3ef7a60b0676b45e8fbb21e2a17", + "0x97f06aa039985fedf567fd91b6ca51a6bd927be00e998994c23cf2055cfdc61a", + "0x1b4d500fcdc40d18d77468894c2b3ff004902f17c3862a940e9a238e2070ad9a", + "0x534a7d9c191efa9f0b7efd9245a7fac2f5814ab7e5b27d39c5ae823617f43985", + "0xe49d5b1442ba4aa3659cc05e6909e704bffbb85b472df85ac3153481eb15f32f", + "0xe2ec61e5f56f835106b76a8d117bece6dda14a96ac9928a6ad0b0a9567ca8f7c", + "0x3dc5c785ffc6d96ec2ced6030c1adbf6b86215c1b1795444694cbfac8ca7c5df", + "0x3ce1c5df4f686fc24bd0366cd371f4736edd498d1108c686cb472ed79dd46244", + "0x47d64b64b1504f41ad410d9eb08f08c94601d616bf580ca778569764d15291a1", + "0xbf64c136deb198d6dac0f8768ad1099c4006b2b42a4842894eaa6dadf73f6c2f", + "0xdb1e9150021e8f2db1882498749bb5723e7c0e3d1c900e8ea0783ac0decb9d9e", + "0x577137c084e54e353931a1b98467ea5d6228daa2596b4e0d53cc9fc5e1d285eb", + "0xe01caecb9be3191e62fd2d572447e8c3988f753d20979d5e162f5c3a0aff6b4e", + "0xc05eb371eaaaaaedac8c7cf5454ef6b91238d6de29534374ded74088a818c9b4", + "0xfd0adc93be84f13d3086532642fd282881d5006a5ba1870bc4a6fbf54d3e979e", + "0x76d726a4ae353837969b138a3339a6c482b8ca144bc440a0fccd55492de6fd31", + "0x1d609aa04830a9cb6bcd25f1b4e4cc75b43191f76626bba43326f6909ca7f91e", + "0xef905abae61412d130933b2065ee20742746ce170f4086da27d059752e518bed", + "0xc969d282d0e47b82b0bad7f72f14d88d75416bc8110a61dc478bdde663f52f32", + "0xf055b8e5060840bc2300d0f99812ba690d2bbd7a873eac88cf4a38c9204ef724", + "0xf1901e1eb7dae15613f33f3b72a72a5e9bc2d3959dca102d4ecf0dfa66762362", + "0x0dda80e65695f4bbdd8e98a8f965b787212a14d4b6ed8d04e7f9c5078b0a7d0a", + "0x74a3e95047ad8d547e40c05c65a447d885a130109f5689070a750ab85b4f2330", + "0xa3c998d63f556a1aa4d39a3a6e3e37ad63cf75c8e480165a823142875b6d834f", + "0xea751383daccf994220d015b61d9d9ca29298f54ddfe8247bc512122cd8ec73f", + "0x336f8b568aa6d1edcfa1e0fdfabf7b98e217d43d7d7e1d419285fe70a8939132", + "0x1fd38d30b1f5a4453a92c99028086b13697320df68cb3a3d2d64a395248193c0", + "0xbc8febba7dd5872e59c941902965eb867a51e7636eb509a857ede69cb50cc75d", + "0xc21422e7f83e08961bc3f1647b984b423eabca997e37166ca178a91fd63615eb", + "0x273cfb850ca04201eae53aea6b0823c4fb4c0d8d611c0e6dc78e3cc98dd1c73d", + "0x74bb4c49e197373398e4c16ec57d39fcb2e685686de9eeae45c4c69419acbc5c", + "0x544632d0a0ee85d5296dc403a113905ca6e67f03d1c33815ae4b45b27dd64636", + "0x36fd7b1ff611bbe4ac1ce40a5a503a48677235c4292b3cc3be39b310d4b7a946", + "0xeb48ef73c01c2fe26379ac45107759d93f9b4b89572ededfb71d42d70e853620", + "0xad23d3a6473231366c26285eaee10873a5e05193bc033c274c29119b8696a7f5", + "0x3fd7fd157ea7d3291ae01c2442b6f5e88246989c3fd2c8b4bfab46e1a4c9ba30", + "0xe35d2ffac12bcf3958b6dfe54b5e81590a6a0a7314135ea131b598887c2f8489", + "0x65e481eb9f37e250246842eceff8a56c17a934142e6901d6b886c3fe259e3a1d", + "0xc68088ed61d778432455d38ba036bebe771dc7648d13ae158e462051affbeb23", + "0xcc3160984c3369f94811e3c5206c1b209ee53620e403933c10759637c4ff1755", + "0x9469a4bd9303b378b8e2d2f32570aba671cf25154c29ff867d2fd735d79b0f51", + "0x163307401fa4ef4ede6f65778aa193d7ddec14230dbc859fd69a3ced7bda273f", + "0xc24910fbd3dc94c694a1fe49b3de181ed24d771b96eb71e0578d1ad618a905c3", + "0x16ed5b361b6fa3c41d041477159450f5f3ff378659175530411775360abf3ea0", + "0x89d07761fbdd0ffbdf1c84ea3988b04d8fdc090108fc74c2079cbc952e0684e9", + "0x02c63556b0d2253587f8991c3df6512f8fcf033e8ddbe5f65ff0adcb54f68a47", + "0x28136ad64a85f03338595197125f872176baf58cfc11353a1a29f7e820c79af1", + "0xa4f3d501f721445eaaabb6a742b9a4642dbb79341c29a54bf1fa1a6d3a01fc43", + "0x41002527bb804b56e427f2dedb4c27f20924ccddefbcb28c9f4c57e084ed192b", + "0x1b8277af73178c152c3f5b2232772b48385353c6c07a6bf1ff05893c511eee30", + "0x428872c50ab0d775dd4ff56651046e4f74bcbd436e9f25f913caa7410952c6ee", + "0xbeab3b88ce2184b0cde72aaff3ef0d26642bb356039526d90046f17e3a0709fa", + "0x688e8a9fe628468ad31c56457725c31bddb3b16cb69ea7f0106272157db53059", + "0x19bbd50e43a2976a68bb6d6b6782597c514f1acdab74e6cfd60e49469effa0a8", + "0xcfe287de72c1f9ff4b433fcba6870b825306fc27ca5e687fd886ac656b6e428d", + "0x8e53cde4ea00f8bdfc44352156d1c7a4eecb69c48f1d1d6d19497647ad0ee01c", + "0x53cfc99d11a78bb3a4d696dc20e3055307ac1166e97935ffc4b606650b905d99", + "0x8fe03c717815a220c2506a3f9a7d7ac2bad6dd9ef8f2659d9e74a90a1337f412", + "0x07113c5007f1112a83affb80348720c9b5f701cf01914ac99d2f36668c8a6b9c", + "0x07337a44a53c3f4082e5ab31733450f22b828faed28486d85999d912f9bd783e", + "0x5e3b9a05db7009b0cd6c935bd7b85019b295f9c9193d1a42d88ec6b60276568b", + "0x7c6754acf3d72e9a63adac0691c4101c2081a8a03de86d7da6d3d10ba27a4a04", + "0xa23abf236b5f7c7cf275a4a25076a92a32e1a988bb925104d472ce3877b13b04", + "0x0a83cdec965715bed3f7aae5aadbb8be5c8a9216704b1d6376e6f32bcc9d6356", + "0xc31903dac3efe34c09febe1a49804df9682ac67853a78ad6fbb11c29a57e5e91", + "0x19056cb0e9439a88df93956c3b57516ce12e1c755ebc5af37540cec4acb68088", + "0x4526602e6b38ffeb8368a16ce903501809dba1ac392595bdaa9b442b7b588f33", + "0x00a2138f1ef90b1ed17e377f194e4094f3326fd58463e73819d269df2a861918", + "0x391ec060a5ae8a8e96ba66cc1dfc3c86fe2da41d57b1b4d96dce042d219982b2", + "0xe1ba60fac312ebe00a0016a276e8815980eb70fedcedc07e1492bf9185b840ab", + "0xc41dda6318b7195b16f87b0cf030b31e9d3de1fcb2a3d951d1cb937ae4785da9", + "0xedee457f9f8f0de2c66010cb82d3841acbe188a44273f7c5ddddc78c84bfefd6", + "0x7a6af55e5c567ee6da201842ba8b387b968619e2017ce51a8c7d51194e182062", + "0x64c99f5891625a396d59ff5e1ea9a937876740b1be57293b8002604133d12d5c", + "0x4c2cf408bb05c947ca0f84107c9eef7ad88aa60b30280b41384c582d62c86c1d", + "0x9b5eaf6f0323548132d3516c18fdfe897df12783efe3f0cd5125843c9717cd4c", + "0xaad2489dddb5028cfa913cc183b0b7b64ed4d6dc6321a2ef6d1b5719b1f24c40", + "0x1fc2b763c89bd240429336d9607f368821f5ff62338fd5f61c2531f1158183f6", + "0x21eaeca4bae66049250f11d214a436fa27274444fd26c7cafae31207a39258d7", + "0x94934cccbbf3869acd02ff344df6089af101a51898722d082f9452fc3b420178", + "0x239b7e178a5b8614c2fd81881b902ad52b3f5ba5519cdcd10d1626cb200cdb3d", + "0x3bab78f0f6ff1609c42922de4aa87547caa60497263698034cfa1a91bc1461a0", + "0x374e3408a2cdd8affcb96787b2ccc3dcc3709e0f82f354fb0318ccd052692eba", + "0xc6adc590af5b706ad7e7a5f74a24ad5b98fb4bb00d77f3c6053caa27b490e514", + "0x8565bb3a2606d59aaf58296d89025887488ea9189ceee798c5e2e057a96ab526", + "0x9b7c848d5506e50c12d51ee852cbdd484c758a521beaf6e9112a0ca7bda9bb66", + "0x84d554c16eb4dd69792c2c16b64a0350ab000fa77f7579a457af9dc802227938", + "0xa133b2394a2b8ffd25618f8c69a5c13e9d89a63d5b91c06f4bb2adc25465fec7", + "0x7cec2baba2edce029e50d40c0b4bd4e354e2f6d2a4351998fb4660332a2a951d", + "0x4c1faa4eef0d181cbf8cf86c6c36c25bd27af79cc1cd3b9166d90ed861d4c214", + "0xb5a17efe3718fae052135040395bb000982bc3e56a2a980f316bc451cd048443", + "0x3e0a7aa77fb92b55368fc4e2e293c37acd930f62e16f40730a32056d77fb4071", + "0xed00a463a0b3dc967356e82092b7f11da72608a9626a4a519821cd365d6748a5", + "0x176e1ab5161d2e2707f5807d5ba12968392317bddfe65c37fbbb31b5a78f821d", + "0xff168efd47e8418315e9ae6b32c2cccaca0832f79d4553cda3027bc91f52426c", + "0x2f18779e1acc65702a8b367ff66c0d4a1c661a57b93a1169fd80d9167c6b153d", + "0x2f3a6afe316ab65e37a0e199b8329df76856fcc92582984a5a8ae62257f78874", + "0xf75149b090981b4b8f9de5ee51fcf1ed8de90c377a7e61e456fbbc5211a87120", + "0x7920ac2929631d14f212f0e28ff2a98d1e4440a076e84a705faefbcf9169a60c", + "0x19d7b081a5687b192dbd35e39e3368acbbf1ccba1f373dc5a122eeb5dec7ac68", + "0x60d11ab9b24e1ba60a8bdf90f2c3c442307d7f193a8b85b7f19d6caeeb487ec8", + "0x1c730acbabd9b778159a1d9df67eef9651d3a5fbdb853e3c92f56fcae4085a39", + "0x509cb2fc217658dd0e86f9bd23edcd3e2acc545cb9381618bee685e9fea9dc2b", + "0x1a7ac129dedcc62540df54a899847b67d425fbd7f71f758ba019d8c167f5cf50", + "0xc3cf3011ed898e3d95640f3c22b7c36fea4e4664cd0e6c150bc137dbd13f691f", + "0x7118209a1905f1b875fbf19bcb61e1f2a92f4e12063383a579b0b15f52195807", + "0xe27c52418369fd3079b00b4fb65966129c61c66815532471454e909e97785421", + "0xd493385fe86acdbb4d25de646cac1e54196dc6e77a4a4c170a1bbb92116831a4", + "0xb7f672a8b9d232977d833cfb24617b0ebd36c248b28956822d92b3748992172e", + "0x0679781e0ab02fe3844abafc1882813675c4f0ea543faf15e3c07ad52a668677", + "0x3334c8d3f2eca1a3f57030ee9b5c45856dbb89ba599854451382ce4637f8dddd", + "0xe28ccff42cf0d285bac6bb5287bb8d88576694a25e68c1444ac5facab395f2d8", + "0xb90eae2cd11eaa64aa81bfa9950300c6969aa62c74da79d7919586cd8339f054", + "0xa7ccd251a5b57150e7fbdbce925256ae548e8e2f4a15dac7c83855f049d63399", + "0xafa45c75e1c915b33924d9876cbec2fac540c4a6c06dd5a357c86ffeda9017f7", + "0x4201a93b3962b2e43851383286fbe780e5634a5a0ca881b7b420de03fa6af395", + "0xe4aa9dbdb5f6c36d9b7eac9727481e9ba5f8ac6a06eac737c2781555b918722c", + "0x4ccc75e890e76e54565022809ca0a06f8298b95c7bf72531eef7f9f5073e9d8a", + "0xd4de6d8b52931710a9a873577e2ff745a375addf8d0df8d76add21f1b28e93de", + "0x56dfa49f32b7ed0886b237a3b9b952fce621a7f330e5f6f1f9a3ed651d6149ee", + "0x5d04a7c1fc033a182f14776f308e06f7bc72d539dbe83529d57f90707c7a1b0d", + "0x103ffe878be56067b173d427bd1a4d2b46926b56cb31364a5e6832fe9e01a85c", + "0xf7408ba2a3c12058e6a7e75d987d22e3a10496a9701127d31c589b64ee6784f1", + "0x3d99048f692723bab2950b0e5de4c9bd8c5ed4b0bbfa960df2e80e8ed7e6dac9", + "0x0c38f52be7d54bebe5a0afdb9e583a06b6d75aef98c62a71231fee133b16b2dd", + "0xf5d4cc5ab4f9730f2a3f1c395881cd6511ec2db263cda417bf2ee4135ed9d9d7", + "0x4713456b5b66e238ca90c93faa6dacf8dc4ef1175dfe2e5c03a631a8a55443c6", + "0x80ac121e8235748f73a5ceeeb1e351401faed76f4d194d75256dc770000c40f9", + "0xc87397b64632484ee3572966459274543d75e5f6a756e854b3f27419fd29824d", + "0x2a6592565a35aa0725547b8933166c7d02f028d88fec2242538aa35be1c54be5", + "0x745b7774efae3cd22f393b07672b48b436eba2c5427dcddead5560a81ae2456f", + "0x34e2ae01665e4b14b35ae3f0d8767005a4b7f3f244d66fafdfadef3562103953", + "0x86b55fb49ff51dcf8c1c7798371851c9ab09ab93d1fb2a230d2827687bd50559", + "0x89c192eca56df103686713906396f88664436f0cf476d806cf1425a4470c294a", + "0x42673e1cf1659d78eb4fa5e1dcc5dc288f037c574b8b9a51cd198b7fd69a2b61", + "0x5453f86c61c0825f4ce8313006c7d9090e39da1bb9e5840fc5e8ee57ffc5ea76", + "0x7011c59e5fa7909352f31f6c0e53d6ba06966ac1c24ea8831546100e28ffb3ee", + "0xd8463dbfabbf63b09d071250ae2543e5a0e42eddb7c512d07f41ea2bc5a0efb1", + "0x89d626362a6b714fd4adcc3c968f5fb0522cff3a122c733bccd140def240b727", + "0x093eb84e776c8702da236ce2731cf082666c3a4e98a46e11f545bd6de2d09104", + "0x2c4d3b3f00c968552960368e6d16f792c94864906379c5852a76b359b68c5050", + "0x3aee7ccb3c4a781911b6714d6a332f446d2f98146bf6aa07c775a47b0474796c", + "0x10597830f072392310e1f3417fc6908951e1bda87e447767c3a7f5df639feae0", + "0xa5eeb3111da61320336168c292053718c52c118ecfd413f4e453339a0d5acb91", + "0xd171b1e226831005a106c0bbf887c6fcddeed5d2d9ef4f165c6fd67407d974e0", + "0x33e705282879cabfe1657c302f9293b2865a3891d8c5ef5264658c35b08166eb", + "0x33e705282879cabfe1657c302f9293b2865a3891d8c5ef5264658c35b08166eb", + "0x24b36d30d05c3505ca9ec8fbf0099247e7444c9d9a11e31e99be70e569f933ce", + "0xa949faaae2e3ae2ec20cbaaad9cf94f7facce50fd11c7c2cef06133ca0b9b169", + "0xf42251e38cc00f3879225695c737bad1a768f239c20fda46f1b5219d155da1a8", + "0xd6634e510efe25169fc8668988def40c5733a5e8268215bed8180a0e7becad1d", + "0x06638f606b78527d4f22489bd8694fd98b6de959601d1e97831c2dabbcd6a8e1", + "0x7f42dd3cd96a167c90026a3edbb3dd148ee1d3df9e3de4f4cfe389d0687c2e04", + "0x8089fd99c3cfffc3476a202c1f6ea2ff10df21b9eeec3a76b78e56013724be75", + "0xbc4f293e1a9a14a23dc0d5c375249eb7e4c94f737c6852234942c9cef82bfad4", + "0x0c0053c921e58658a0d6ae767e5bf3b3ca75f35483da529b2bfe02f56bb82f11", + "0x9345d6b21b4b377e40cec860fc325f554f216a7b0f438a03483df205f9d9a789", + "0xc97fb2d6490a8cb633400a0d4b8ea54f78e7e28c065ffd63e1a7223abccec44f", + "0x8c727ae024a118b5041ad6edab762fdd44e4c21961fd69ba345e036b3474713b", + "0xb1d93ceefaaf66115287b9c70529f30c5fd612bd0e60ea48abb4d9c3c861013f", + "0x3af60cebdbc6a1c55ad3e430539168d8d08158c45f311ba25170de75263f277a", + "0x84c879dd22154183749bea2658e09a85dca0607f8cd3575a479cca7650f1b480", + "0x10c30d1cb0f08c4771726ade5954ec8cca0f9ea37d7070d4e84d58aa82de7721", + "0x18045877733e3ae8c1ca96a26a96197e531c3e36bdfe1257c7bbeb4993644850", + "0xc35b7f6cfc4e55631e9c066bb8c942f94b2de68db14f1e187893457d450789ef", + "0xfce7cdef1d45c06891d4f115e8f3d2143908d6cf0a2971484415c2e45d216f81", + "0xb35d7061d418bd46cd918807327378714f6418a474cfe3511929b22de6087c57", + "0x530abf428384fb7798077498f60fb6a6fb027e4dcdc497fd8bac651c0d92ff2e", + "0xa6ce0782cda5fc8f89ad2214216dc1c2acef79723619d715487b017e7b32b8b1", + "0x77ddc1f10b63ed92b593ba90415344288e5868421e70736e6c1e35e1c9e03017", + "0x45af33d266aad90974d3b0830a89e750896631d00f42b367ec2d626d7a81a53b", + "0x7f52f65465036ff6c10710a42583a2def19fc0c78202707c7cb0abfc4bd36df8", + "0x5d80ae59e66e51d8435ab3da770ae283368a6af7cb4473dc61b042dd3fb8b9d1", + "0xfed1074c964c8b12e24bf455d79c210ec88ee8c0eb9c2222a1b58c4b554db356", + "0x83a2f0a468d762fe76a5e7d2c96de007b9b54c6a20d04908e493bb3cafcceb78", + "0xf749ba1589183b6f4bf36c6c8c95d5969844c56c8c761496a83f5db784729df2", + "0x1bdf1bdc0926f912c9deb37f943fe963a249bed88b1ec05664bb5c1a09a232b6", + "0xc0fb1b52ba13c41150cc4703974a2688bdc6444873eb05017cb49ff17d7469c9", + "0x1ffcf6e62368f240a35b92b8b344f89f23db1789133001a0ffaf70745141220f", + "0x34ae1b0ba6b1dd5f0eaf5b03c55232bf648773f2f979cad5aa599da8ff8f188a", + "0x3faab478ed095f5ae320b8e880a54065aeb24c45ec6956e807af53864188ff7d", + "0x55353247799cce302c556f07fd63b281700e46460f4cce4c42f7e7770a0fa3c4", + "0x6ea3bb161fa132951122b3386bff45c723bb6aa299a679fe2748628bf8fa2261", + "0x0390fc660f85ce05289427044f5d79a4937d981b5c98d17161ba426e2c5f3312", + "0x372fec6c0794a575054dcc1b98882eebaf12263150175f6d1801ada6a5d91cde", + "0xf60ace8bfbc9c1089be448baf287bbcb813993f6fa714bfdda944c15f249c868", + "0x29b2246572fa62e2f765520fc174fe6eb5eb49c157dc97fe69db2587e638ee4e", + "0x1470ae8439aafdcfc2ba36d25cc9f75923aa42b25a94227c612ed72451e5c118", + "0x7a38d94c08b71cf1ed3d02280e009ee4c4e459c95e843cc1386d1559135b0f5c", + "0x6bcc9ce0f83372e53aa19259a01bb7c0696b1fb8a0b93c4cfde861ce4c5bb836", + "0x400f66c9cff3135dc8e63230aa54a1a196d9146ba4f1007439d23c3810925d63", + "0x1fec67e4915ade36cd59b6e39642f120afe0a4e86e7c2c5757c1aca89f9e090e", + "0x8ed1f4e4c20adcd20d4d646069de5d1c1016c374b484fbbdafd425ba85db8257", + "0x8a85494592ccd6b688a860b5a65f0101ed806d6889a9082b84e042a9f760e3c5", + "0x8a85494592ccd6b688a860b5a65f0101ed806d6889a9082b84e042a9f760e3c5", + "0x5ce8aa311095a83b8e33e9827afe5d834ba7a6c7e341c09fe9b4f0e82cb7d6c4", + "0x61b58e057a748cd3c00e31cab0a890f93113bb24202fd877fa66b60aca46aa1c", + "0xb86cb7ed9ba12746b696b8cad535f58fd066f68cd5502972149399cd12d675c4", + "0xeaa454a30dffe183d109731e54d1d322fdc86cf57259219c30eaef21d3a3f20d", + "0xefb6848e83e99c4e9be1f21433cbddb31120be28316053b2651ae15f97193afe", + "0x023cf455df7b07d57a80e7b900047bcfc39d60200198345bd6b7e0b8c8ff195d", + "0x45c605efa81e4d943042c0910ae5384e8cc9f4664eb7c184e8d6d4a8b0e0cd5e", + "0x09a1b91939e904832697d0bbe2fc5bad478e183309bede0c820b5b927be29843", + "0x4aa0cf30c5374bf1939131001fbe73c1bb29e2de9afe95c5ed771d13c17b3820", + "0x18dcbb2730f0dd22acba33b6a5f98c5cbbbb0403c31774083ed5d3081bf6f8c0", + "0x7114410e72fb0f048ebf3fb2ba8a508034aa2bca27226606ba6a31e71a672306", + "0x0285986ad3095de90420d2a09515ecaefa8862b284ae15b60d1cc40e787aaf6c", + "0x76abbada2fe4980cd5b2e4bf896a75e82b896910014c84ad2a92a64966351318", + "0xd2294afc0f1b470bfb2d7bf4765fdfd544c44cb36d7c090586041118d981a3e5", + "0xa1417d7c36a1f9a584975ce38a35b16a2ee6401ccf0f7012e079b63aac03caed", + "0x2207149f47456a9339d494c8b5bb3ae7b26cbf71e460a14b19fb312669765b37", + "0x0e1f86a9849ef7a97e2f449045406eded513a36dba2d0e02c0766c11ece218e0", + "0x7d5412b2143eba922d175946b826be3b056b82f027aa745b14351a890a84d8b6", + "0x6268b1c18412e0914b52793c74f00fba4c6f262fc39743c4e3672e31bdc7e2c5", + "0x71855174dd9f61f71158ff6ee34d007bb40233b0f0aef354d30cdc04e8860fee", + "0xb82f2c907fc51c1b3f79f16c0547b54a73729308198f2ec0280737eb2f776652", + "0x0199c752c840b64d9555e5d12b127ec3be8ecc74d1608086f82772121eba14ee", + "0xe2222e146dc7b38ba1b56d66eab011f810daae6a254f60f56e01dd757353fbd3", + "0x8ad8a02948cebebc4a35e3947b07f7c54cd60d38fe2d9521d33f02d3a8f651d6", + "0x8e2246ab7d4c175f2f7896aa72b1a4ec340b374b9fa44ebccb60527a2e31b2b0", + "0x39f825028f02ae5d613ca61989d35d849538c2ac4d5a04740b1b4aa9f08076bc", + "0xb90ca31f87e03da8732f9abb9a92a1837f238de4ee7accff024f64cb88f4e9ca", + "0x3ef623d2577ba2621c712838998197eb5d6d7516c0346ca89cbc371a2fc013f9", + "0xe7fab43c17ec8f9158cfeac58f4a91e464d0e82df327698c62af200209d267f7", + "0xeed99faa54d4c025cdb43c343777adfdd9481141037c523b7a185aba1b0876fe", + "0xbb6501cfb9042fa5ff5431859d7c5067ed5f551eaef9a6a8b76e5800dfe2a7d2", + "0x549b03124c988c1bac441aaf9a45bec208e638fd7cf5c06e4868ee44bae9bb88", + "0x026e193ed74815b811cd221ea8b958f7564f527b64e4bc1c00455a200c84fadf", + "0xa744372db023af304447b15383ed12860af03cefd80387d520dc64a764c6c63c", + "0x5b7c24196dff51fe54b402d73c76144abaadb794f7610239650f6b3ad4914b97", + "0x4cdfd8685d49a7258a741f0c02b1438323cad2a779e7f2272ebe6e4da819334a", + "0x10c078da3f42d410bd536ec6040354ccc6f5491222844eb2781c847a51554905", + "0x7b1b288e57dfb3c728f28d34ecd529533bdf5cedac705dd017d8a7d1d32da9c1", + "0xa8c83962ff2abf572e3d34e68140f30dbe2f7b68f3fb91a1a42d7f72502b92ea", + "0xae6a0c89672488386ef889b3e975cfc1fdc01e4309cceff104e5ed070fd7c6d0", + "0x48c65e2850e4fe6f0c1cb8cd27cdf757c19b73432439f21145274bdb83da3ee6", + "0xfc993b90ccca2a844663de5a2c3c6c6e5310badf57ff2804edcaa623bad82245", + "0xf73acc7abc0e4a4bd9034dc27d5ba9a79500ef54fc1a81f2b8585c68c45ea156", + "0xabdb3b3d79becf102b24398a5a6986d49824b98d689b5da8287f78baddeda6cf", + "0xa31a2f2aeaa23d8ee2e5a67e371809d65898f62cf67b5f3ee7ba13405e7b9e49", + "0x199b5eb855c61272a81b3dcbebe1daa8647d4b5790fe401b3237782929ddd1c4", + "0x2ed7fa9003b938918ffd3d8be542806e255d4ebf7a67d1eb6fe16a3e05081a1e", + "0xdd8bd6365891566e2fd3af023b8ddb4074a6a8ded2fc22fe67924f51f89c9b6a", + "0x795703a5819e96dcde9e59f3d4c1d6f0849b7ff56ec2bbccba58f702be1cba37", + "0xb9d34d8223cc9a308d0b5918716e8051dfae240bdc16d797a190436ee6d717cf", + "0xf073b9e3081542a7b937c9cbae4172b483932b7c8cbb58603cc48fa8601f24e3", + "0x0134860d35bb585c3ca0e7c5d55841dfd216d3f23d30687900572d6e4270b5ab", + "0x976aef6f667cb39f33a26bb3b269dd453151c11a68873455e35d0f11e470caad", + "0x2d68c6c872abe540b903c5a3e5eeee9d10da181fd491100ecb950d6edbc3ddda", + "0xd2541df3326ae7e2f71cd74b0c2ac5a1b66c75e820254d9de3e6641cafb0aed5", + "0x22a1164d16c5206494c1268a550e046d18af52ccca714f3822a6e64c1e1c43b7", + "0xcca9250c2d5b4d8839137ad5cd8ff9b4bc8759a67c0376d0393f2a919a74a51b", + "0xe62bc074a26502527d846e423f62ac26c225a1459f1a85606397ca48c17d3c7b", + "0x96d640130c2e5ea4b5256be346e3b7284a1fc5b736a6205fb8e1d6a6843eed50", + "0x0ec8a64896999e430f7ec36715d1437b07351fbcc3d89a1e4fa8a6147d47faa8", + "0x6f3a42fec3366808c974f789ceaa93c509d3df1c96149a800570cd78a22189f6", + "0x8de59fd322eb1f90b0386ed9a66751e41d8081d0e560abdbdb50722f09a8dabf", + "0xf072336feb2244096fd7c0ef56eb402bda34863103905a4b54cc3469aef8409e", + "0x2889e7f8dca4d694f62892bd0a358030901a8c04e461638a3f5142b795c113bb", + "0x48182252f0cecfa53c432c7ecee0c4ff1c0a419e6cdbbd86837d100c1b734ac0", + "0xc81b7947593f8e46c9914a65ffef7fd7bb9257bcd96432e524ebb7256f405d53", + "0x8fc5b79fe859045bbe05662833ea624f4789ec66d404b748064b99764165ce81", + "0xa77c3ef2cd146839ffa09bff6abf25fdd5f8902f888e287403cda666663ed65c", + "0xd563c0252a061eccf5aba4d67fbd2975c80045eb63c64b99bca589098b16b922", + "0xb27a69a1314a53e1b5b7c7d629fb43eb76c98b3b1de5ff80ed63247c09206eda", + "0xf4ce9f4c03541253da237995a81b4e1f163524fd94fb82210b884c410d30b3be", + "0x265927f2d4c1e43427ab6c52c1dbc92e1d99ee493df0026ac11bd0b38a2e4f31", + "0x210c0627c5b248acdf922e2d6945f27c215addbd4d972d493e4399990fe7fbc5", + "0xecb666ebc5e34a6bedc195280b291abcefd2a877487340c0e89964286c4a896b", + "0xb0f12b65b143196a051244765a74d5f4426b473124149639ee66e65573c2d724", + "0xa7b53ffa222a13f308ade1333e97c6241cf21687ed473344ca26c34b9aa89c66", + "0x9969c5191baea622bf27dd3fc8a7f479431ec87ef3d45ea9f8cd80c591e340ff", + "0x8d6698d7bd8caf54d4afeba0f826065b9d817cf0713400d44343877b85d0a970", + "0xe761f2418bbca3f5ad303ad1453082af16b3c5ac3cc75312b3ae583ebe7fe6d7", + "0x002ef2fa2ee2bd9939d9a592c3c1347363c0a67a0408a0f1408abf2fada07115", + "0xb46d082d72e7b660cec05b02300c28e41f9d2363df135b3465ccf14c28b42e8f", + "0xd3c61174d6482f854abe29c868902d89d43e1b4ccec0fe882060aa99b50b87ba", + "0x6338c0c513b67ca1bec2e2b1b5155f591b8e3e98de50031bf052602cc47cad8e", + "0x182810fd2c0cad8df5ef3a2e26b575968c56d285bc5b3128efb86a269566196c", + "0x65ef46b98825f3fcc55f95a8a3659aab0b1c19773acca343f3308b96a1740c15", + "0x09b42aa2ad06c51494d99d01b8a7cc31e80c7bff9220e5f719a46d6276ad706f", + "0x6c80127dce5e0cc92ad91937954fddc419d2c84b7f6b9ed673dd315c9f920d33", + "0x29044b60fabb4f62d9244c7a3ab00bed958f1ae13cd7792386c666041957b94d", + "0x9643cf208f2630ac5f5f75b2f8607e93d4a49fa666ccd149cab66b9f1653df44", + "0x79eac803ae349603132184539a9c9e683cfcc400b01b498c9c131567ba93c427", + "0xea571cd950eb6478c1f6439acf4647269a0d6f5c0d5e9d61dafb453277fe6362", + "0x8df50867c41feda99dfd9bf5182bb0a07ce7e7557080cd059eeca03a3be0518d", + "0xb4d459bafa73ff2f2df6876aab44f4d2d7c24b0c2fdc22a3261b52b14a9e6dae", + "0x6d0ac04bf479409aa462f1bc58762ae919c43e2c425ec5f486279f9d31234267", + "0xd97ddc684c1b289fa21d8321c46d08ab12ed55730f58ec0781ff8240dd5a8535", + "0xf46794eee5c45dde0267ffe30721202eeacbf08209b76908f401b688f6da1a29", + "0x44a57834f84a1edf28fbb4033695870a8a9385194afe5290110304de8313cb6d", + "0x7d4ff163c38923298b038de6ae9a31b5bc61270fe67d967ec7a14551bef96a28", + "0x3ad42cc2d8726d0d76d03bf8285b7bb2b1a44b359e3c78a1b86446d199392198", + "0xee6e02e7a0a1aedc5727e2a668aaa8535eda9a686e9e78f4346dcc82d64bc966", + "0x57b1ed07026d44c5ad092736c5a717fe9db6227283f7a87b6b2b7519f2b3c234", + "0x833c4a39b0fd86f3f050af318f9f0466fdb627116c1de6787716ef210769055a", + "0x0643c9deaea070be25a15b3ab6cfac4dc3334a568668c5863e10622595090c4a", + "0x86a295d0ba3b2e4c5edca8742e4ff08415598dd2ebdf42d68690a16a7c68be46", + "0xd03cb48ef8b6e609ae1dfc635ee5a17efd851988ec6b726a94e497f2433c9847", + "0x175543e7b53a0b3d10e2bb112f83caa64443cd783914aa18625c4865188be8bb", + "0x603479aecbd87a0d0fb4da8a4a90f2d032ea060c62ecf8276148790786354348", + "0x6615d9f129f6dea16543f9605242199db2d04e0268d44cd4308c97b0fb2c9343", + "0xda1dd13f70d2d7a918278bcfafb2e4a941913768e792e4beff7ef982d6fee0ed", + "0x4565eebd215cb8494610032dccf6fcae7295379a6d296eb01ad0e5a0fb5c81f1", + "0xf898856f0eae89e76092aadb4880ac56d76a7726b61b0d719998deacabc71651", + "0x37bd23ed1fcbae706fe8aa2110e4d3eade604be195a59658bacdccd4f1e378ac", + "0x2693c26f2efafbdcba8830ae6131050e96c9f28e660811fc37627f56a01f92b2", + "0x2f78fc7ba21a9edcf0ca37f971db652f26ac0f67fddc9596316606600a04dc80", + "0x9d36b4a304be29c1da0e463968698ca45f5fd2920e2f0ec1048e6baf288de3e9", + "0x0bec7e74d58b15b191ca04a3a599e150fc86e2a32cb6b4fe7afe5eb149b1b196", + "0xd2ae75e88f5eb78e64343cc1654db33a52adb9a7f637cefb84a7d67e1f76cd4f", + "0xcb18a912a268e565563806ce155502b0d13e13f6538664c7118ceb443c296f1f", + "0x9d14d43475f029ea8377879a682199837c22f02050462b1b23485bdc25a6a7ef", + "0x6795f18a8b2d5cdedff29cc159392956e49f19f16088ba4f237b3ff6bfe1b6bd", + "0x966e9855ce8d9f3434f6881a73de2dac4cbc82f618252bd8965f1b263cfde327", + "0x997de8897a2e2e00d35c5b057000a47e99ec3085865fb2117983a41a271203cd", + "0xae03f96ec11d3938b7accc924df058edcd4ce1f1d2115e748850824ec9ba64f9", + "0xf9889dd4df9e3b41735238f4cb8400f60e7e99df85d67813e547c65e54d102f1", + "0x00e8d391229a80806ff96c50fda65c86a5df472532e4357a77bf40632ccdc574", + "0x0710e8a909d4c856fc681e713f6700f6956794545b60fa543a6cf6465f0b6ae3", + "0xc26058f7d1d535231219b3d1c2376d967468e673fb1f9b253d960e3e1535d886", + "0xa60a557d71dbef37914704072354ad244902c00726afc80f752bdfe75b2156c1", + "0x97ac55181adad7f6facf0f0fb4bfc477154867ff98ccf62e2eac15e45edf8f3d", + "0x2fe237860dfac2248b460d5b4513779679439edbe3af64ad1b00af29985d6a5b", + "0xac56fad060d4fcc19fb98883c49019fb071eec5c6d215bd14fbaf39f623b6eb9", + "0x799a66f5ed13ecbfbfa2c438dda2d529114505fe872dc7204febea278d3cfd00", + "0x984b77cec6fe3d098b0387039b7f346980f3fda8e5ab97634956cc78326ec940", + "0x0db165a45a8a2ee74bb5ea945f556ae9a03d8eac6b00ae6046b4413a71ac9c4c", + "0x81d054c04b219811030f9892e95ca81ef46d3185679f7a380f1c04362170a615", + "0xd7a6930aadf28dca7eadd9e4fc559cc0cf613548a5ed3e19a277a4e7f16f6ad8", + "0x43c8cb5ca43855d404096094b4b131d78f82a0c8eabcdba4b79fe86800302e2c", + "0xa899c614b3b9500e01b4f7d44e7c82d7cc93184da55e636bba0356befd958e1e", + "0xb4721e62e280ecd9a4562a119c0d67fff0424df858bc10bc9066f9dcd2d2f720", + "0x0061d18ee82825f81d5f24d4f8c4175b93703763cd087c6b78fdde911feed875", + "0xeee5b7354211a6caf6d5e3ac4dbc138cfd760762d84ca64a6f56a538125f0d95", + "0x34c1b239346ff9f032f57eb54f9438154f4c21eedeeeb150c7e84234e1a92887", + "0x751608755ff12d53bf2a120c934b544a5fd4d18158e0c54af71b4161a06aef8e", + "0x769de40f79b3d50f0371d60ce452d530d81ba5a7f2596c0b8439a37dd38eec88", + "0xa362fa94d4cb491b5199c1fc4830071dba135afeffc9d2f136492e72272a97ce", + "0xee4bbc9d8011fa4e2f204f1698f5f56a66827ef36d0ffa739b7c9dc8162cf6bd", + "0x2fa775beba22fa17d2d55264d4803667e3e869e1af1f8a781309c872d65fbe5f", + "0xd6406b2c3ea21bd8ae84b38a75f0128142ee484003de557b49665508f34466d5", + "0xded61e63e4025906533f6406ba4800cfa1d51c8f7a45e73870d9069d7a0b3c5d", + "0x1a71bb5dbd429912d57a9448e0e80d7dbaf8dc3567a3c51b97948af2558a1b57", + "0x96653d9ff4c1250a7329b7dfc3c230e8a43cf5ddd6566c605d3b6b37b576ed0f", + "0xc2400cf30cbbc1db6782e58e8bfd2a9e367ffdbb31815da372ca332221944819", + "0xc9dc898bc2f5503ce099c4ffc7b1f230332d7a9e9a603d0e1a8d123f2dcf92ca", + "0xd81ed5546ea00a10206af975a36c13ae3139ed9ba67e0c7f23a403369f7c6b84", + "0x20a3f6c8784abad2b61b920446428308d7062f10c8ff78bf1ec7180ba4aad30d", + "0x75c98f8b7340c8101c775194704c9bd0cc7b9cdb7e703290fdb5ddfa3bb5368d", + "0xea4f06a7e8a0fd39874ec2586d0fb192185ea47eb9d9c1ad7a6f73d685068713", + "0x15da2f2662a8bf2164468531c3817f49c0e39a7d21b42d2ebae303daa73d18de", + "0x059e15ce51ef7123b00e1996f50266edb28a4f8368063f5eb1d2dbbf65bf02af", + "0x18f939dbde5d5b675c0bdb18ac0fe28e2a7be04a8719806402f7622b822088dc", + "0x1500b000f228e4d9eb5dda02d45e93f8cbfb3f0b7a9ab6f789fc4e2cbebcd2a3", + "0xbe6802263b5422892c334eb53c5c5e68883352869967dcb21b3fccb0a72bfb10", + "0xf37e966ddd36e47011ad48ed278be7ebdd20cead4e015e1c55054566f2f26466", + "0x22894cf8033746dce658e08c0275d54b7bb82921f89f0f5da357589bfc9ee54c", + "0x4d50068f46e3b62fca574291747fa8642002ce83ecef9a5a8f13106b51642bf6", + "0x9ecc2b3d05d41a4296be5678394495fdea1104fdeb8f7251b1d82c605fb57b73", + "0x7352a571824f79b57eb44aa589752d699fa863055f22bc93f7fa5bc8ba87fa42", + "0xd33d9e07b3d7113d09b1800b4f9665b1247a3617fa1ffe18dc918665c4f530c6", + "0x22eb88187274e8700c343a9c69bb43fdfdb447371c3173a2453145b021325c52", + "0x82709e877af0c1ecaf83788ec55e4805fe4efde842201a032a2f7b735f8369bc", + "0x653f12fa96e8016f68f1d798aa1449d5b180dc76aed694ed5900a7b44c79e42f", + "0xcd89dc4f7b87753a0dfb875e5e16c7ec0459c05f134e846cddd5337355619c0e", + "0xdfd1b9ada4e163fabd530b7ebd48f89e9973710aa217f09df9d7fbb927982ee3", + "0x3ea96cc975bdb0ca0e7eb0f83fa5cf31978ab45838ac1eda9d9e72bc980a49fa", + "0x4de0e76b1e0e29cf47f7e3655564839f30be6731cc169692608b2e866246a805", + "0x1dba418437e5cab5deb8ebcfe2564dbcb7b4eb6fe31f2ba0f38083bc117b2d81", + "0x6d4fa590a83ebb93e3fb9b9129b99505b1676c9beaed65ab140308365d48f9ab", + "0x70eac30bfa651b25317aed8ca80452ab3cc3f4d73f25eff8926672b336ffd36a", + "0x836bfce11b00ab92c167e4e79bd8ed2c0b4e0a85f2bfbef121483804ae2c0209", + "0x0318dff6a4ca65f3565e7b41b9cea6962cd699a321ec38c454f352612d0a73a3", + "0x71211f6727759886beced5755f74529236ef9b5743490dddbd2d3305d90a40af", + "0x276125380cfeff649cd900fd466d9072e70b07e4691b7f47bb30d8b224d5112f", + "0x2018db9db95132819f636d2e358fdc9f5a58e1e0b387e934a606578657acdfdd", + "0xc0ee79d8d2b482ab1c1cbb3078f8e9ad9774b42958ea95b4d96ceda99a5ce310", + "0x5ab58752a096a5343a8d4efed6eebe4c72e1e976bb5baa193f66d15781adafdf", + "0x6b70e7a27c2c8e36915f4d40c1d9363c2662b916977aaae7f8a5b6c5ffceb971", + "0xd25db577cdbf76b01c6e86efbc843f58fc241fea6edec314d185b12c50a8684f", + "0x6101bd4289986961057ef51bc39dad84a17fe327e0a76737239569861850927e", + "0x648ed93eeab9f67cd5d89f274858c6319d9ca3c5fb3eea092533f32688354cf2", + "0x0c587a837b36986aaa9df913cd3a3c03df34c8c1344bf7c77ce4fac561f150d1", + "0xff656a061375cbf23fc46715c125520d12029cb9314f222633e26c58fd05ab43", + "0x59684fab079b5d107f43cc97febb02ce8cbde16e9e13317a9c32a6945d2939fa", + "0x20f8b55939ba08af8a0fce0a25d2eb239674bb9c8673eb8c39e87e0c0a92c972", + "0xeccecdd1c5f8838bd9c4fcb06b3a6fc696cde8bb216ce006ac52ca10a01c2da7", + "0x09c203d3294e746ae98274f91f9f6c7dbbb38d2c92f4cc89f26dfac92dc6a4d8", + "0xb1b92ccf2e7dfd8410d6448a2634e4f6e7dfcfbf6547871a61014141a522722d", + "0xf59b0a9830dd1ec5771fd9ed0b3cb39c1e30936dad8688deba090620f0073842", + "0x2bc1e5e1892cb7e404e1da9b363fec93252d1d9737b4611be730e1cc68477c30", + "0x871671a8888a8ca8b2007db7aad88dc5a925e42ea1ad1751e2de98cc1279b55e", + "0x4b71711ca4e3d02879e97cccce18fd4c7dce41e7cff0c803b53004912b3ca0cf", + "0xbe7d98d6ad720124bb9f0b289535a6153f1dea180547294881976b57e3d9c370", + "0x38e5e288800fce0fe1de4b1769c6fb9af657cb614b84cb71e18560064f1c03de", + "0xec81718ba48abef54eb21b65a2582f8c8bfd30476db4a974094430fd886f391d", + "0x6e76015f1b1b0e07366a74656de7247aa1cabada51cad301ea42f27eb44e51a0", + "0x59e9d0e76f76aa171a69a502678e78e7ba5ff4f3a0c3182e453ee101eb794dbe", + "0x814fc0bb09470020d34359947bd637382cc80d63b3a212160a06bb6a70185549", + "0x7e0c6d752543f9d2baa70938a1c768482e2ba3a4a688f93b178c9c6feda5681e", + "0x77541b066dabf3827b17dfd4454da5ad3b05a540faa706ecf05c5782acf72b19", + "0x94b6909ec2dca70b801f63258365748e53483c1581c8abf819999abe600c42de", + "0x9eb031630ad0dd8090be11da36ac09db55b00c9c42b0868f4ffce09a7f7c5233", + "0x11426e0c1204b387e1d682f94b9eaa76d1afd0974ae8bda88be44e75c515befc", + "0x4d8462d07e5b2d65e4fdf8edce2445ca35a69e98b0930e50788516937207d0a1", + "0xfc88f9ff39ad2ea65a528d065a5e62b05883dc5a4a2a83a1a28295bbf5d7395b", + "0x044ed7e4976118f051808be0c5eba2979e347c119fbf09315eddee2f28ba1709", + "0xc9c1101c1db126a02248ac5c24ccb623a26ef0ec8bf732a7ba27508dd3939241", + "0x5061cc6c7085fbb96dbc3672a49a475dd2b98843c0c24c91e4aa300e2aa3eea3", + "0x02428b0932e252e2c431bb1a2082f40607c07e673b3f293745e6c1965455e142", + "0xf6defc0dae6bf3f779eed5d73ea54eb509dc86b337472d45cfe392abb903cf43", + "0xc8b524c62f6afeb893154dfe77643717628b73f0f30110a7260ddfa5775a1952", + "0x8cf2a940d51791a75944b9ff5ad644c175100da97832958f88b22b303b5c11ae", + "0x3228adb384999a7681a270c52f5b4606e1bd7824b1e59c731ebc45423a6a8758", + "0xf506aaa90d4e4595fa3231a8a1fb9ad74f689f831edeeb70bc892890fec6a7bf", + "0x479f6c83264d07b5ade7cbcef9fcf7e767764f12eecee92ae70dfd62de0e1b14", + "0x314c630d516952402350d056dcd746eb8538e3df9c86efdc73655a1762a2cf70", + "0x0a625b216bb2e589c60493455cea31bb4bd36fe3f00fdc6d31ada360cd833e55", + "0xcaadc67fbe2f3f03d23dd37a5f0ee2b0b43781abdeae3792c61610bd394f3ac9", + "0xa58ff4e607065d968cff137bd416e635d6e0f2e6a560ae5956b47d7e66f88122", + "0x426b7d1ef0714b1d859a27f352b2c57fe6caa31bf01ac55f1e2b6bd53297c229", + "0x8384459bc4ab6569265d4fd45563d86cf643ed1c4dd96b891a28217b350f201d", + "0x09f8008e0d5a223e8dc2c41692e0919773f23d66931894942a9a58472e87e4fa", + "0x597e7237b7fb91c19d596dd960187050045982d42ff87bcf5d89cd4b4dfdc0c1", + "0x672a78742d419f9786aa1a438b91923c73923a1ea072a25a2ee85eeed911e7c5", + "0x3fac9d88cc14a9b84fe93d22ddcf7b4454f7d3cadc6f2b08253f999fceae33f6", + "0xd3f5c2626b81b29f04cb3b569a6a1e782021abc3a83c513b0b737b16ac04a1c5", + "0xe0e4501cad4208e92bca6f5c56fb66e018b4305f716c00ddd6ada56ba682f1d8", + "0x93897712c979b097af072e80b69d9b70ddc9514a4cfd59311a9ec543f65e7dc8", + "0xac669a0a973d5273623ae03e63ab9d8861f4fcf4fea6860c3aa5b0b14cadf5f3", + "0xb27f95cb718dec6394aeb9e49e857b38d914b426097395b083fb23157bd85471", + "0xee02f3a1faae99e69a986d26e8a06c4bea958da4497b0c554d07e1eba323ecb3", + "0x44a3cec5bed490471dda741323de6944db4f9d43252d2bd8a8f458031fa4b114", + "0x7cc668bafd3f174778a2cd90b1222cc03db7ad17f24fb15110af08ac0bfd02f7", + "0xe85b5b1b07f63e7c89700b1714796812cbd48458e5381088bab3d486c80db746", + "0x2cf3f826ceb00adad39637a88741d8f1a182deeb24c849b718efa557541f1221", + "0x22e5ad8625efe3c37bbbb7c3644959c136e5565e9940815f889ea61c701b978e", + "0x5e44c7c70a35845ef917d577459608efdf053a1770ea910e41e1370c9115832f", + "0x52997d3f1a222b11aae8875698faf6719cc14beba056e18aa6c5aec389528c99", + "0xdcf9af84a9b424588894052486ede39fd6a8e4f0ac114df31adea9fe7404fe9f", + "0xe698c581886a77bfeea127a6688d268041f65811ce8d9effd45fce322eaff51e", + "0x8340e584b12e0af15ad03235b718763c314a3fbf938e97621b12ca9deee8567b", + "0x402cf985ca7edb5c9bf57fc40027fc57c110d415e274a2d48bbe4802d14e9b63", + "0x025ece0cb539652a1c38af6d65fa2753c53ecf055880bc86c6d5e6686e38e4ea", + "0xf071cc886f84b0312ae0177b9713d2cc78392b3064fe74e013b90b0c93ac5260", + "0x2a8b0a9b099ba581ac99c3ac09f0292059af2c139697472021f6f668e029058f", + "0x511fbd562462cb0195f1363c63b738c040e1abb279385553c64e986455426f87", + "0x8aef96a7dfaaf712ab068ea4dda94d8e2d8c35939f513c0e99b6f07ad437d29e", + "0xc169b5fd6bfac9d9d4e192d02e9bc585eded4c9e962b2eb9b66d6c07afa381c4", + "0x24a17fbe3c5a4cd56d8f6b6a4e11500a791810c3c4410c50b95335a2781e4b8c", + "0xf70b7e9c30d2865da498f83efb048e308f06b121d5e62c9aec88eb3da7dc1249", + "0xe2f90483b55e43466f90fa659c4cecf691f942e1df8d88cfbed2bc8205df3ce8", + "0xd46bd8be08c46d0cade17631d927f9d192b082f8805e75c9989a2ce9ea46e9d7", + "0xbbd86abe6e02e6ba8965b5c1beec580e9377b453915d7c8eb7cf7c5c03971c2a", + "0xa1a7f3a8f191ecce2026cbdb85b387c268c60ba8ebb0413548839879e9969831", + "0x295ab06369719a81e8fb193ff58a02a9f78ba52a59161404b9263e206b1dddb5", + "0x4833b3dfd3422930baa36898e52d432ffeccf2842783fae13b8d38c44d65f76b", + "0x578ec4c2d4a9d174589d1a78480cea42aaca0bbe44c9fc91a0c1c8d913c9a4c8", + "0xa3a356a142b09b3ab297e604de97e0786545c86cca2f6fdf3c7afcbaf85c6b13", + "0x7059befb89994fa5ab69e030bfd59fb2eaa91506771386a0c19b47ea9d1c93e2", + "0xd287b0a0f7b39dfb5d1362cd900100c91bc10b31c2cf31b4f0c46c8aba020950", + "0x6e60e4d71c4c348f96cf75e9fb265efcf511c7690fd103c62a95ef34f9c1b0b3", + "0x62e28e825f6bc969ecf0913c087aba7c4c47a15593c60f2e5ff5375538dc5501", + "0x7d23d4a58526e566dfd0fb05ef9830cb96914f8bc639b681b78d1b73198a0467", + "0x692e4831b4e5617676e7c7ada45eb8f3f8a44aa03bdab6e77b240c9ba3187120", + "0x94d04c50372c8ee6e9aa537574135bef722158b0df6613c2382d4f8c7e45457c", + "0x92c8c889decf4d2ee2b0b3a55962918c3dd9f4e67f1ee8360ff5d344dee60039", + "0x97f168ea57d670ed32951a05a5d641709f2c5118a6b8d9e69d931f822f0827a5", + "0xfe48777a6ae8b67c3a77b13aa190300bf6906d54699307a6394c1ba406b04762", + "0xb22ead04a195269bf3890c97a73377e7f0e020ef7aadb5c801ebb724c14cf698", + "0xca4f519daafccf917e69ccca89a2862b34284bde1cc8755013481f885907e86a", + "0xd918914fb4ea4f5f98379d0100f574730f1aed39ce2c5aa714735916f41a8afc", + "0x45abc0eb464f9be353c6dc0ef10d7c04be2d986dc5e8934e389186043c82d769", + "0xe55416813fb7eac9f20169181055cf57e0951f6052d9720928fa864e924c3196", + "0xb7d0431b876e1a7936f67bf1ed080e151452eca3900ee0c5405d1250d0e1ca62", + "0x7a6b1e8fd6e05af48e3865ec17767677fcae929b07776d291d9e3da14c083656", + "0x0a1275d044ee63699b7a451aa96b1d7af848b3edbf19c80a770c099d6fe0d0cb", + "0xaf1235df7ef9f404f51023f8e6a15b6a538d2134f26ff7fb78ffed576eef41ea", + "0x0b1f1f1d827d5bbd0ff9cefd45a67969ab4ad8d4b39d854a94bd60881ffd940d", + "0xcd140cdcc5662a29db209bb28466e3af28a949b9771e79398ac885d36cc73e09", + "0x4d9ba24661f2c30947cdd741cd8df4cb4bcede37f4519e6a0e9c224e8aa686ab", + "0x77e161adffec057c9a2377550cd6b840965e1ae3d6effeb33b9b9f6203a48b0b", + "0x6dc3873174c3ef34ac3058c8304679a3984b1b6efc1e4130b7f3a0aa4b5cd42d", + "0xaa9fcf70bc701b37233fbb464fc9144224a3d351de0789d5bd934ca261c296ca", + "0x051cd5cf4cc57504922deb171e974ccbb6d46969bec611e153965458f3e84dcd", + "0x1e3535a5293f658a9c072cf28032c6633876ead1366dc5496c01c5b8d1a7c78e", + "0xb11dcc2859a0a276a33af2e70d12d0eaa1072e0101b4c17348a8bf801ef0d27c", + "0xd94bffa1ae5d2376c1906d5bb37f23952fea9a32550dae81513dbfc70d2b60ab", + "0xa805298a05743ab8106df5171aad6975e79177426948773ff4f710fe45e36fcd", + "0x5dedba7dbe175ac5909630aaea05e41e1532008be34432c4386939c62009db8e", + "0x059be6aa3542dca78140c697733d131e38278bccde1fe5d72acd20d903455640", + "0xa6fee46d99423f847faf8fd8f02a945e19d40c4fef0c2039e895fedd199d9b99", + "0x3ebb56b564f175418287b41a76bc1b4a93616439496b208e85a132a1121057b6", + "0x2a37ec7422ddcf87e98388ec7aea6bf7ac9b5d059c33444f1f3820357c2321ed", + "0x2d5357f0147ad3d0452cf14816b5c9d04b13cf06cb640d804a08e92d42aae605", + "0x0a2b947074242025674685a359c4dd1c11dd81dc390fe01dc83c0e4a4cb54422", + "0xbed172570f45243ad11ee8b41a3350eb8e73e9c5499cbae343773a9080c579d0", + "0xb02b2be358985e85c00d1600840268c5eb4670be035e9025d0098b16e2e93837", + "0xb4868ce0743acbfc7ebd8961b351b91afccd1bae63fff31cf7d0f1e759c43093", + "0xf01c1e0c187bfdadeda9eed277dbfd87fe4059cc5e373fd0821675782b7168ae", + "0xd98d3c737a0dc3fed2ba52d48b334778c2fb8d31034f8e4dc8e42199d92c7209", + "0x041ff76b66a3db813cd2902e7ba642d82f5e7ff9d2718dfc64809f0cdba96962", + "0xbbee4b665318e86dd1c065c70bf8ea0ecbe9ecdca71d33d028e2a34888b52538", + "0x7741992e20d7e7d3dd142ea186e16925cb554e7cf3f2ddaacbc990bcb407d655", + "0x96866c98e4209602a24782d480e960659411492c54cf4f252f8a78ab96eeca6b", + "0x9e4939536f92a1ccc01d1f5f8eaa26d8ad9a7f867ad108f4d99150901fa2ccec", + "0x215086d8c8cb44b93b97f0c40c8582231652e38122789f1435181b680c1825e5", + "0xccde89ab4d8ebf6b16c17e4b504b98dcb19f0098afa066d2524de22cdcabc7a3", + "0x96a38bcdedd5b12e3438564e2c92b0b61d27fe4bbd1073aa35b185969a7270fa", + "0x7c06e06dc94b5ce4eca34876430016887d63f0cb92f7ffc33b2b849162d95b50", + "0x36b9ad85eb68bb104187f9093413e3ceb2fdf86f18ff8a9654aafce62a6c7668", + "0x4e6ce3afd526038122d82198a9e2869b7159c4ac63fd65b8f0c289391a865c03", + "0xfdc8bf6f19a44065c22454d7998db8240c58ecd05182ceca5f20fe85df2e4a60", + "0x5c97ca9ab7ce8d6007f6b42659591a5f3b9c32f207c8891a91a5b187d072a12f", + "0xda1c09ad8ef99dda599ef0c6491aa8d596fd8a328111d6dd5d6fd53c9cedf830", + "0xfaa35970771ba9cd0d4c5fea3321a4fa20aeb8148511827af2b836817760b638", + "0xc9b3609b0b02ab75d79ab1836c8d6c954512ec342b1fbb7f3113a419f0364015", + "0xb95cf997e789052f55d5c8e028a405e19bddabcf4397672dc8e58fcb50042606", + "0x87fcc0928a4060492e0d17d060fbf0b8b5a0367709b551fc0ecbe030e4848b40", + "0x525daa9eec9c3f24a05184b9ac06717ad5d9c8050271e5627c0a38cd64a6a8e5", + "0xf2a296d7efc8a398d09a29174c4a31639883ff25add20a541c3cb6ad02001939", + "0x8f2fea47c89ae85d0d792c3625dd2eaa794dfa3539d35e9960b6d39407739ba7", + "0x5c64baa5717d14bbcb1d1fe8786a077d0ba4b0fb20fd05e2ee272aed552979db", + "0x469f534d566cb8f8f3204328ed6fd08ac5282b83d8b7bf13605879569bb84010", + "0x51e51cf0a797b40aa3e53948118fb6562487a59a6a14362cac048b76e27c9657", + "0xbdc33ed434a61f0fe523d89d2008e604a25738277faf525bdf4217c7c5c2ad8c", + "0xfb159d80cc32d73509632f88c5cc4bf31a85d58954c2f929fe0bbc32ded66f6e", + "0xa6fdc181b36f85f8ec937370c61ff29a7a9d6273c78bc7b4246c33dbdaa239ed", + "0xe69f6ab6fe66bb8d3e7c598c0e75f6b48f2ebde9e946778a8d41761e78b8e5fa", + "0x38ec47b5cb83416d73b812a7bc7b384a65e455197a04fdff966079d1899ae283", + "0xa3eff789a1516dc26641fb64c834aa773f7e096f772d414a50227213a5a8d713", + "0x292eb6fd353d342d3181be08fd9ab2b52f19c3e0039a1e21330dc41c3f881fb4", + "0xf853951bdfc6e46c7e89e7540830692a39e031df06a948d151bb78b8dc45ab31", + "0x08140c11c071ef9c53fd211cc519ad9b89a92e366734d7ebcd1a058c5c75e0e3", + "0xb87933fbe040ed4b0ca8b5fe81ac3a4f4e58784111d4a89c14b8b735fa8342a0", + "0x17fbbf6583ca7b9281fee46b098f7e73099b85012c1b25dd171b1fb501c45810", + "0xca7c987d07ccad191876a75a56d2cffaa107b375f9a1cdeb646c3c025e65cccc", + "0x3e6894536c89bd674a11457ec9e3eeb09aa92b0d2ec582e87bd38ca24194d2b7", + "0x3527bfd70aa61f50f8b4b05b8b944c8fbba205ed95badf052158e0619d7f2594", + "0xf3ba57561463ae08aa8f0694fde41c277f3422558f976d67a3117ea613be276d", + "0xae9538fd66dc94d355e92381aa95203efb405cfec4f360bd41178b6b6e297a45", + "0x96363e63e6abe88ca0a3f2d6677ecf7bed3dbff2e8a530b09eaf1a3ee910c0b5", + "0x760485a4119ff9f7f3ca17931e678fc897a8bb97c43ab39edbeeed42d706035d", + "0xcb039d3d693e548f321f33500c34c2ca668888df311de7f08079f43e0673575f", + "0x7f8bb355676cfce9021cd0a9ffabee160982c61f2589f6ee34854de6c74b7dbc", + "0xdb68f7b8e5a7c59ca355d18d75fad8fcd3263251d81266f06ee6d4f000916b32", + "0x231cf59150cf743000100d99d40accc1013d1ddc8a8e0459e203fdd40d0c1c34", + "0xe6129fb61f9e5b2652c17645cce80b4865cbd1edcb4e1c21fa465256110ab0e5", + "0x45f8c3e7dd8bf9ad33be20d32e39bc1c2863464d7fafbc692940cf735b7fa39b", + "0xce84c6ba17f9de5a435db7d3a3bc281693ecd8d0261936e7da65d37c875b62ea", + "0x5b60b9a04ce2987fdcd013d43a1fe9d4a9ee8abfd2f0698f38f2a931da4fc7b2", + "0xa17d02f84d962055e6d0e109a8a04392289e15177ffc49a87c10e1b806ee6208", + "0x7b26f863cc8e9d60891be71c0909e3c0b19ed3c8cd186b4b02eec145e2a186a1", + "0x7c866e7266decdd2c567d584df48a10cfa10d511860c7c3092efc8097f51b059", + "0x6fcff2120ed74e53344ee04db62c1db3b651ce6dd4155f9bfaa3296b49013c47", + "0x0909f7a5abb747849e546cfd07dc02c317375e36d919d6970e79a01849503eeb", + "0x637ca30ce9b8a9af230fb5577381e5ad96e4673feb161da5e2c2b3403904e73d", + "0x3300d720b60226c9e5e9f5a73eab11bd1f1ebb482036e342205c1205028452b4", + "0xa29f41e17c5ba683b3481ae79b34bbd3abfd9d63f79be017a4417cd6feb5d89b", + "0x9b2253536a412c2c882419973a7ca35600d714ac2017d296d0b0ac9d8d1aa99d", + "0x4f6779e668e638316464962435b7a9542c48209379b507fd7b365e9883b37a7e", + "0xde72a2a9b3c281b3655d1efed6873f3c92988dcc35b5c7fa02b2c0a01fda21a8", + "0x8b4564aa911356f9dfb8837d0061dc636b64cf7d87d144a0b6f9709677dd0107", + "0x5bf026989a76fdd034a6a47e6890f2f687f3ed29c0a1967b812ae7ccbfcc1dcd", + "0x717b444f5df4502cff8fbe8747354cb5cecc829acea01f951c10ed0475bbc722", + "0x7a6e90888c37b726d8c9891bf19c20653054c76c9121559db51c236aa146c1d4", + "0x9d63f0d1e4a02a162f900c1b6725c52a67dd97fc6a471a233fa636905fa6e8fa", + "0x3e74a5dfb3da01890d9726421c9b7bf5ff8ec96c6f9737d2788c958ac7f0052e", + "0xfdad0becccb0207cafc6d8b80486e6c501db5d032a70aaf23abc0029e9a814c5", + "0x5e161b903a845855364a84c2d46061a5c12240c5d6b130d8ec5005b5ed2c7488", + "0xc257a6fc114ee228f0c24162621c4cf49ca9fca7042464033c7ad88ce14ea037", + "0x4cd9ecfde22e2ad625c4240e6b04e832227ca209e5c6691dd203c5cfcecd5dd1", + "0x6705c0a426999258b71aabc0112f3efd685dffe0adf06c904ac6d376b51bd821", + "0x3869b223c0bcc0cf90123c358dfc53ada9e1874c97fae558556b0822b1cffe05", + "0x4dfe560a7b6fd1153f2c57fe511ffdac366f77267bec9428f342c779c4291794", + "0xc8d0877d5f568d49d1ba80d2001cf531e1bb7b440775faaf1bf8a77761968e85", + "0x7f2c20d7fc58739b44b1cdf0ed627d93f08ae410b7ed83327bee52f62c476632", + "0x3638eb205f2bae5519fec73b8c78208ab6ca898ec30bf7a5de61ba34dcd7ba30", + "0xb28f6de399e77b2ac11069f7919918367fafc8033f5d679cfec189cf1640343d", + "0xb6e3a59bdb84b8e4bca6ae938e757aefb050f8a704492eebfe0cc7dc664435f4", + "0xeda36fa9c2fbe6df965e50ae16acd2ea43551457d594bb528b44c7c0f40d3e2b", + "0x0260f88ca7e541aba6f6f42f83d8b24c62141e9d3f3be1f98edaa7598ee855e7", + "0x832eaa87fe9154c15c426329e25c4e1318d04fff4ea95826f93c76a4aeeb1eca", + "0x7605aa5c23fdc00618184839fc513fb047710b9c5c15ee23fa66d35110280292", + "0xe15435281352b4cf8c6843b15139874c032948505e140267722d36510bf8e79a", + "0xc549340ac8fd1b13dd83139198ddaaebc64ac083a822525f36860c052d223bb0", + "0xd2ff2510ed7b5860c43574d68317cb6f2ce98bf012e5c03b6178aef15b2b9c37", + "0x79b173756f63f1ecc896f061424db97b539e053a091b9f4a1a7b2615275f735f", + "0x34ee6bb6ecbab398972447dff4e54ed2c0606f570607b430379cc1610dd1f7c4", + "0xe34f9da0b38c99bac039a5ffb267374bbab6d1b4fd8147656cfa02a82b3a6285", + "0x4786bde48645ee69056f45934892bd2bbb8fa4ab712d0829f751a18548580fc4", + "0x7baba5b5e6daf848e89e5c1a943e78dc3c121f6ed5034f54c5ef0e8dac447288", + "0x8bf8a1a7df791bd9d994d38a1a975d7f555ea0d7aa44e48f40580645392a138c", + "0x2839387a9bfd618b7f231aa08fadeb3fc152c18f2d8dbdc70f76479cf9eb9509", + "0x109959e3dee35a4573cc4deb6759f040c1214c6669818edc001bc087309f4e2a", + "0xdd004a6e065c6be55e6194128e53d6b817da5a758cc9992f65cb48e6ee1b9cd1", + "0xb57620b75297ec57e5c8b281f76eb2cca415d7aaca4df866aa00a48744a135a6", + "0x10806efc8661663a1911b67c1ba9ac4732a48a9d2ad8855df10957c740791eb2", + "0x1b856f53bb8786f1106da04647f818814b17dc5f2c0ddaa8f224879416b1c515", + "0xb8cafec7e21b5bc140c5541108739487b0c3c8cf2aa02ed06163a969963426be", + "0x47082839c201ffe580a07cdf371547520e168ce1e05090f821930192dd4e6986", + "0x72e9c7c3598c6e60b16a46627818e6cb71097931067112b3f7975e4904f0dd92", + "0xf6b62f379d02d6754bc907ae2d57bd2881ed33298d49dcac2ce14b49242fb0ba", + "0xccf4d196c4ca23d24132c6b7780815c59eb9e00d2c8048635cec1355deae26fb", + "0xfc95b3adbd39430f2c7615925f7512464f3edcdd35e99c1b3965a51af7377f02", + "0xf9362d38b8a741be921e841392441a03bcfb0956480bb3a24ee880a8622e8eb6", + "0xaa2f6e59cb9bdc39b8f8eca054ba7d2e357b9965ac5bd623c1c9274aa8c6a86e", + "0x6a75a6b80edb26738c2cb65e53801f702f5410919db1a99888b41bb18492a494", + "0x35ef8dda34da93c9a35e246b3c25695eaf5da3b80ae118092cf51f2230addecb", + "0xba11b4e60a5cd1c9493b237f341bbc02ba69051fdeb493f4c098d17a4b7abe54", + "0x8e4cd2af5214a3820aa5f9cff713fa445dd148ff6cc65a6a849913390b909978", + "0xdbaf6f1175e75a93dc4556c803e440a3e04e528eabd52a67a7cee11a0038f045", + "0x836acd8726b49ab237403473fcc984576e697a3338f9867ee151beb9c87a7a4c", + "0xe864fe179571de3b5095ce8a73659eaa1ac7c8a6a7cca51a446be42dc15704c3", + "0xdc980e873b52eb87c876ed30decf57e49e583db8cd02f401bd8ea4634a3666bc", + "0xd6c1ebd9c078afe050cd8e0f34b7395d11ffa4777efcf8bf56e0090578ca79be", + "0xd338423f7f234972466863be6a12426d96b8c7129a6f4b998847f1d16220554d", + "0x3ac3f1dfb2f6c5544ccd713042c2a0efd1de94fd7e1e6af0dc538de631bba142", + "0xb98ae4d99e97aedf89894e4904829a46eb9338d65b9a825648b1cd44f75c6c94", + "0x58fad67b1821a1b7ec6ad6276f57f5aab8a927c7670ebfd66eac3e0103573fc2", + "0x9ddafd28bfeaba54faf828294620c3873583d99ec99c01a93ad16839106c7be8", + "0x31d2ef4fd434eca68139d4aa43bcbe9c2248d4c013aad11113dc3922f7a7e024", + "0x6a9e51d0c9ea1311d0b8a81326e5855c56ba77cd38fbdbebc629ce78020f2e00", + "0x6ef72ec058365b493ac9e27801183fd155a53edba05db5225f561ed410a088bd", + "0xd72ada2c86e5c4166ab226510005583164e3daebd6b7218ae659551ddb2a86a8", + "0xce1622ec592aa58bb6a494e88b4a023f28e2f52b1f6bbf1fb549359479df93da", + "0x884e56805e6c3c3f7754b26ac2e0b83fb9950d5c7a52273f02a8ba19efde936a", + "0x12e2589ba3b298c2f04e66a9c90471cd3fa3fa2b35e6d6f1f3fba4596ee58056", + "0xcf599ce5e3df977f30647ee8398966c5628d8eb55f2afdc22b61308208cb05f6", + "0xd82b8b53aff2591e12c941969b41d74f3fc61e46615d051c9251abd6f6997da3", + "0x56a57b1b0782a15c504c81bf366b703c7adfab69c77e0631b77b1e446ab9ed79", + "0xa1b55e708a7c9406f6c8ed11baab74e52a6ac713854c0b34a4dd701ef0e932d3", + "0xed1dc12d5310a2eacc44906082185935db19cc4e4f3412705e8e2ba0c6995875", + "0x97bccc69a204a44f1e3c02bb4dfa21bd0bc6b1b5b9381da507c6605417a1c686", + "0xea5b7a5eeeac2da0e2fe770ffb4c0b72b42fb85b889df601c93861241da4f8ca", + "0x776f050a77bc2e2482fcbba5148ae373d3eea9c19833d871f5f93316787deb2d", + "0xe226bd4e31508261929ce7f62259c51a6e28166ed24bbf24b976fc5636d542da", + "0x7b9fa25d6e4313f9ac8637d42e1fd34c271edaa72cfdade9bf46f480efd3e90f", + "0x1196dbcf3d6339d4740450d59fe21a38c7eb7d89c84d61fafa0017a42370eb8c", + "0xc87ed19cf048a53e3fad2825cf523868e15ef31495081ab3c9617d8e1f910ff2", + "0x57239416acf5e2ecf5cbc37d9daf08983eac7c3ecb037f5d48a779488389d026", + "0x7230f8953568ad9139b1478a05a88641239b6c6eea0b5f809bff7b96213dc482", + "0x9bf5d63bc6ebd1b03eb79024671555137aa62b8947747fa8b4f53bdb833cfa25", + "0xf79108b3b13535996aba6ed6e6d76b8c4daec8818d7d7da0de695a14790d475f", + "0x98796a409cf19423a505f1c1d85f80d9b86bdaea4e56eba434df62c7bf7ee8d8", + "0xc822d1b7ee7504e4f2580443e29e2dca1925a59bd9fac6116bebf4dbf3583009", + "0x518601394250b80eb4597d5a6f4fae63351e70ee61be622d374ccc3424a08a76", + "0xd332f734b4f5181753addc6a4f55a61e5f12b1b8faa428b950b73f67a0d6c464", + "0x953a2ce196d4d8b30b5cb2fa61e247f42e8542cfd3342889e6d5e8a5da1edb72", + "0xe1975141e0f3fe8f39597dea01ad5c44137af5f00a5f392637f25c99a92baee0", + "0x905f642eb7692a2b26d1abd2699a8bfd33c32a1898de266037f2428531de7902", + "0xb27bf36466b6dab9ca4eca012ae4f566b19db5fa3d435a58fe078615a9e56542", + "0xb42c81c51e415ca8dbb3de7ae97fd77965cbfaabda5782a2475d07031846badf", + "0x7a203f7abf1869818c6895bd9b64bafd854baf8194ec479df8ef02dc4acecf3b", + "0xa4648856263e7d1a819817faecfc483674cecaa211f1f8f3f74de6ccaadfd288", + "0x5549bcae41667136051889565c3900aec9f5d58ba20a0ad7ab76794c09926ebe", + "0xbda107179c721e2ff1bad6522ca252972002c1bd560facfd34a5eb319ce0157a", + "0x7aa45b6153daad63131e1301d872e1b706320a641b734864d7228d24dbd93013", + "0x2e7dd294d4ef359e81864f73e6a1b531cdc44b75d273d6fdc72c7b3279c70c4b", + "0x104b529ad25b3c41d3916967d5ff6930636b646b0cf62bbb03bf2b4e37856c4a", + "0x5b5f1ebf16f682d99f9fbf7dc26c8ae31488f25e4a68edcef68267289eb6701c", + "0x38d60e6f119185d5107d6e541d7424e8ffeb20f4cd9238257599b261fc1d107a", + "0x6c5b54648191da3c4e950cf49eed945bde00d919c20a0f7ef6d14d546f5afa66", + "0x3bb348017f6c4fd9fb6e45a1136cd695ef4753e6c275ee74875ab200a9fb723d", + "0xaf74fd1ba1062aef43d628fd692e8319dbffe7e6faf641860a903a6eb8516a44", + "0xe20134e541e9f3004af650f646ca845c71da4625e6c07a9577273f8b386ecb45", + "0xb1c563075b05bab2bb03a43edb451bcc7e6f827fca643da675cc02ff2d7e434b", + "0x051dd97bcd29d8117e77bae744e3b01ef8b08135b62d2495d3719fccbc6dfefc", + "0x0c124733c999a74701ccf8836dd8c617f5f6a65776aa718041c829bfb327924f", + "0x66cfb19978d5e83c86e2c44bf9ce7d499042cbc434d9540835db3732e85dd86a", + "0x607713b4fbc5a0bea7093569f67901e37d847e8c312932cf0309c2aed5b56ba3", + "0x969a1cd63414b7cc53356f91f405c13ccd7d5bb9f70cb2c6b7e7b898474e926d", + "0x37efe22c778cf79baa2940bd72a4b02966b23ee8e9805a45a0276cd4b79d9ff6", + "0xe16c290d5f33d6b16372e2c60622a7db840aa76052f0fb5ec3e2284fa26dff07", + "0x033f241826b6929b0b1e69697a4b031dbde9b1fae6a1865a72239c9a2851e084", + "0xc7dc4df98bb5714466db9f73a535c83a8d25f53b7cbac5ff0e700c46f3ae5bba", + "0x865898126f9ab988f32acd76235084b3ed5233547755ea6ac3e53d4f5321e723", + "0x3d50b31bde5fbd0ba81844095d6e51ab6d9414701631e2078bc30c7081798458", + "0x429095690a3d089e9b135c33515b1a07dc57ca3696cafe06654a453402991bf1", + "0x4afd12ba2a27afe260b72112a2e09f421c29a69abccd84a69694499ab1d1cce1", + "0xaf2bb29eeafb9641f93c969aa95011f042ff21c1fdfbe062341c6a6031f7612a", + "0xecdc135efcf44d5a86d532e3d513b31116371883db8465bfdc1d60948c141ec9", + "0x487c21e5dcea3a6ee7ca82c1a1f529e42353eb9a9b7589fb64d2cc1cf42d7954", + "0xe26a09c0fb6e6fb64b7aca6e6de11a46218906f1f84ba4148e549262937522ff", + "0xa86496ea2f4e54e772bef7f4181b90d77f6e498d853a4725217970b26440da98", + "0xbd133d1e47d5249eb88a836e6cfa7c14c6a5c2458df75efbfcf5a7d1cac3216b", + "0x2545a7259b59893ebffddbd78fba6d1d2067a918ef617ac71eb3094d7126a29a", + "0xd20ee02f5f98c2565f96c53871b212088f5f058899223b2e4185f227e46ae656", + "0x626836710574794d2fe0a66f4cb29cdadc5cd7e6628915f46739375595e7a43b", + "0xcade1deeb7d9cf7872e5dba4e2eff63b903680274192b48a16c3b2f3dcedcd3b", + "0xd4a7e48f65c87a94bbadeaf917616d07cef02a30b188f082e51acf1d54f4ebad", + "0x8889cb79ee9df07b063721442f9fde8e03cb561c169217ad6fa025f74a0e6148", + "0x6bae9e309b4d3db531067eb52abd6edf15f9cf2f9cc37926bf345e06df758a82", + "0x9fd6cba75ba4dca62de7caddc81f327992494d674a934967a9258fc3f27f40bb", + "0xe480f7f524e168e491168e135e12282a9096e0311b06eba99a180e3fb6ffc427", + "0x5909957186e530f93ce54b36c4a6315c857d8e915b8209368d402c6e64d7522e", + "0xdfc5fba17d1e92c98fe9b4b5d6e1a639a2a97494be40fd1ac20400cb6c121958", + "0x8e46081f6280b4fcaecab5ed2de2c7a393cb45082243bd7b1223f31ebac178c5", + "0x27e875e8529d2ece9fb5d8fc0cbe021a68e27e135bb5b2da9d29a65d3031c16e", + "0xa520b449612a3751a7248dad0c74197c1c6e9ca0c876d72a06d3ff6515b22db1", + "0x7187440e3a91e337d25791a23d2fdcdf16656946db9c3b36b8a09521c119ccb5", + "0x2c38340c9612c105af103e5be8da5d93db97781b70f363e974831a0ce89cf2fa", + "0x324b236f8228966feb23c8d4eab922a38e64775bc7c2e4bf7c69bc3b8cde6aa4", + "0x5ef8a221fd60d272569bbe237482e4b932e1891970d9237fc615fa0d2657144d", + "0x85c668de15a1b1cb2105415601a787126a55570dc68487080cc4a546394fc8ab", + "0x9323eac52e780d05ce240f068840d7af4e0fe4c2397c8ab8a541ee44062980fb", + "0xa3dcd02dcd62d3253dc52bb225f1ebe09e48deeefb541d16d7f069c0539e5213", + "0x4f1d9a05f678b1e239c59648b932d79cf3da4675a9b30d0866b8626980f5c09f", + "0xe3d1a8bd54fa444e9955fbd36b246ea9685b9de88f362d576912f102ecbc8bbd", + "0x40da2a000b81a83a3913a141461c201fdaf65810d1c0e4d972022dc160710ae3", + "0x6f2fd234bceace296f8357f0ddd937d406805ad5fedad6dc201c137220909710", + "0x4237b0990315749318dec2153af1519141b09f6f2ea50386e34de6c8d1028407", + "0x829bee6235432fedc04a203460aa045cfbcc2c46bbbfe7f45f27bd757a3cbe35", + "0xc3c1846d32f96af79e04aea30b23129a3c4ee47f065d542da103d06d6dd46ce1", + "0xfae716245052a756bc7a550a52f5823c2daf55c71d8a84ed570124a33ea93727", + "0x833f0e179a19548cfa537fdd84e85179674f5e7d44efe3873b81520a401e7c3b", + "0x98d2d2745f05ea6a414ae8e94d93339f61c3b3cbca4db7659be61916ddbaf814", + "0x6e51deecbecf747c27eef65230d34eeaf1a2622b2186070895dfefc3be158b48", + "0xde94761968e95d375bcd68e9d1e7155626381a7c3217fac6d53cbb4597248146", + "0x8ccfe5ad90f627fcdf30095926ec92770fb93a25e173131c11bca809429b3df8", + "0xb105f6560e08a1b7145843c489604a02cb8630fdb827ff41fdbaccae7e60612a", + "0x0c67492fcf98ce68652ec7dc9db61f9c39aeffdd5519912b0e0b12323fe57226", + "0xb851126306776058aae24a95912e3f69bdc904fdaef1e40c41bfe2aeb0fb64bf", + "0x91a7a713b0a67fb7ad81f9a33c696c7159cfdc04067218015b40f2f98aa71371", + "0xed8109aaacdd5a205b4bb81bfe0876fc3df1c3ab936cd585009918e131de5b7d", + "0xc585767a6992d23ade8524c5905602249d0ae1c284a1a1a4b8be3b7c27eaa51f", + "0xefc67b308dde69a785af27b62c111e9c9aad2f3ce83a480c7bed7d0d4a9278c8", + "0x997aaccf38d7a644555e90c826732158a7b9775bba68d02ee2f6590d9e024b5a", + "0x484ea8a4b8eb8977515bf86a966f52fef5d41fd96aae2368a6e43a6b8a6e6dc9", + "0x5b8ef979aa26c7a40d1f746287d289bd9bb8f792d99a44ee30d603708f0611f1", + "0x22b30bed06043b0a04b04055e4639b67709fb5caeedfe2b7f365288b62cc4403", + "0xb02b107dc43388167827398bfaebdf179d9e65a3b9c76dceae3f52c1f3585dd1", + "0xb789be2cacd3f38a5e531307d2facc31c2984da95cd2a83d90bd3ccc7569d07a", + "0xbab2880d54894f1464ab021db04a180030da025b42fcfd6cfb7818a27e3f8d85", + "0x4b2ecf5bfb696ed1b702a0598a6bc831d437cf9b403eae5d2b49629b3e606bd3", + "0xe45f9ea3996b3fbad50561d285ff78be2ba646c10bbbab4fe8ff04c025469cce", + "0xb9f6e04c78cee4a7aeca66d9076c013b4473c8ed5e75fccd11e4cbce010afe59", + "0x52f496d8faa76587a2da22271b1ecf1987d121735256d81123f82c5c5cced9e0", + "0xf22fdb2cc7b648c6a636b9a16be190dc80ff2fc4b0a8ac836b600403fd079aae", + "0xc6b8e9a6cae3d31dabe33e23b6e097b863f367ab1b7e66b16caa9e623038c5d8", + "0x8c8b453c7d034583efa2db24ab0aa36c77935b2bbb341061d310b66b1dbc9680", + "0x439a168bb10fabb38e237f89be1568e7f812cd8c07040a302042f25d1c1e1743", + "0x681ef2da71f50b53d2786eebf9604f573aac859cb80c55d0d992f58a654c176d", + "0xbb150656200137b026f7d5c6023055116e0325a86bfe4242064ba198983334f3", + "0xd053981ba7e2061b4dba42f934412a5a7bdb49eb42ee91e8d2f21fa8e9690d76", + "0xe380f7a0716348c22980202c9e0e59c6eafbab539b60c9c141380f3657e34bdc", + "0xba8eb198401b3f6b889b05ea9b792cd98ddce5c3e5eb84db767ca8857bd2e3e4", + "0x4f418961dae0b2a4313bff543a69a0c7cf5194c38276453f0e954b6d18807d16", + "0x13ba6bb9c046984118cc30675a73ab502ae4ea8c8b6e0e8051e99546397a9609", + "0x0ff0d8219efc61446b70ed9c8c2c8703dc63661c4012f12c77854b6999435ba2", + "0x2d8b5b909cd87e91ce71d62aa2d00dbb59dc5c8ec0ae5193da7a70e5eba0597a", + "0x71504e92b4703cbb1345985a81a0217e12f47b43e389c4eca8cf3753226a78cf", + "0x9ffced16379e9ffdec099eba10ef04e4312ec4eca94f42fb32a8132d7a8bb8ad", + "0xda5a829a1be3033c3e4d6ac0bf1e6fe96c3507e4a149c94f99a66d69debc0c04", + "0x381be46aa309e82d7e389f67cd1d4582057a6a96fb3c35a2bf49bd23cc6ed6fb", + "0x9dcab0a5ce50489ea200af33feecfdf2590bc3fdeaf03b7d901c01b631189145", + "0x9741079639439901cec6256106a166ce41b6e94b4b0fdd05e330e0272ca6e578", + "0x55e5b876c55fbf433edccb7f1018b0f22902d7e3e82dbfc7183733797175cbf1", + "0x77903fbce30ca3f09ec1aca895445d803767becf980185fc76929a778d7bba5a", + "0x05832f1fdc77004e112e944d6b69f9d90728db98ab4f4851819d281c37df82cc", + "0x2e7f9be5675757e11e880bc5bc1abe0c59b0f893d6b9b18b24f91d2a8d67d361", + "0x129015a3943a6be8c7173038231cb168eb37df251e98b58ac5e6a2983f0cdea3", + "0x6fa08dd7c1bf6f463c33808115b269831cccdba9eb4469d26c9283d029f693e0", + "0xbdf7dd9b7c0ee02bdca402941acd5d0173062e37205ee461b25eb1f5f5b60a80", + "0xd6959f016799370428e8f052afb3c3496dec5661fcb5febe105826171f58fe42", + "0x934a0a109001dedd2ead9af95b3ffe5ac812fea1af83fcb9a4fb37c7948f13a6", + "0xd818bca90c658056ca1bad9f9ca2ddab137550e383cee409e50c7ff1955fc843", + "0x14f1a70c2229da8d77f2a9339772b345b14e80e00df374afe2c9bb37bb1feb00", + "0xde31cb39fef1a971ec665bd5b5a750171959a6f3396ec2d60af7f2476120729b", + "0xbb89f8a782dd5c3f2ad10dfb3fce3c721d2e55543e37fd42b66714d54bf3d65a", + "0x31db12f19f0dee64fa9615681c442c39f9ac1e577193a18087c3183d31087b85", + "0xd47dbcf38bed088b6e9beb58c43627e97b3874a2b126df69d612b65366ced8e8", + "0xfd5a35864e4a50670f085e7b0596b7598a7ce96586887421ede86e08ae80f5f9", + "0x1d0c602e3f22c8b3f54966d4879ec83cfdb338bb6051be2505489fb7531b7443", + "0x53d96b3df559cd78a7d68dd84ca6e1e870b9d03db1043a0d2f62bc0bd0350e7e", + "0x85609604e35f3e35ce7f3bbbdb0e63961c74fc94c668c7bfa44086b59ade4b8e", + "0x1fbdfcf7260846af11dc652798c9e8d2cef33692d97bf60394538c382992d42a", + "0x03fd3b08c453069c87733f4db9acab21f2533211719f02fc4dd2d7ec52b6f01f", + "0xa70f4f1deda8bd1ad3ffd11a58ba074b82b11f8bffe1b8f855ddb4dc56ad8803", + "0x705440935e4e84147a8e22484a0df67365ba79749de9ac663a80a269eb2109a3", + "0x1be07167b1117a31b6a9c79f4e4c6b85570c6801197567808faa063225a29fe5", + "0x768174ffeb8ba45bbc5316315b09eaefdde4272f7d65232fd511f2804778eefd", + "0x269eb251191df8ea36d842741cc8673e45dfdf6483bc676109cdae592cffa7ab", + "0x29eb9262aa2be73e9234a7416ce616968017056621d6811f195b758069e7867a", + "0xbee418e965c702626d6e0aac748718d34e496ea7b9687ab2d2a8486c73ee37fd", + "0x33af89a00caf9743693c443e309848fe7894b002398598634fba8de0def4b600", + "0x4a2c7735e8146287887f9f3627e8e6732419cb5eb3008e0e6ee43aedb6fcb1b9", + "0x296efc10273c674deda2895e1e56b1b230746e60d93855876da53e1e02a297ad", + "0x37bc5161655e2656a9b836650007748088a3e355d3ee2942406bd619fb1c8ed3", + "0x9f7205c120dc9899723cb73f12599edfaeb38209c3f5f95182533a01eb9ee18e", + "0xaf772789fb0843cc4fa7ddcccf148827e635e6407376678f0397247d58b2f7aa", + "0xe70f46fb4cff6ecdd6f4dc10b1f5a75959cf1c6b2a56dd0668ee48cfe45d0586", + "0xb602d3d9c12a96463e590e38c6a124339da115047bb127cc2f4f8030b66b2f0f", + "0xa72058949b2ca5354e61baaac9a6e23e3944d3964d7328137d8c8987cdfb9031", + "0x045aa0652d0457b5710209bbef33d741bdc72b09f34e11684a36b68490751921", + "0xd81258c2812e141196dd7700e34afa5bb10788e1cf724d0819a4d6baed487121", + "0x7d1e71911d08c477da67705fe3ae848e83fc4528df7645d020b99cfad9868ad0", + "0xd61fe2d4ec78e7e5b72e89a83f4baa1f64c5415f0c62ecbef587c5b054cb2b56", + "0xfd7d73d7ca0b1cf705ba71298e9ad86fd550ea265350768237afd24955df0bc4", + "0xe26d9439696561b369859b43d2ac2252a74e854626ad193c53478f52be1a8f18", + "0x3ce5775cbec56257ba2ebc181c449392da606d6bcd6c30274faf92d72c528a0e", + "0x8cbcc11a2af3d03bff412dc1b6de500ec21655f7b1b3b9a34a8bbd05e5e29a23", + "0x3b87f2a9e511d20e709b545a68cd3d267457097818038585013d9dd04d101f51", + "0xd6ea5b5cbbcc9be7ed0b6d49bbfab08b6053dbb5b893d28afe77c26da69d7154", + "0xa8d3cc1e5314844001e9bb7b739cfe39432ab27e365eb40cbfb2718cc469200e", + "0x4ce811df04f172a4c650557cd3f9bc9f03be5e8a3403404c1098b790b12f39c5", + "0xae5a95a50fc416ba6b1d0264799e9ea4dc7f446ce09d4ecee4e5fcb4811b35f1", + "0x074ffbf5dc4cdc1770d507c97ab2a63ccf34f973bbf70d0809f47906aec14f2c", + "0x15a9674fb42cb82d480aa23a6ef66933ac5c8a67d6023d38d75d16bf6796fedf", + "0xfb1bc2dafdbf80853df80027713db759561f5449d6c0d040d800f01004326c77", + "0x1b69a315293bf885fc4e1aba776f26d514e3f99a1f4400c65e6a210b53ea8c48", + "0xa960d074816a412481692dc5849bbb9fb7954383fdca383780782c21250fb1d8", + "0xfb2c05c0c793a7af514863228860a4046f06018ac559fd4cf69fd270d5bc8bb7", + "0x9755f7527e04f3eaacb18923470f4b981ad19af84b0ac460a30ae1f7a7829f1f", + "0x4f091a15bd8d8f11ab82028c70f521c5df3e1b60523c71c20f18999366128321", + "0xd548e61a34546dbb654eb041d2ae388ce24480b8b293d8269da3eec317716f5e", + "0x3a59121e08362abd33fce01e88d5bedb8109be4a35548fddb7b8b002ede6e8a9", + "0x2995dafa4e4369509509fbd85695f2914c8b2ae96a724c899900356d2a8be90e", + "0x7f8d81b98374fc7825223b4608b745585f84f2f1d433179cc91d941099fe3967", + "0xdd95c9a9fb0bc80d4fc53148351791182f5130edc25b219e48920d7fbf750f45", + "0x03b359930d8b476d17a87274ccc6b6e69d99cf71813f6ab46d1bfe3fe681f3a9", + "0xe8ddbf70dfac5b479e75c32e008517d485e0eb9c3f3c62c324d4a13e7ec3d6f2", + "0xbce9a1a70839d4ccf3cce122a13b8fb087df42432522cc120112db2a127d4fca", + "0xee050cf53c34e17ac1e617858e61960e8652e516fc88765248714c1b88c0bc20", + "0xf56965cadbaceb1f861d70ca2b05ef3d66cbb679761da315a69f85adff890fbf", + "0xe59354508d4d1cb7ad1ffdc015bdc79c55f5474ead88d66dd624e08cf7d70c9a", + "0xe541d82b08f6f41ec19c5e8f4f5df75891e9306b2fe5c7ca91ebd2808df1af03", + "0x8eebd83e9880be50198a19d8bb15495be0d961b40c3895475beca0b152138896", + "0xe565934569abe2ab42a7f7f0c984498519e1b6aef08072f116e058b12cc89954", + "0xfe35b6e874ab886f98b415a0ea2e03b5c6688d9e0e4006741b8615df3902c8c0", + "0x758b5d9cab1dad85e6da8c36d8db6692fc336cb9ca13237a5ff87ccf1e36045d", + "0x6bc4b76f233069140de330808e59d5f1e87ef4936801252e4191b25b86a13599", + "0xf26901d058e5f4ce7f7b52237f44de3ed8a3d7c8fd5cdfbb412a1b5bbc841c78", + "0xb06c947b5cee96e47b4a9e849a34b0bdd63590a881d92da0d627faf5ffc5c093", + "0x1f48248b5d10a132c82784b3c424dc5dfea15ba5dfa0a12146143d6d97ff2d15", + "0x3bcc1c101aa58b3c7749d9be5eee42e418faa928bd7f57ccd6d2a9f3849cd5b0", + "0xc5d6779c88a56e5cd63a0085c197ee80e6b726f93f534226a9f165901cf2f205", + "0x86d338b19de6dabc3dff7eaf1276f6016054e3c376c71a4e3f1eba7705461f90", + "0x0e5490b115b8be25a96eebc4f1ac979d39a6d58d733ecd032c754a23f454bfeb", + "0x16edcf4d1df700843a95dad87836ad9d27ec1f5d7db298b97a55c77f4a589955", + "0xaee28d5f29b29362ffd4d90e23af1186717f21dd761657711d655004ba0e29dc", + "0x1f4e4fa1a496080b50f52f2e39650f14c53d257e08cfb08a4fad3208fe221d48", + "0x91af2e25c7c6fdaaabdead112dcf50b53f058cb69bb4d200edab3bda6dd5bd95", + "0xdcfdf34e0ed81bfb3a259e26ae966cf8b461886ef83e70e796aba7bd9a47a0f1", + "0x42dd9f14d785ecb68f070b0a8a0decbfd48f76118240467008d42cd68ed9bbeb", + "0x818a53b564a60a270df1282e1dfb42b44c645badf2aa669910e0b908ec3889c7", + "0x45f238a75f68a2f3ff88ca33f750e4af9ad33940b25583a509f54310332fd560", + "0xc0d9a3b6416912f2d570453e6bf7ac27665968cc118587a7fb6d15b1fffed1b3", + "0xeaba08fb85742a11443489ae6ed095d9b6df22f3d022958b418ac2ef7b20c4d9", + "0xa5df8ccc90e564aaf125e64955ff234226bfcb8a0f9b1f27e65192e194c57f90", + "0x50a7fcffb206cea039083fd6d3f3d1ac4b52a2c91db343d88abc65b1045859c0", + "0xa0812aff7be0488e11eb1fdd2de0ed8c316c956a8f04f3b23ed892b046ec89a1", + "0xa7c513510f71bfdc29e5eb0bd67acc40ee4c13e529fafd6b76b094c0f8218482", + "0x50448680747c586f1e540a8f003b377c35ac36db46db125c969d006327171ea0", + "0x636856db1e945fe6fb8752684234d023a6e11ed808d7bc1978e6a174eda1523d", + "0xe1534ef481618d861bca1a2cc0ef011d1dd46cef8d292f65b75e938610813d58", + "0xa75fec3c35cf75be90e95ccfc52813ae8f711ff052b2d7837f220d0fe0a16dd0", + "0xf9e2cb946b25988ef94c38c8a9b02570ab23e48f660113f045dd60fb2cf54264", + "0xd0ccebfed4ec2ef4f4aa6b668226028fa20ed960d328a0c7189e470570d9f027", + "0xd090381aa9db44282cff725a66679624f77995af52be02177574a04dc36698a9", + "0xb12a700f0c5ed113bf15a3db0615b5985d86bc1c8e554acae05387d6b50f264e", + "0xe7c8eb2e03a7df232c720a9b78eb57b6dd20ae16cd19fc2139d1f050e0a2481f", + "0x27bbde2221222128e895911aee15a449eb085beeaf7622d276834260a6c60105", + "0x1998cfb38d193e67f789e65d75d01086cf8fc9529921271b652d54691ebe5441", + "0xa53bb63e1a263b48e502b4eb5da14d2fa0c758142f4dd97417228b4eae71e60e", + "0xd8848b771b667ea371090dc70dbbe2e34372a00e56c2d43502cd6239668e8f8b", + "0xb9f7f3310c84b130b4632bcceabc2968611db1fd2102df9baf687e83752e3e6a", + "0x882c0a52db9eaf9ca992bcd90e7dd21ef84e8b8555bb3654694555579acfa2e2", + "0xc5c3a172fb4cf08ce874f3682c9281d3a3810c266d3e3a1787a62533da14653d", + "0x8713f0d901358d88d9b21397a1a1dd813ea8733817dff84c595536e7a4741924", + "0x5ff75a74ec895de5860eb900bc4fafad0eada4933659083c20ab15e18b341162", + "0x17a1cd9702d6d3908ee368a06b8e82a543b78b47632a7e8d9ec632362c1463c1", + "0xba89b0097b4a48e8f61c582a5deaec16706bf20417359bb12c40fd5080819fee", + "0xe9d7b65c8ec59a81a613b777408f67f9b81f6bdef0536a7296909b094e696d03", + "0x515f3df26edd7ca0999133c883c7a49e7f0c55e41fb98430b655e61c4bb26153", + "0xe62739f8b14858564e3d054d6a3ab7139e2995274ca25617befc4261221e6f50", + "0x005900ccc3f5a9dd32edf55914aedab67fed369d55593226d6baf0056fdf94f7", + "0x80caee38db2b9beafb1610d9f5a6051248f372aea34991947c30a777b65975da", + "0xaa5acef1220f2d808eda80656c1c30ca05c831e3a2016f0adb6608f92a1dba76", + "0x65c2cf2fdda64755745df38f3680d25645bdf53645c1dc9c81da1d7288ccab93", + "0xe057bdfc30eba89ca065a87193664d647a61a4889aa0534cc18172ac2f7709db", + "0xc93b421ccd7115979f3e55b6a30168ffeb151ca6864c52e22bdff2990cb7112b", + "0x640c7892b74992295d3adbbacb8397688140f68a4610ebaa0134dd7165a1bd9e", + "0xb2a199f92cefe41049849180f3041cb1e91c533e1f00873da43d7f1679a33b58", + "0xf996bafda506d8448bb935aac1d59b29c497edc6c26f49100ea05aeb63e05d35", + "0xfc29f5c4dedf5f95d18f952163c29e4782339787d92c9515195dcc16e66db64c", + "0x737cf9e5cd5421996c2008d47253fce895bc4cf9626efcb7c4e7d0fcccf11d0d", + "0x2c9ca5a9174410fe3ce0b11cbc465d1c29f618a1da62ba508c7120180058764d", + "0xc0abffd1bafe93126584e19c7730be7aa4ab03d898c9f54b610706fa163c643b", + "0xedab3ccecefebda26695c1825ce6963df2a882fbd847c5b76cba38d02824205e", + "0x29a3c2ad51e472c334045d1bd0d261fb65d52c7d915dd84102049e6de4815430", + "0x610281ab2cbaf480fa932cea0b66b3a26b823257aa6602a46813aff1ce4c1d7e", + "0x5837ad157fd23aa89c85a85fad61ea1649cf213fc65f3adac08617ff23c9c335", + "0x2ee2d5e96a8964af252e498bc6ecc35ab15a3bb11af3a747ecf55e17608f9e04", + "0x1b5eb1ed214823fe5d4ff7e2e4cdc8c65bf64738be98bb0683f3fe4d12af6a7c", + "0x4a9809e299660f72a090ed029ab30fffd8e5d385d7b90c5dd375785c34c89610", + "0xb58265dafb35a626787c3fddc52b1aa28e59db94d0780a2199124d093c613489", + "0x07121555a19ec798e0520d2c16db7943ba114c9831ccf778696b0da9e3dce194", + "0x344c72868d5f99dee44bddb6b941ffacde88ae018ffab9f650c6c40f51461f80", + "0x0cab694da1d099e287884791eaaf185a7dadb99dcd42089c469d49b39bc7b8db", + "0x811c9e2a9e66d7b88896e3e211f565856a17f567f30d147a5e6466f445b19977", + "0x33cf2f859514d0390a68134b06c8c686394478c713eb76306dfba3c80389235a", + "0x8d5d61c2055762cfb4be13072f8c7819cb04ddfc91c311619f1cb2d702c5ceb5", + "0x46859a7e65578bc406323a133f14e48dd5efcd60fc8a37374568b23ac39f9b1d", + "0xab25a7aedb5e987050069e847b21029b637885157c3d22e04448bf972e26ba02", + "0xf643b307f2a679575e7d7d0865aad786254568c5a726db0b91e76d6390949717", + "0x352c25d51a28302e861eb3df398450532cac49817dd30863bde2b674b2e675d0", + "0x265744aeb581dc9232933979a3c7ab02d67eb1405b740b08c7b169d6870436ee", + "0xf2cfb4eb76f900bb03967654efeb32cbe99922a3b371a43ea3bfb866e754b768", + "0x0e1267f4f2f5244d31a23f6a1ed01d50062a8adf21a632be52e697e5fb4e59c6", + "0x6f509546870f1228c9662853c7c1bce519bb48de5aaf231071b16f2d9eb30c48", + "0x6879c2ea296945ceea72c3cfb8fefc9299e264b2ed23dde57a98a7354365e431", + "0xd8e425bac01cfc99eb43bab9095eff1d1c1f134365b81e24af914fef5884806f", + "0x3b3f86bdf7fbc74c1ebf4e6d5128e787bf8cca958fdd86cc63f59bfa5fd4d52c", + "0xed5051afb73d39d119d3414d5ff68674a036d24770f753aa14aa2c14a17f8b53", + "0x68ec2ac1de4b46aba53430bcb7602cb2635dd988f53234d14f14c09f44ab2b87", + "0xce9971cdf75de4b702d4842c994b2b17ab0a680f61a92c4a3c47e6fdc6838ca9", + "0xbf3e191fbc1dc8832b063a5e25963dd0845368a24339efd0b8b7869025269502", + "0x638ba536daeee92433419f6810e6e672d9d3340bf31e8aa649e12aeb9ae5ac63", + "0x34fb4b369710e1c55fc34034ff4146ea8479cad268945b717663ff4603b49fb0", + "0xec37d50305e2c433f3b2a9db832bd632a54a5d47d7010ea2438c9eb39af3d8b6", + "0x9a8ed10149499697a68cd10c2d2aeab88314d18b42e7163bb3b4151d15d7341c", + "0xfbc5e6200829b4948fcc4affd3c698ef2577a33ba14592f7eb52829acb795db4", + "0xb5329c62b4751a0a3f12c4e6cea6d5636ed4b5f765763180e5dc4d5ad1506e98", + "0x27e57bc9a0b4a52a9a584fbb013bcd322f7c14c928737294076f1c5db945f308", + "0x89007d46244c8d9029a79c00396cff9d705264e6d8cc4f7a9eb39621e0697f1f", + "0x571439737a74fcd2436c5dde04ec9de0724820f161ed73c2ee3cbd8b0411ea78", + "0x23d700b5d21d4b789348240e3242513eb5e9d397fab24dd4549c930b54b30949", + "0x2e8f9448074550d21deafd584853b592c16bf418689cd77653dae8ea7333b63c", + "0xa85fc5bc386183c03deeccbc033ff32dbc4c5ded1cdc0797cafba320e52bfae6", + "0xc64f8cd865270bebd5593f9471f7cb875b9e3687490d514d7253447c3eb0caeb", + "0x141534121ab6f72b35b66041bcc623b64e27a91095a2cf29e6e55695732d4944", + "0xec04e86b6e59582f5f7c8f0be4a9ee2aa15a60a5a9d770fcce98a780f30d3cd3", + "0x2b982ba0dbeace9403cb1deccb5dfb90e7928459498c859bd84c1be0e056d025", + "0xfd634387b8462ad4fb21c140054801842d23fc4fac52000a370b5ace8384e525", + "0x673f08b4e902f92fba5f2b950945be46965eb85aec5f5c5562a440f1356ff518", + "0xd1a298bdb4332cfb2e3021124a2fb6a423e94c91046e756abfca0615f1be16a4", + "0x1e4fea3efbd39862fae9ef0ec849db798b51e38d03ca1a076b72354c92759ec7", + "0x1c24d2ff7671d655f8c2db9434299066c3bd4d877bc97b98c44143303dbe494c", + "0x48170f7df87379373effffa65232a47f77a85919d8ead12ee0cd1b61a8631d81", + "0x0e5f50fb0cc5b72873c0efe5ef10b80f3414f5cc2a9f8d8f7e52e837d95908a3", + "0xc821167e3d5391b491d4b430ae31a392f74277783167a59bfaac4da68731b0ef", + "0x6dd1cb6c4b1e159304c0d3d92c55c49a207a4797d85c733107b7f767f40a588e", + "0x5e93fc6f84efc31ebeaf7a842bb2b6a9f563d6d2fa74090c243f17b2f2f6a5c5", + "0x723207bcdc0ec398ecad568a3d4d268abe84f9f230217e3f273357c68af4252e", + "0xc76c0f950204b24254eedf11e2e6bcb87f938bd6d5b984c3e8725e3b9ad291ad", + "0x34972e34471b3013e55b71220c52cb3256f62fc39c49cae693d74af6f37c53d7", + "0xfd674c25d6dcccf97bfc9a0dbd080cef667b973d870627399221226164469d15", + "0x9d2c424491956e9429c23b8272b64db1fb4484cb807ba6d601395230037d1da0", + "0xa6fbe813c18858fb5e8485f3f0dfb2e14e4a5351cf36e9155e622fe9440f4633", + "0xbb9603b47b8a79a242cc0f5344e922ce356b7f27fdcd804cf8c61be09a2c3766", + "0x8a44fa8ac1460e422c7905a9ec1e7ae8409711e77dd05b3ea475ccfa9ba66540", + "0xe98353d4c5d96d81d5a3ec64159e0d1d991a40f0d59012b8bc98a011143b8c72", + "0x588b934cb4bbba023e05268e75f85e4e2afd6bdbcc74bb233018625d8f86ccb4", + "0xe31e8368c4241fe7e5973dfcecbabffa722802dbbdc09dd0261853b18e09bae7", + "0x56f0725fcfcb8e0065b2706cb653ae83509446ec0e439b0a9e69704f049dd176", + "0x96a711c6820ac1e79dcb3387d5d33d2a344af365a83581f73301e9dfeadc1db4", + "0xa3b15839f110faa441e29dadcce9c80a1e7c4121e0f64405fa050f041984a1d0", + "0x5c437023491088323878e9be336d5967b0cfc6630ccc6d0efb34ed3bb886e80b", + "0x15e537d4fe79bb058ba79b2607cd66e390e4f14672aea92da459981c52181f66", + "0x6f64b88ab301f899b3b1a53cbadb0e16825af49fc34611b486a2d8dece57f347", + "0x5ecd503bc907032bc77802c329fd015aeb53a6665c90210261a3bce46ed77f00", + "0x80d18e7df42f667b2e0e62b8cdaf88895853f2816fbe7ce41d78bffc5c9fc4b4", + "0x5818c74dff81646dacc52bafb98a88aaa402fb70183f43e87b83869d5bcb4294", + "0xaf9ed7920b80543cd2dc480e4337e8f556d55d2174046f185330f9a000e44af3", + "0x61b4eec3aa380507ca9ecf083f29e04acd09c873f8f5938b5c01f2a290f89a66", + "0x523b0bec4238433d2d793dfb4b5aaa1a66526da3f50c58e9abec26930afbe585", + "0x4f3b11ded9db186eb04303dfa38561bd94ba5240af38ebf9062c06f425096b3e", + "0xcb85b3444e4cfa0bacf3c40adca6e29425828360365088e12f7bf5a6cbdd0b77", + "0xcb2ba868f64b2c0f5fdc190a5c79a918543d1c8b31c2b2ccb3d472d4213f0876", + "0x34ba3ff2d5de4f8394ac6c6fb3cefae593b29558f86393a0484e1ffe8646320b", + "0x590c72d33796b337f0775e6b61799bd384e20ad5fe8bfa77ae158becf39366dc", + "0xc7ccfcd640eabfae6af924aa73145dbe54ede3e3eccdcd6f779bdd650e0858d2", + "0xb74e093cf0ac6de03d3bd36d5bfd17a0b4c5cc7f417a061fcbd35033cefbbdcf", + "0xfc9055cb65694263b31ac1c440dff9f7f49f51c590b4d7096544028df04685d4", + "0x618412d01ce295289c9d9a82df04707b2e67b5e4e8e36fcbd82e0d10fc95f68f", + "0xc0a52eac91494cf0b957c2ad72729b7dafd651636b9a65fce3412aaec9e949ac", + "0x32fdd8c309fae7d6e6883d645b20f5e68fc8e96ee1787914c404b0b03ae57b5b", + "0x1fadbea967ca456dba9418fa999993f5b362b7a530f6e05d4b4dde8429a8a0b1", + "0x8738e308a6887129936b9102d4b420ca4e4e53810248d8fe573c86864437dc12", + "0x42b800e157c1be6918923896810f85c5d7e9631ebc836fea0b40fd244059a067", + "0x354b82a3fb9da4eb99512b7d3959d79bec2278b6eb678a0787bb69e5c318fa32", + "0xc7c0e7cfcca42bb06d877b2b5ccd6ecf2c551801a3d967a3a0d1c27531cca071", + "0xfc9b5f5e5beecc7663147b2214098ecca85661d547508788149a04dfec755f97", + "0xcbc51496e55f162b1c3f1f696e5cdafef5c80acf11f3c0e4a89e2bb77917c88e", + "0xfbfdc0ca4ea682bf103d07a82c267fcf3776ff29abf1dba2261924e60da3c046", + "0x3fad0363111249a017823c2d207efdbd14310eb561b334ca1a196e742898f7ec", + "0x463dcbc69080b63a290168a8755a06076ee07604f69639aadfa2740c4de67a8b", + "0xad812a8046e8f5674dbd85650052076ddd425b67314d91234dd3a512c3e910e4", + "0x4e84dfc00ca1a2db1ebaed065ea07f1eccfc3cbe9d24c70df56145d7ca51b5c2", + "0x4b991fa8b1ad1be706b747d662748bec4a480e353da1f75be219c455136377dc", + "0x190aafa5f0723fa7cabddcdde7b4589a9cc877c498416197d991d2f5a147c02e", + "0xc1ddba572fe5bddbcfe454a7b669edba3f98253eaaae137d92f1ea7523d67b6f", + "0x110721f64275145e4052123598480d70b841143b7c32ee5ed4065bdd216c924d", + "0x3e3fd9131408c88554806b74571e8910ff9fa28cf8e5d508a3fb41d2cdf4c13a", + "0x35adbb7927f34b4fed2a0aa91a1388f92d0de46fbde5de6e676ba8855be74f86", + "0x15456f2226d1b095743eca18bc44d4b02bb49aff54ccb7242bf88fd974323c02", + "0xc89586a187eac10352373b51c32451f6916e2648ead9c7c21bdd253cef415bd3", + "0x9c9c77a6eb1265e462b6869489cc17f9a4647eca613c1338502c9adb644f77cd", + "0xed65fa50221db8ac42b51471244d571b4768e7f4346658a58089b267d120bdb2", + "0x4870a665cfb2f344f35b94b13d6ba1681cdc17633383b187270692ada476e391", + "0x4dc3ea1a539cf51596fadb4c7991de6bb038fa6a1ad99f36f4e8e4dbc7a01590", + "0xf9f60c133794375a0885b816853bd2f201cfb143ce9b329ef63368a31124dc7c", + "0x277e18252a9aef369333f171f449a649c92c224d6501b9adcdf4f5fb56dec1ac", + "0xaabb26b8386ab49f2cb0b304fa8b16c900fcb9f044915e81e74289677e71bcf0", + "0x6c2c829854c0fc0b18a8a96b79883419a8a3c7ad05c8beada5b0374df06dd5fe", + "0x30e242d93d3cfb363b7e32b3c4f5773d2187fb862e475b312921689d5ec5c1f9", + "0xec35cec03736179577edaaf6b80854cb2ec8b1c31cb9644a6965d5a751319462", + "0xb42666fb939310bb104708420edf42d314714da9eb0ef92caf83923911612409", + "0x64e6cbd4ca2d663f8c6b67b4a738fbfaf0f057d32d6c9a52f59716f3d473b0ad", + "0xf98ae1c8c1aba6c62424f9f75ee7cc4e8c9787aaac3e3c7b3da2598b76c3f56a", + "0xcdcd314a1e94f57b7782c859d1dd966e74e6027271033e1c52450fec2fc9ae7b", + "0xf271050b74e2a29af1c43261d209db42c8b3f81d47f50d21ee2a909b0da83932", + "0xd7f83d9a01a2f8b1b9159e2339d169f3a8298a6f1784aacd64cb766f8dc12030", + "0x40204dd3bbe972d491c1186a75bb2cf7d4f0a5b92653e6bc492f0a0a14f9097e", + "0x05c9d92c32446af61299045a6abc348a47c196d2554eaba7507c65e685c53307", + "0x77d0bbd7f623abf86a91f3369387eca7803e7dcb9522ffc43c4683509a6f6113", + "0x388f024f681a2763d16e249354ddcd87bd07496818f483b22102ca29641568df", + "0xdf6b223489a3894474be16c273424935397d03bae32b655d4f8b8b66feb954e9", + "0x83d68f6c9d37f36f88d1c4999a6ebff58c917f511e85a86b8aa59cf42aac9a2d", + "0x50a4bb49714a654e451c06f676eab569ae5b47f94853b2c1b97673f3e4dd4231", + "0x9e3b8f54a147a57bbaf0c72c1240c001c8e2098e9905d8606b63dd728f5c0c95", + "0x3b9ffaaf238c1a59e89edc1e3853ed794f5fecb61ae3640dd855b4ad0a3c33d2", + "0x9cf664f7e3e70869bcb38d8858d621ca23307291f73b9a26a8a5a0a33f1aeb7e", + "0x37c7f3daabeba92968300d205ba93ec7bf020366a6e4781416a906e016a033f7", + "0x652ab028df65646e5f17c299d68a77b0167003b05659d458a4039dee55ad7496", + "0x45c9e82018c3dfd5f6edfc48357c724dc95f2db51c9542f6c1dbe73c6229e932", + "0x2932fa1d9c8a5c7dc3236b47c4f25c4bf15b5bb6c1404d394e3ecf7ad226760b", + "0x7429bd07ad50cd7ba44880ef3d69b1d83374863c6f4bd5bb0bc268ef622419ab", + "0x74af51d965fcdac2625277d6a5708a2d1c6ee4a4189882e0932dc145e6c63f3c", + "0xe7313ce74ce9ce91baff364a4cd3c61efe099358cc789589695767bc2a68c7a0", + "0x81192c7766ff37bd1179303040a03f029ccfc9b90c72e9d050fb6b6349058e44", + "0x06edc6c28c4bbefbda8c642cecd6ce00d3f97b6caff0ba6fa2d27dac54d6031f", + "0x555ad73a7aca0cf10f0c2504876875731e61d2c4c36efe35b87abd1a91e4d0ff", + "0x161c9bbc32d09b153e6afd459218259383a273d8ae3dae6cc2861f3495f8c3c3", + "0x11ab4d7c739c697fa44e48d1e6bedeeb7dcd5d078a44d55bf4a21db993cad4ed", + "0x2349df1db8b495c318b36a733db7e46c7fd723e4974cf11a60699fc5147bac97", + "0x9b5176d70a4bc4ff5b886d908bd5c6307cd42dc2b2b2cd66fc9498fac1772eba", + "0xf395385dfe7857fff64431c6eecdfce5fa0a65aeee13940733feb2a29bc1ed48", + "0x7b6296e9b12d972e138fa8ec57b82559719547de04b0f6d46f59e344a0e1f1c8", + "0x98eab4cfe1a18543ffe27c90661a16ff453602c7e459f364f277c905e86a2e1e", + "0x4bc8826d378083371f16819af595efb007033d7df2525cb5019ae393e0520438", + "0x7cfd7c3e7653f4a863e177e35cbf7560fdbc87e8e1d70625fb558371c0f088f1", + "0x89a5b99f364a74ebf809e0c1cb9e581b521814373060fb3105d6976ad28b2fa3", + "0x50a464c38293b9bccb66e744b87aa44950def6fd8cbbb536c36bf1739a7eb7f0", + "0xbe37bf6a021f29a9c91b77153e2b621fa5c5a3e9034e9272012ad6c47e7a15db", + "0x31fc1cff6dc96b677105c61c5c78cb02f90d1435b1935fc523d4086d4c479169", + "0x0a1295c08d6785c53185aaa2670d2d4ab044a211278c768bffe20de5410b1dda", + "0xbf8e6ab91637b0126d5de0e0f08e2a55a58afaaaa2e2b0c5ed26c3064b36acab", + "0x668af52abe7974e0c550497ac0e28868e3440a8136dccf3a0706f4661e371237", + "0xae67844eb0ba419669aca4c6a3415b9d5213818c5403be341ac78e380635609b", + "0x8c7fae0cb56683a3f16b8acbb2eaee96143f47c7b38d4c3263f83fb7f61acd9d", + "0x7c01a6e172c8b5f1a4987c9a95873a822fe68ad7c9a25ac8101d1bb63c6159ea", + "0x94c69597e0f1f533f88f82fa31a3db8e022e85e39207b482afda49c0c9a2f837", + "0xbd95307b183a1d91c4d8bf28b840eb9af233b442b6d75f1b23e24fdc21a6bd1e", + "0x7a9dd641b830281abb8ccdae1d26a25f89d15b7625a3c34a0c70d5df6d8ef688", + "0xb18bf4da15b6d36f5887e7f5a644d2fd6ff929440e365f8dfcf17f6433d6a802", + "0x009404950c0fffcf779b9027892e52289286f7a5d3e197ebd6bbdba18d32105b", + "0xf66a53ac7c7403b7d4471d93cda2f2cfca3ba68de41aa74bc750b39a3683a023", + "0xfb0eaa2de3889613c1ec86e118857249d41a2a9601f61d00abd5a489c84862aa", + "0x158eb2d258151403e9d57ca279b2e041580ce2691e19be60359b17f0226df5d9", + "0x592fa2287e01bf22187fece312b0846cd047619fac2704e84d4103cfccdf8fcb", + "0x42c82dcf5a8c1b27bd18a4361fb0b92fe9b46b5639ee968e15cdf954c8103795", + "0x294fedcb27f428fb798a2a1293770d7af64989855d1a364a13b13952403b91e9", + "0xc879b7bd0133ddf42032beddc289c553b460965b530fabbfac8a7398e1f70a49", + "0x3903dd35c67b78f80d66c2b7cef0f329c5f67240ee203d90d1af93da534540e0", + "0x5d451fe30633dff404ab50bb9b3f68a85d3c53ae659a4c5c9e8be666552cd852", + "0x6faf52a53b24aabe80b2e10b20f0a9b02d1a649f42d77862325f25b2c7c7c727", + "0xc8f598993eae8c47b8862932f434174feb3922b03de88a2afbc80c4372d29641", + "0x1254af8af4747f78bc4ce1ab41455b8995c67c15cbbfd5d86febdafe5d99ccbf", + "0x9a19ee5a039c9f41e22ea099c5eb29bd86d9be1cfb3ee28be9ddd394e7335856", + "0xf5cd54799d39617d19a06ee2daa626f86ac9488fc946dd51d0c3ea0d2f7235ba", + "0x45c5a7410df243dc116d756ed1afc9c9575c29bf42a900ce87bde66c46d407db", + "0x45ed6829530b5447368c907b52273896b6e49e26fb1e60f25066140e7a81c199", + "0x544de03bd9f6b2f515bb8708cf772a43d2574d7593ab16c33e5f60912bef76a8", + "0x5c97669b284cb0861325e31f26be94fca9f6a0cc9a8df08e2176dea080327cc0", + "0xb4bdd144963144176a88419fdfd43af2e260090fec4d35899dfe366b9a154ca1", + "0x508504a45b84c3f118cb588a05d8e5eb5deb63c31ee5ea08cfd3e1f9399c9087", + "0x8d8ff50ff9be1dee7385dde74b3c3bbbad1f450c74d66d57d7eb32fec270e7ed", + "0x1b7aa27b24dbcf6d8e3f79ff731490785d048bc11b6816832a8e19f1341ea9b4", + "0x12454aa2a9dd6898f4a2ea8927b119fc4f7ae95fc0460829d9103ed45d765c1e", + "0x6c09b5f620ff483922bf3b85570aee2b20eb83b36a53c47aef28c6ff3a24a231", + "0x3af55802fecb5c4e6d50096ef4bac47ce918f07d854d21897dfc6a4dfd0565fb", + "0x79f6234889ab5b26006480ab34223fd2e598f6644c207262416a254e01112f99", + "0x46e4e04fa6dd459de746321c6a320362f27d163a4b5398e942033368fa1dd411", + "0x9758314d21a47120ea7cb45fcf212b2bc4dc098081df30e1a32cf7d2093601d8", + "0x44edf8fcaece55ef27b27b52c6bedc5f1c6ae60f374d34433ae8000d691df5cb", + "0x1967a347e9d10ce560755cd38bc1176973540ab0557ed36203c49066700eb0dc", + "0x681102c8f0ed6491590cfa2874450ccc21c5df846827f3a0737a7f46d7d4e9d2", + "0x1e2addf6026a3f1ca563b3d6d3e467a560a0dbe9279f9002e856c241d013a381", + "0xed16879d7467e6f0976b7f0e40f777ae8e9111d3b86be8616fc60f29d02fcc7f", + "0x5167e6bf7d5830c277a186e925b3f49e9529c7faa0d91d7b4c4bf07e02046102", + "0xc1024500dc9bc54b49089009a3b59fe89b7352c505a3128991ba04861061e6ed", + "0x11f9df500da7f1aed3ee59b280a99008a5c2d4a817e40de131b427c5564bde31", + "0x11f9df500da7f1aed3ee59b280a99008a5c2d4a817e40de131b427c5564bde31", + "0x1ef781a8fd12825056187c33519e600a829c8e285a1fa20b24fe019e558afbdc", + "0x40afe6fc3a0caa97a2725af97c0ab2c17c2f83c2acccba67580534b4a65bfeb7", + "0xf379c3e4a382550ac2383abd399f0d4fd58c738bdb0af654fa10d9134f6dbd6d", + "0x7e631c75a8957d5446b8685663ae430dccf61b15a13f1477e133f3ea48e0760b", + "0xb5812e0a17dc39460c3228e675100d5324ec44c315d11d9e9f45ce21f2443882", + "0x16e2ff82ec30682d500a6301d18699a85da9b1f1da9a17df22f89fc299bd6d4f", + "0xe9e2f20791b40d97b797fde95f7b17c28fe4bdf00e16fedab0fd663ba3521cac", + "0x4db58704d06fe4f5dc8898f37bb927ea1072f0dc1c63b9d1cffa0382ae318547", + "0xa8c7e44de7e1b555a1a0098cdcd1f92e27c4dcbf5320dfebbf93040e455196a5", + "0x33ffed5daefc780445f01f2ec6bfc3f7952ad5e0ea849ce3373d60e68b42cc20", + "0xa73b142075d13357f661dd86e36adcf95e6fa499196f3e02d20eeeb6d91aed70", + "0xbc66baac5ec3a6d6314397f3db9de8ccce6c192580da7b07f76e7c858063fa25", + "0x89db683c7e61fc44a1dc6b46418f0d7510d4d9211630fc68fa9e7bb1a5f76170", + "0x07c93794350fd10c3bd1a7c0c27deb213d2026d6cc10cf90f0c62b2aad092040", + "0x2025f9261ea3788af58eb844472a6787e480c5efab814f59e936e26c27bc8c68", + "0x0d9bc132c8e68ac73f19121c2e66bfce14a4218aa2d2f18423a4a06824cad89c", + "0xa78cea521ad6b29dcba4eb35707582a830e60fca8171d2337d574fcb79926097", + "0x4dee66173b457809597dfed047ec7d279a0c924ff612329a173be90aa54cea42", + "0xdf70a15f1c421cf25e6a9cb98c8b08bf952f5a5e9c0b114f0ca2c0fccbe1e54d", + "0xea647d98bd5ceaf73229a026d6da568a74cbebc586c9c7911e33298add5b80ff", + "0xddbc1b694583fb67d070962d035c342f9bb60452fda4d07ff81541ae5999ef47", + "0x3af9f52c954ba7c9d4111cb0d5b8c92a3bb44569a45a6c5e7685c0668f71575c", + "0x22cfd6be2992d49431580750d973a9b3e3336e6333de9db7357b32d0f9d62d70", + "0xc498c892a34e3e593b688558cd18b2d7e77d9ed0a71a4b48be5f00358926d004", + "0x1aafb86ac19e1e06d632de46a46167bc927f197f086050f9f7af345164e469e5", + "0x23aa23365f51ecc4e6a377e3d70afa150ed22bf0acef2094ca5a0e2a6d3c8f1a", + "0x654b550861281405718fe6fe5eb7a6940f0f66012e1b755dca769dd183a23a31", + "0xf729958c2d6762b3c7a09737486bdaec0c2312970a08ae8bb1f4fd1b628834aa", + "0x0bd2b5529b63dcba936b875f23d9f0b638fe57909bf336e43ec72d43cf19a8dc", + "0x588661f9abeefc470a4a79fe90647d8c59d5de5af8ef9b6258062ee1a65969e8", + "0xc464ade3d3bba8b87239daf8d0ae71102d2c789c74b7572045a712ac7a96b8d4", + "0x8b8e3c98dcda505196295918d6ef1dbb0c0f98b06d8419050fbf9b345238ceb9", + "0x5b9cf524285c9e82171ad1f958fa675d0acca4e93ebb14322ee80b00b2059c63", + "0xb747504afdcf8f236c2326731c1f190caaa6b4e969f96550535c949d4ea92047", + "0x52e820014d7001edea1958cb43b4ec72045b2af1b2731381ef813ce4db7b18ef", + "0x9e46bdc08412a22cb1dfc9d421e5ad40bd92eabdc0edd1936ddf6c735c21a2a7", + "0x6c5507044ccfc958ba4862c54edfa2f960746685f91f2f3424f8caae410b0692", + "0xb7f9fb87590160d2565a9eeb2bbe7e9a7ff4e75666c4b3ab9a70f21f6a62d264", + "0x6ba0720d110cf35edfab63dd0bd195c1d2667f1489994d165832d5e03ebd9f04", + "0xa22aa5bddeeb2c40dc6fde79d7d4a26b4022889f880c71f40d68e398577899d0", + "0xffdf9c73808d6e2f32bb9517f081d95a3ddbaba5442d86748f93978c3a956475", + "0x22de014009eaa2f863745a895ddaf1182cee126f56c602ebe19f61df6054d982", + "0x23d1496082904a44f76bf200a4ab637d0a3e3cba09438469c13266aa78601034", + "0xb3b2ce1e8774b587d310692fd9e07ce08e48902912fe80b1563c08627a2e0e3e", + "0xa8ebf98a89a79efffe5e5820a4ad60519d7350b49708c1ab84826870790b3b82", + "0xa9dd261725db698f2badbd4ecf5b7319a9154d56e55b6fd457d550bf0341c362", + "0x4fd204679d55c7fe6655129ebd0fb4a71c3077442afb2c378c0d191a0e0288ee", + "0x4004e3cd70c77f4d4129c05f51922ed9051c0ebd1a91b236d87faf0ad908bef7", + "0x836de56852f79ec0fb3d643583b64eedd74dda0f2673f2db8218758447f0c7f9", + "0x7d0b8a9c278d3d1e7c7e71445aecdd90c307f68721408d871751638e29145d3d", + "0x879b20156e82284eafc8077c40debec37ac16631b09229ed612416f702d16a08", + "0xce158ee27bb182fe81fbb3b697afd448f1e0a7b78bb79f7e28aa4127c67052a4", + "0xd347e0db6a011654f1e8c7bd95d48d3bced997db7715dcbf8e86e68977859992", + "0x698e04c141b1d3bb4492e512c5bd89e10bd3fa9ae549b221e66dff6fc05bf7c1", + "0x02b537c4d21d2f3eb0bb3374f2d9ae0a6e33cf715383e6cb12f06c40214b5b11", + "0xd450d2fab7844d19b6dac3d3b61710b73f12a1b281457eb1905bd0670b503f32", + "0x1ba446394e5d12cb98202d167e41b9fa56c0f22f16cd070ccb4b50b06df3f98e", + "0x8698e69c4f5ba0d20f5f61f632d084334eb626855a7b8ac64f8e2b08bd5198f9", + "0x71556a329abdb0341c055416be880a66b53c9081304f5ff868adef145221d460", + "0x10129f8ff529f0b46ce666510e8a5a6569238f5cfb1b9f7b4a0110800855f9b4", + "0x7f9425fbe46fc7d457f7f6b5730a3bc6ff1a4162ef3416937443cc744f6139ba", + "0x6b80fb833481fe897bc25ddbd7c5fbfbd55aa892e0e81ed5f493b6b5e54f526e", + "0xc7dcb57e837ce0e9dfcc5b1f36ab432b56a69b224988af03175c8f6ae6b74573", + "0x9bb93e58c899d1a3e3c74d38294d17a55764c555904b9e7eedb0820ae5ec0e67", + "0x73f411346da1b84d7f9ab7f066cfd111fdcf24d67bc2499c8ea9cc794902852f", + "0x4243363a6eb5b4a36579d12870df8cd0089ecf1ce9720540d7459125f2e4248e", + "0x8d60dda92a27b44ea3bd91e79d1f543cb897edeb93ddb2582d726bca6739c937", + "0x7d2b0ceea5f23e4a105841c4651ccaae96b832db0776f9d86366208a054ca61d", + "0xb476ae06058f0e65d1d62d96726934a3c32e0c131515b615ad11e8d4f046146f", + "0xf314ad2b2e7b91193f2c4f1ecf1e2e43616d3a244461a62913f1c623f2e495b8", + "0x3d057a7c81eebe10561a9298d4fef14c86e364dbe7855fd6138c4f4c514bf388", + "0xe7604e1af8e8a7b2fb03a5467aad52576941d62cf5f5e8481a52220bc271bf72", + "0x7bac5eb9c56da55bbf10266d5e037e231c5e2af137e54f17a66423f932bec47f", + "0xc2078c89459149cb3c59460f6cb1a51f22c79275939ead18889331bc7cdf031d", + "0x4f63218fcc278ddcacba2217180b8cb1e81acb240e42d168d0a387e5a5661d38", + "0x29a219e5377d97fb3328d102ea7a1d143568916210042cb0ef13c07d1013fd00", + "0xcb4f1840959048a9d8dede65fc5c9e689d270b535e3e661d5ed3ac19dd5b0cb7", + "0xce866e3457672a5e58fd0159e0ab81f6a5985ee534ddd7dfc3634715f6fe077f", + "0x3fd8392e2e550e8358f1408549ba349cb33b161346f5e1a1069a40dc830778f4", + "0xf53eecee694224038525c98a21d452e39329015e95b1198e09868324d125909b", + "0x3bc7f479df24f7a871831982f3839913cc1b9437cd5ae07368a834a2b55649f0", + "0xe16cefcf11b0fb90e95a35b2f4c4787acfd1e307213b1155004cdf27d9a46cc7", + "0x1b59a899138d99dcb50a086e7c10b9787b98e6c601d9f526ee173cd2c5e62769", + "0x7b20288811e8c13a5d62ca72ae54423969fb646d772560bb557509f177392aee", + "0xe092a4acde917fbf37c10d0b544c9e9b10a8cdb2e78b7e8a6863e7531fa51af2", + "0x5101057287ea455d165a127e03491ad66578912f4d1245e8a6eda0e266b01442", + "0x97a25fc3e2be6b65ca27db74aecd9fce6c8ef92fcb977cd7dbbba459286495c5", + "0x5c002dc107da40407eb3a80457c86da3b55be442cf16161d4529161ad18633ee", + "0xaa043025cfd92ff2ccb0f7242a874460dd007db371f12c4b91101ee7b37a75ec", + "0x129b032df084395fb70e556f13f537c84047398a74da6e75c2edff1b1fc44c71", + "0xe29d49e6972ab4cd5d2e4e1a5f7aad17807cf60cf1191c0e97d2a9a9c5959f36", + "0xf83d96b11c5f7d49d7661b0445315679e2cde535f01d8ab3cd24ad8f57b44141", + "0xaf917ae2dbf54e9b498ec1c0c97e447e7014d51fc6a0264f53b50e8e3583836f", + "0x61c3e8340cbccebf1ae4b18deb2a4b1ae62dfa18719d9a59e1c73b4abedbec55", + "0xb7b5c4bff14fc50308e83a25b0d1dca54be07d2a60ace3eca97c4211ba470071", + "0xa3e5d5eeb2693b32173c0ef9f96e00a703367e28d79958ecc6ed9482e7d8160e", + "0x4ee129d4b6254fa48633b88e0292f7ae66e024f23684043df23e38a19f6fe8ac", + "0xf16b3378d7817398d29848cfc06efc82f83ce81f2858a779eb2eb942c43fb4bd", + "0x70238c08a12a0dab1514b0fc72d5da87d455d28fc3ef5be2a1ad9b8a084a63aa", + "0xac8d5199f386e8907bd80205b38d368112284dac0881feba505ecf6fa382e4c1", + "0xb6b47434d327b9d5608a92221c73e7c7283c5790ada6afb96a9d8868d8cc2747", + "0x1b6d32eae45e557f2960aa8624b53641a747d0f44b5d4750ae76ffdf6d0f2b49", + "0x6984c795da667c6691443a03cd152b9d74455e5be4d72dc5a177b3329741fb8e", + "0x488328105fd18c54545cb68874105a5569fb048d8efa4338df370dbbcef46ceb", + "0x9f414bf51094d2b29f9d6befddf33e2f4c3e215850015c19312bcf5025155f86", + "0xbeaaafd1349f1168482fb1dcefb617009304b4080b4440f2fa67b76b92337eca", + "0xc3d1ef1e7d95067c93d5786446c0b5c57bc3f4dccc4531623368ad20c8f6378c", + "0xd992544223192ba8ac9f745f7f386305d9d0503d622958f2c54a06e87a5e04fd", + "0x64a0f7f73ee7d955871f2198e3e9f302f387ee717dfdbcac58318ac591718b3f", + "0xd968a4a6d06484f6422a3813a345b3bef74a7c7256ca5daf4907c3719428fbb4", + "0x703466bb61f1194f2add53bce2de20ac6ddebe56ee061761c451c67d6d113b4a", + "0x59ae22291f20f25dde1b1c0c7a565759fb021df39fdc9ddaf8ca9d98f3e9516b", + "0xd01b585806a2f1be22184cb8f551e37dc7d416c2d1e0c9e92b56795b1c5a09e7", + "0x4a1ea644e0a5974229aa3d7b904fa41b53a3cde1fe1dca5816db164f3b36ef38", + "0xe4981b0053c76f4c9915741046ad4d3becbfa370a747b081c05f084e5508896b", + "0xa80a92ad4846a18bfbaac71b5031659ce1b1267282eaf579a7831c38c0d3e8bc", + "0x59ba2c7b68285bca54df68748ffb04f14a9794c79f7cb50acc83720b15dadc1a", + "0x5b06b3012781b433c2b5163f56d154c734432a98cdcabfb2f28f6addb4d692ec", + "0x080108d4311d57912bd6615ff3bd972b04644c556e3f90289b7169c108a99661", + "0xd22295b0b398cb7627268231ead1522a1e2ab1842e665c5183dc6a84882e4030", + "0xfa706010f23d06694d93b3d6e1fc9050713800340a30205fee5de63da3809aca", + "0x4602bced0a54b84eb30a999a912b4e52761efdc2ed2da6928aa85ca097859108", + "0x2c1d2d340b8abe6f71035048288cccb80eb6e7c28e527b0d9d7cafa047db81cb", + "0xdefcd862255ecf02733b7db5862957fdd8a6893d88b4ad6c7ca600bf50ac4247", + "0x089d951890ae8dde0c9f3ba3405f58b0ad435d65693f6128dc360da00d6e543d", + "0x8984e8c987e74e682431d6d1d06ed71568a0b0af1c7588efae8c4c9c322b317a", + "0x543fd70a4a2c5d9aac760fa065d14d6891aedab786b455c6c046958e04d9e51a", + "0x0514b7f6739485edca01db877aa11574ba89650bdd47cbf67fb8303f544c11f9", + "0x69b406f1e6e7e3a3f6a96e6c3457bb229ae754fb0605be0b3c515a28e84d4eb9", + "0x4a37e42b6219ac692b5608defe4ceddb058efb3b402488ab07147a490a70bbf9", + "0xdcf85c0def5ef38569612fa6dcdb8f8a9ef2cd5ae7409474acbe38b19b7efea3", + "0x7747c6fbbb686bc9f6a569fcad33970626d8ca264dceb27ba7555a8be1da90c6", + "0x5d4c08cb19cad77f0d4e589e92be2d25c54c96bf4f70ef8e9ac0f5649e519db2", + "0xd9a3230a80ae7a3fdcdc68f7d0a50917265caf8bf81a24bfdf4c4d2efeea6662", + "0xa945c91b196593d726f7db2ad5234627a58ab68dc365f914af5c292e289e41c1", + "0x8dfa3e3f34b3b59923b2f091f068811cb1761bd0bebb25ee613fb266e0b66c41", + "0xb4b1155f58788768943992803077a2d4a753ec2bf4a6bd5414abe7edf0af5a2f", + "0xd556bbcb90371f38a9ad8a7be9ffc34315c6cdca3879b34e7784579863dae313", + "0xf7f4c7e55a38602ff2a8c2adc188ba99e6e5b97f69fe123747751bdf3af0343b", + "0x80972d0b46bd0f432a45d82f48d0959a04750c8e7efa2a8857b638f13e57bb83", + "0x6c6ba8b56ad8dead5f3d39a9dd2458ef187ac4f88e3a13d7d1b6dce70a0d7113", + "0xd1c2761c9a19e15f772c0433e3bce635c0260b8b90d1c07730111630c4d231f2", + "0x65569bab15e349eec4ec38f21c48221b341f33d8d2f744d6a2ebdb228bf85dec", + "0xfb4a7a3bb3c168b883c5fe48ab82cf36587552cf2f90e3a63b3d2f8ac887e3e1", + "0xea2beef27e3f9d5c5baad3bef4d39928a5d1ac4d13b57b1faff02f0bee2175a2", + "0xdcab05645366d8586bb416084360fe5fc68c28f145a0814eb0b682fe46ac534f", + "0xae621f5d3fd393aea73d4089366a196b2f5e0757b528f4a6d065811042c559eb", + "0x363ebc81315482dcf4de8b05f3b1abb78b8c1211fd46ba52fd6d80f4faec8a74", + "0xf019b5ec70f6d3e83f3fd143071b78dfde9f1fe26665009a765d770b40552f97", + "0xfa90a20a0db39b23990c05745d647719153bcf0af30d77ca7c89b67c979b57b0", + "0xafcabf117f32a5b61e50b8007be5063fde0d15bc3d976975557cfb2e9b032bb0", + "0x389a5fc6ea9f2ee2e29c2276c9ee905ccb8177ade403cff54f0850fef9836486", + "0x07261e28852448a09481639d7f442e589703cc2e62ec8b28c356607a64d95066", + "0xbc445f8a0773410bcc8ca2d482802a449758edf284f33475e49928039ba0c1ae", + "0xb773fe88afb6c21799fe610eecc875cf4c0219c85059b21956136da86288f396", + "0x598ed7f7e23a234e49d9ca47420a8dabe6052bd0430d5088bc4678fc1404485c", + "0x8ad61cad58486a739d7f8616ab509698608f9dc74ed2bf34afac46196215c0e7", + "0x39f179aa8b140698515375bc7546982c7424f2b9f60b5e897a44b72dfeb68e48", + "0x9a216f91db4b4aae1c2d39ed033a676aa9d9e8dec8b6b43722b708f9995cd8b0", + "0xcc7c7a5c323dfddcecf56a2c2d037dde677024d9ece2312db01118499217b226", + "0xf2c830232ff594a10ba39b4cdc5c018777895fe19a1f44c88c79663d9ebdca20", + "0x743ca709ba6a7cc4c5d56fbe9b5151ba31d739c205521c00d6e533643078922f", + "0x721e011c488b10c18c246631ed091e46aedb67e47624e62a05672dfc0bc12f94", + "0x688f7b6b956d7c5a120af56a34e99378bf995f1dea293909cd98f72147fd44bf", + "0x4090b41d6ddc4a78aaba1f796a7794aaf77cba244a9325904aac090de18196a2", + "0x1d7ec2b8d489dcddf17a6f7976212e9b808117e9f932c015f7c64ee5d1d1ae6b", + "0x75b19c648149705fbc139bc85caa72c4be9b6449a97119f59714ec06bb06b180", + "0x1f4292242722669b1de698d4d08db5b43e9f606c9f840ff131a69dceff780e92", + "0x6c30c47c0849244e345a23fbb09173b695732bd9ad7b0e3855dcd97813da0bd3", + "0x97562c896b08096126c0b98579bc17b4476c3a17fff4a4dea5d293183c42213b", + "0xbe766059c9c3a4912149f00a88d06c59928f76c9298a4493577a49ec95ad1ee0", + "0xf6e691523dba74ac551667e8d746a24b55c73593d2bbf4aaceecb0b5d56d83da", + "0x5b42292da2a38dbe29d5ff340fb6522820dff5d77278db609d82dc289da36f4d", + "0x5f03444e0f7241d5fa5829f83f6c49220e196fb51da7c85057daa40c4997b538", + "0x04a264347a7ba27ef4455b9e6092123191d3979b7bec19e4b0058128ee22c2a2", + "0xda3b4bc13f1c2293b6951ed4c564823fea1528cc6380183cac4b5b13950a638d", + "0x1a12cd03f0dc0b6102f2e19cbd2f3a02c5d4a13b856c27aa412693a12277388d", + "0xa4c4d315a4ffa7576f77a0704928a2a914f6ad379426580db40248f91af5f96f", + "0xd018c9d71e6bd7bd9ea5012f371b418c928ce40b821bf40ad5302e032d1d7bd4", + "0x1eda36458be9aa65f706de55fc70715f0f6f0716288ef4b14388e736d9e7b696", + "0x3e076dfd07ecd993ca2f38e6d1aa926932871dce01fc8f1d38d534eab23a42fb", + "0x7e5774e651346c288bd3665e811cebcb7116fd0614b421f7a25ce4d20995ee2e", + "0x7101a9c9f443ea528a6efe0465e01fe48b829c132b7112a3e6cb2e34d4587e0b", + "0xba3ca6896301ff2ee66dddee548e2bf2acd20288ca926af08051c54d85c37a23", + "0x1c16a3c71417734eb17c567d64dca2b057e22ce94c54171c9beb447cd24a5119", + "0xe8b1a702cf03d0435222ebc75715801d618177ba107f4f046d36e1c460b5a7c9", + "0x9cfc4a5a525a4ca64ea54c82aa9efab62f4763adcf796cf7bb5f2491d84c7cca", + "0x21127e6292ea806d3dd087379eb49eff76c82df0ee8e8702720ff9665872e505", + "0x07758805518f005ac1c79201778d6a825b9e58aef2b15f8165dd9819f5d62cf3", + "0x517e75a4b18368e78e7930ed68af85dea4b063de3d3278d2514edbfa953d8536", + "0xa8535f43e287121b2a8fda15f629f1439e80d2f4dfd9d7812f384178207cf5b5", + "0x5162b5e3d694f88f99bea039c574eaadea05ea8733c8d9b53ebea494e2fdf384", + "0x12a81cad7259730842ee38ef0f48509a6f844d931a92aadb4538ece6d23ed58b", + "0x877eb6432f1ace791631c4cafdb91ffc8ecaec0118bc40ac475f79f3b850df6d", + "0xa84c4a2b29a8d653b61079b188e19879f4c49950e132b10b7d56d30225c68bee", + "0x1bfaa1dd9a6cf440561d6f7856650f01e85771d29d88bcdc7fd83c4f747fb6ce", + "0xdbc399676711c57ac8ad091e28401b1d3b90e1789f52eb9ee1ae4d7fadd3df75", + "0x476d7f0e1bdc8d0bdeb626c98b72b72799ce3351d4c32d6f5ad1fa6249065cfb", + "0x51a0e0eae152092b32d2d545fe3b2032cf81f2f7b18605a7ee8346e9ffc9f17b", + "0x5bf571b1dd6bf6d9d012277607b4eb8170349b58191504435150af4637692fea", + "0x218e71a6ac305428d695b6bc41155e6db818fa9f5f2be1ebdcef9f4acf9bb6e8", + "0x7622f736852c9a97fc707353acfc1122c06de254b40c552d8d9a960006eeb623", + "0x23e0f0ec056e4576ac6f3e482719cf4fa072bd8a4ad5f7073da76b44263883ba", + "0x7883e642ef2a0a7802c2391720275cf4cebd72f1d4063e43c07e09c0632cb946", + "0xbacd7dceab12cbeaec108f834ad4e2df271f6d7d6c44600a9a24d945408190e4", + "0x7248cabc9b55f739e7d7c7ff7065fa802a6ffe071fbabae6da31041647fdeaa1", + "0xbe49594a79f6f1b828e0e5a3a0e8cecc1aa0478b55afcc477f0d2ee1de8dad6a", + "0x59ac0abd39985a25ceb75fc4a3614f685500ecff16e000e6f33a456a110b0da7", + "0x982e0e495fc234f268969ffd0d496f6e7238c5a058e21f5a1034784a49b67099", + "0x9f11e04c91f3aabf8a0e80e11b12a3194f523f042a3e753bf1079a57083c0319", + "0x64f5ccac88c12db7d0cbca5b01c3a10289e88026846effeea072571010192d9f", + "0xe103ac048893261044bc5917ea6942ccb0a4a4b97c55e97133c9cf53aefbc692", + "0x5ead86cee43030fcb3877a00531af062389eaa5b8128dd7f0bf4289d64e07d57", + "0x475637f5b96f32d617f8764b8ae1d5e1d34055cc11c2a7dfe8fb7719d13384e2", + "0x4b149604e06cf094a6140a5353b8f6278be52f2b9ec5b9ab800cb35d0733aa9d", + "0x9c00301e23e1500bc6f68e3fad44e24583fe0c9fa5cf80e197131e978ff731e6", + "0xa591f947cf446ffc440868acb93b95853c2d8f4f3fba2147cc8728f0014d3d19", + "0x8779e707e373bc96a5d5d1a6033fe0d97c57274bcb262be7034d002d4c91188d", + "0x84b3c3841c714ef3bd4b739515851f818fc4d587f729c5d37e17e748a76c81c9", + "0x762ae4a484d2a302e672e0c659a4a02b9a12e63e22a273f8453d2c7a5ca99f15", + "0xc40909c08615a532fd1fdcb8dc3d5e49c3df1352fcefe1e2685dd43e485fb1c7", + "0x5cd51ca9bfe125d30b5a368e652cd284a4ba6e6b6ec538f6183cdf69b23fff4b", + "0xc83b224b1e7f603483c469c797a61af50a7edef9c392a8d94af272beed2d79e7", + "0xab23a079ffa09407bb2dda81e052f380572a8fc275b1f19b651275ff8afc9e5a", + "0x0fc115ff6711bbc92ca215bc14cfa2fa4798d03dde5ce0e23591ec38750d3ab4", + "0x5abbac3eef34734bf7ae0520735fc34c561a3b74dd1c15b7d13ff52b94f2ea40", + "0xfa629b85dcceba5deff13afeb6ed5f3bcd050d54b5aecea3e5a0825dcf5404be", + "0xb02c610ef9ce2fd7bdb1d8fb043a8e5023f7d200198e19394554fb620cc0a51e", + "0x068c419946fe4f06aa468d93908068bd7297ea627dcb76af496c72223aa48c4c", + "0xe22db15da2a57799048916f7a67af8d9dbd7c75340093bbe579958cdd1885bf5", + "0x4398744d98ab6e758f0bcdaddab96e64c90600009404645685ba7f331fcfcc4d", + "0x98263ad5bd3a9d9d29d7799bbafde6a2b88b7541b51ba9722ca327292e4cd45c", + "0xaac348f0ec07b55f9876e708bae1f10bc09a377b9d34212382f73d18c98d05f8", + "0x70107e7ffee5fe6d4cea03b2f5a87d33b489fa91859bb026efb6c157e235995b", + "0xdc3bce19f01b66f6c7584ef022398e6122dfc86a5d5e948f19bc51311f46208d", + "0x43fd322208354a546588fde0489d38342665555447c9793ea1750851d98d6479", + "0xbd4a65ee9be77130c70740679b31639694b064116795035991f4f906ec7da1c5", + "0x0364e790a4698e6ca5874a0d6f60e04260b381d1e6b2b8953d8d56bd39caa927", + "0x4ec057e627baab1a49662c18caaba867acf62cf7a0de12ab21f7c2644c490a1f", + "0xa195c8fd9d578e3bea8354b65abcba567252b5bc1637e50b7f11a1b0b2ef774a", + "0x1b58a6b0d5db5a365247383810c1b72df5208f2808e6b0fe0b8283055cfc8432", + "0x9c6a7a25af41323c386d75a95bdabefb02221c18c68095850551e8aa5cecc7ac", + "0xa16295dddcaa18e0fa4d9faf81bfea375c027217e199085cded5a53e56fc7ad5", + "0x5e9f98aa85aa3f70b28763566f0c1944db1ed29ca09e61aabb172e5e16e57658", + "0x4043f6c19ddd2e7093e46351290dd0048eb002dc0664fee8e82bbe383d812277", + "0x4c956159f1041387d9de623123f73adc8ac1e349858b37b4992f1df81ce1a433", + "0x1a6fc1b5e8b5ecf497bd83fa3a0496eb41951b35f655a485f0f42b4c440a74ee", + "0xd751d6d562ee035b527de6d571a59ece1bf2edd28cc23ca30154114e0bf2ed0b", + "0xe64db695af24d21b5f74a84619584f534916c278722a30c189e80a39f0699f6b", + "0x8cee1eaf4f557eed87237fe64eb883a3a79ad2f980e61f6c3d3ca2af81df401d", + "0x916c6d383748af5cf135ae33167d1ecbb0ceda8048a4425c9d0a89ab3cbb1ae8", + "0x9eefbc3e15c4856c2b143658aad57ccbfb4431817d3834dd6b01e7a4e04d6d46", + "0x0d90ffc4c0779d860e2832f5e2e48d4c3ab39f227e83f92a6397afa7c42d07a6", + "0xecf0439964a279881ada4f1e57dd90a714ec87ad187b9eda80cf03b619a0724b", + "0x31e5d78e69c955c3c2e8503826391e65a445adb07be1219a52e2e633783c131d", + "0xc184b4de485fde6bc053f1426c16fac6f0f69e1bf4727b25994516f923e78ab2", + "0xe8bc37646a014f2b36280484474c591679648dc2b54cd9136aa4ba90710a4782", + "0x67554ca1bc3b3cbed482ab1072da2c7d4701d0ef5b09d08a24f7d49f33fd9406", + "0x277ac4795957a92fe0895c039d14476eb0cf5166cdb816e460e6ead50d53da00", + "0x8dcfa78e82d8f40f51fc90701999c060ea4c0b5f71148ce03f040de70b2fec3e", + "0x989f488cb4ed8711a67b0108b786fc9dff3492ec62152e9aaac8ffce98626dbd", + "0x93282808d609a265797e2b6b819d10ba569f825c06a62ba94bab5052d9ccb8fc", + "0x17c1b5d3d5a33802f51cb53b6d48ca5927b7664646a1901308c49d8a23af18a9", + "0xcb5615b55f81eee115ae2e2da45ea9fbc5c40d4888e98a7ea3495a1375ea6872", + "0xe2c0356c562a023972e4dfbc58bd80613c33558c1714281727d45d98a64bf0d7", + "0xb9636368b1dc7d2170edd1ac5867ee43a7800f5a9418b8116ca453476fdc4a5d", + "0xde99a4f79690ac778cfe06a091f6aafee46552a7125b1a403263851684b8fcbf", + "0x79d13a5212ab133d474f2f517be8aa6e7cfc89150c774ebe95de5602c7124ba0", + "0x5f4c62bb72928078e6f957ccf4e663b776fa4c5103125203fbb8d7f4aa7f6017", + "0xded038ebae1149b1059378fd46b2614ce3505e24a92ab679f91bde3d3aa0e43b", + "0x5939e8a1154ce6148b03dea56e5f5e1f55173f3ab163be5fda5719c8c87ddaa0", + "0xb97f13a37833081359f5d06c47e0962c7c4269a1177dfe43115010186d74e379", + "0xdf072e3390fb781d8996f2d1bb09e594dfaebc8eda381232e383508f5d58b4bb", + "0x19ce9df9aaa627a67a5a00165e568bf00e26090b69a836debc1480a67b406a77", + "0x6ecee61be8dc6eeb3f7da5ac8196dabb013e11d9c6fbedc124a7821c8c234733", + "0x9734aeb22fecfb52be020defe48811921eff4a38c0fcc3e1b84635f1aa1bfa16", + "0xa038dbfb74b4c44e684214f6bf3a35ef188cc6068a7b4350fd90d1142caa7b21", + "0xdb21cea9406642f202fb998110b5386c530025ad08e34914f0f0f3370ca952c9", + "0x20b4320acd39f2a4026ca1b8e6d6df90086fd9023b689c101db4929cbaafd353", + "0xec8a8459c5e036d3ec6d620eac50518ee1b0587d383ee4b747867501833c60f5", + "0xc2b993c28dfe0ce820cbbb8e1372ce4dbae3addf25071eadf6291f53ab8a8a56", + "0x72f8ff8fa6c6c18438067fab6568e77eabce6f822c76ef360eba3085ec4e5e9e", + "0x4ba58c6f9be1aa55025b34d78c552c439fddcbc997fb2cf8ea5eea443d021848", + "0x256ba26e426dc51d28367b41be5b243e363bda0b5660f06b47f8815307d955af", + "0x38fb6162a26422a69ee8fde4c7ceb212001a8223e2595c7fd4029fe294498d05", + "0x13494d9d00739897baa3c1544d665279201070bf82a4bf859a6ebf4c78894cfb", + "0xf347a57400d6bd63ad7c037b0bbde5a110df69a1a591c8c75c56ff3a6ca5e4dc", + "0xd1c3e08480879dcc2964c24d8e45c0b95b9cc580901acba6a3e069bd43b6b608", + "0x95f82ee3506b3621838c0a2fba3ba3fbd0302b12a9b835271a74533f97fa9342", + "0x1e49f9eab0744970953dd97bd9012753afad32534b70efee936cc37ce58a6a47", + "0xdc57782ed2ebc4a9a93aa6490d8050cfa6c1ce52a8d84c37ae4bd8fee754a26f", + "0x2cf131af95ca573b37ad78790de0ab79e077f8b52358faca17d762c102ee16d0", + "0xb5b2c9c15790236d65533a0e50adea7b8983e995fc9de3c11ed83b4497d18309", + "0x638060a3f0fc0c1161bb2aabc5d6c8e56bf8257d042eff55e780c6c0dae3f606", + "0x05611e32ff5c8290633c1e96cd3ced4c6dda69a67f41cf854f7fac89e5e5b5df", + "0xe8ddb8a41a4338ccc8710361565a9defcf77072350276db67d703074934e18c8", + "0xbba79a27f3600519e739fb31a7d9a1e481616a31fd6f96ff2c53a89ec7911ad1", + "0x03004ce13787bc44d0ffd5b2856f575b4b00d76c8759f6d83351f9558bed40c6", + "0x24d644e9891bd4c32bc84856337890599fab200ae43b1781dfbaf14d7b2294b4", + "0x8bc0de94070e54153d3ec2a6531a14224732dc307d5db79a56b214574e05aaaa", + "0x315d866433d112ac5252425d246f92640dddca1f7196a4707a13a1220f6808af", + "0x2c3a43c348f4ea7ba0ceb1b2a2223cc18a1d7fe168d6823f88f9946e4ed35694", + "0xe548faccfa0182263217afd60bb3a2b3e2ddd0bdd14f50d55f626121cb5694b2", + "0x39497c96b183e1d35669c093d5d0c9e0b7992ba6f5c86d76e791420099757c91", + "0x8f78dc7eae8403400dc799882be2e011dab21650e2786871ee85c40ec6a13cac", + "0x08d133099f23b5886c05b1f989b387848ab7209dd8e70bdccdbbeddf339368f4", + "0xbcff5981c4e800dd0c52bce2ebff6c4866668093d2826b7e43c9a2cad4a91b85", + "0x33d9170b5e1328b083c21c3c4113d1e325e682f9e7d660c87238d56a1ed23a16", + "0xe7b6c7f6fd6530654d5e55db7d3e46d218eda7d8ede7f679f0e6d27e0861da90", + "0xdd3c3613b1c85348709fb89a19461e2c389cce7bdb3cb41a760ac4735e647a2f", + "0xb96d74e1f688238e995716a9f9dced7166af1fc1254619ee6560c45cdb8d3c61", + "0x9bd5e57ad28a91e8321ac0a449d5abccf9082f99970e893e3725a161d48dafd8", + "0x5414bd1898228c7e054526cc326700767235e1a88b62f9e435740bfb02da78a3", + "0x6ebf6d9f475e980138c3d2e8741225b9ed49b41199ca7c5e7ca9beaae9eaeb04", + "0x0bb59b7025511c9f042edfde6c345abbd61ba6acea252960b698917b1a5c9259", + "0x431197af99349c6610c75762280df3dd1aeebec6f31c95ce5214a290e8c421dd", + "0xd40598a6ebbb37c3a87e706346cda5390ca78aa5cc9ec1f62c408128d1a97092", + "0xfbcb4f98579eaa4e6ace0994bda215220763b7cd8c8c3dbf90da34cb32c34c8c", + "0xb5d01c6d8283c7aca215eece65c8c540ac901b98a27ea9b163b726f6efe842bc", + "0x8e0d598d23a9b5b96d6a35224cbde82168ad7fff7310d28d57c42614332d276a", + "0x0ba54fb17592b13b7c92bf38cf85b779ddca66827b87aa265897cd1a10dd73f2", + "0x14c9765601317405203b3e62aae592b55bb421d9bcc321c23b6a0f79dac8c046", + "0x00b2cceb64b444adfd7fad0bc6fddf083620533bd20ce68dfcc4f45dbd2e6648", + "0xa477e1564f0b5c8c04cf077d9c8e717b3527bf64af2ee93c51bc5005801707a7", + "0xa4ce46c5ed7a7fbc257d58bfc674dd13bbe933066070b0aeba985ede87115481", + "0xe451cb942ba6df23e7b150e8886921ad911afe99f053a7d5c905354abb357e37", + "0xc84d0c9838fb125ef6f81d2da974a90db8b298d549af2e933260b019e8e2a225", + "0xda0bee0382515ef5ec765024e0d74c474ac680fcb0bde66d3a891d0b5a59cd4d", + "0xb44f2040ce071f16b0453e14e1319912adf56f47f58f2624cd7e797f13450c8f", + "0xf25189770bb0db0fbcd234c9f6c7e6854b31e4118e9620afda424776c1b4990f", + "0x7240b30fa5557e954726d803f274f726999c5026c0b41e32b97cec758cdaa312", + "0xd0d7a69e5670778a9a5be815c0a62b9893ba6c0cde214740810d00407a53c5f5", + "0xc0611250437fe3fe98314e73a81814e26c9459f5cd4002ea956e27ac97b996d9", + "0xd1e0864407b3f5e30f5589fde4bbcb23ad76dc19560d24f1eecf76a986dc0517", + "0x3b4eb8f194d78e86c76bf8807ffbdfd2f9d7615b667f1ce132f14d8516778620", + "0x167040a0a5a019fcc0519ca2d3e51f09a3f1b4bbcab168b589459c91b94652bf", + "0x03993832c29362fea10d78cbe19f8217840d91454d6b6c54d6c7033350af924b", + "0x7545f672db6a37660e391e4fb868bbac1ffc2fe2de30edf2589493c433b2ffdb", + "0x446103019b6b2e4904ddb4131f2cb93b3a3550c5263706e4ccd6a88bee349d8e", + "0xa8a7c4e32649b53da73afcca72810c81a18f60af59245ab527b37f854787e726", + "0x8bcde7ce82466b32b05f57729664a986c8913c4372a85f676f0c3442299daecb", + "0xb634c487032bbaa61d0ccaad1ad354c06f536b93ef3e84e05bcd745f5dd398a4", + "0x454e0562ef1f605299d093f80bdbfdfe9992209234c8d949fdb80b81608a4593", + "0x3243fe2208a9c05e21b0b06dcea594f3b28d899dfb69149c6a656b593557e8a8", + "0xf5aaf25279064003a3e4fce0643586a148782012870f24071efa1d94dcf42d95", + "0xc10d8fc1a8e5b909aa6b52f4dccc707e7aec087012a4e2e06d029243cd103a2b", + "0xcd00405f858d4ea70c6fc7e6f579bcf99caa6e05a3dd7e93542b5198e0c4f01e", + "0x17c64fe67c621982c7c8fb60eab687274b05ff883bdfe76d42c9d499496c1de7", + "0x7d4c6147fcad3d5ea7529d43d2cef46baa26b88e5456d9caf231c0cf73de32c2", + "0x37e24e81ae0dee325829f5836bb640cea909b0a2fbf2139701a34eae8ad6f876", + "0x0ea6cf0af3f55815611f6dcc373531974cf2b430ea18f33c16c90c89a17ac8fe", + "0xfe34795a90fc393b214ddbbfba1bf2f4cd0601bc8ccb8a1dda39e0a07cb911cd", + "0x839b61164add05c59922045c2955446d2d2508db5b815dcd98b1b3b28ec3d8d5", + "0x7153987a75c41484c2274d52bca93bfa343814ed9fee74721c0bccbd121824d1", + "0xb40a29238109dd35d4157b9c8c1cf268607ed8a1594bd601b6c02ef42f5540ef", + "0xa9aa52afbf0a5970a9fa7e9899ca6309cc4c7cc590978b8ef7f6a05ce657a3e6", + "0xa9aa52afbf0a5970a9fa7e9899ca6309cc4c7cc590978b8ef7f6a05ce657a3e6", + "0xe0515991944ae65ceaa72c16c7f99a443e60b580264b4f31caa501c862a00feb", + "0x26fc18c7d82828330b1a791821d3755030a8845fc591a0d2a2684ea02ddf7fcd", + "0x174f51ab06e479c01c95ea1b4f216b10f7dad33de8c2489da84ed12975df65fb", + "0x6753b6b7e31c6c6acd24181cea9fe728d15f0b63ca7b85af75175e6c97bcfffc", + "0x79616a9ac94125d52c7ba1a101e5ce48cf7cc8c3331422b988cc9cd9f6a18883", + "0x7172a22f05d4ab47d82a11741528fab34ace01f05059cd375fd90df2960c18b9", + "0xdc75d5b7c9340223d28790b61a12f0cc8bf52b963f43a2b82182e163d8d55204", + "0xf6e8cbfce0c4a5d46c756dc91151b5806a7a68f1d807c59e3b9ac9a09dbe8606", + "0x3b9898984e972323216e870e4dca479755c332a3651e821ed0e0c50b3370075a", + "0x48113e802b47c201677b91d255fc74d0a7b8993a628b5926b02a614c4c4b74e6", + "0x4aaddf819f304853cd11efb216788b881b099624dbb059b9c6890fd060bb9e25", + "0x58c43111dc7176593a47f87dd9b0beab5e2008f2ddd74c49ceddcfefe8f37138", + "0x7661175bf504689f720a3485c9bfd5a54fdab871256efacffaa6db5e0395ebc4", + "0x1b2d3833a29d7f38ee76f0595f7abbbcd336bb969f4bc1b4ca86926368b22abb", + "0x3db767c9eef51ad88b5feb51477e5cd9af55a66f33dcb03cf50d603e74299ad6", + "0xae6754522e5ef7eb740b23e1bf41745c73016abd3dfc48deb6ed75a1fc190adb", + "0x3c2e2b194de85213b19b0d4b25bc58e87506d4ffe978498c3ec3507411a06e26", + "0xec184e89fbd713409674657abd3bf037565697dc5cb8d9cc6c7abc5cdd47f0a9", + "0x6162b7225e824503c0a5deb63b93fbfbc157c8ec22c5bb0cf28383f72c774060", + "0x76129af407dadfb70b2fff503055f1d47ea5d49834a8521695000dcfc89a12a5", + "0xc446cf9f3f5c0f387803ae27ce69a88253c434e575426abc2b9603aba21133a0", + "0x2e09b7e4b7d3a782f86f3a68f5438f7d43611cee5af41d45b80d25e6d756847d", + "0x5e9367429ed2f5a3edeea22cb3e38549bf6c5a52625aa81a009c25e451ce579c", + "0xc89f13b90b0432ee6d3333babee866318d2955262ad6e99417601edc3518405d", + "0x8ed80c4fc233fd0739326d8db6c02df6541538c739188bb428bc5c8c9a446e00", + "0xcc122ff8259cea3b50efccc20c0004145692bc81f8ba99cbf3eeb456a24475c4", + "0xd1e35543c8d52935c3e4efbb7eadb7f7110ebfdee065c7b8a0b76706314eda13", + "0xbf35a418f823cbb6fe61d57dd9c36996c3e44d65dacf118f0c952166cadc2cae", + "0xb71a34a53dcca6a5cba2b04dd1e6eb36fe95b8271fb9955bbed616a27713a376", + "0x9c44e5a878f01bd80cea9fe282ec14cc254d5f272147aca44381775567ce2ec6", + "0x97f6e201a1052a1a239267a31a6f10a0a6ad55f0c21354c56734677471aeb4a9", + "0x0be47c71b9087c1113673ec888169b4fb3653035fbbc94a8e319a5d747471bb9", + "0x598d490b128350b7790372f82bb433c5df273e4ed83dcf29e3c8415c9268a416", + "0x513ff220f6eaa8ab637c0074f3206b46008df21f516f05c08d2d5c1b2f6a890e", + "0xbe9612a2ea66cbf3ced1540c231132f879f1ed4d1de25c5626e060b3ce1a7a60", + "0x7972643b30c2a67a21ca76ec2d1b93bf27e7bdf1b44b8a8445b2016b2342d888", + "0x5c736326be43f77fa4e8ce410e62ae542779bfc8925db0b78d180316f6193c36", + "0x3e074ba2393fa1593124841587d419950d5733e08791d5bd02d1c0df8ccfe152", + "0x2a3e707dc8ec94e28917bdfc81fd6f1266eae4bcb61af53a182857246a5ced02", + "0xb2d40cdd5d05fb5a778fbecc45bc11e88c1de22ac12cc92a6079313668cc1c2f", + "0x6a5f8fc0f917eb0b2675ce139dba5f0a087bacfbf5be5542632b021804f42991", + "0xe9c62c8f195355c1889e46791f296333cdf5f545c76e62c1ec00518a4de968ea", + "0xd3e39b51a5b019047285247e59e5d5393b19e2afaf35b0b04addb3bd38850895", + "0x7995b134122ddc86b829c0acbe768e9f4dd9813262279aabd2265efa9e641ec7", + "0x4f534b178b1f2a28c7c02116fc540572d4559b068e0394ed31dd5b473e939c5b", + "0xd19e723af09b546b36d040be557f24301ce991984dadf5e9a6bba729b16df2aa", + "0xbf8d5c0e8898b5cc9e46a94abf4b8b01948575b6928d82c1d5cfa60bc170516e", + "0xf72ae81606157fc800c8c3392c6b2384d707123badc7df1263d8d21709946cc7", + "0x863c435614ed40478f9762fb0c8762242cfe494d8003ef0115da2053dec72531", + "0x96fb19207a7dbb93bf1c6bedf770b426cfdc6bb131bff44f434f6c6521d2a69b", + "0xb57afb0c76d54bbbddd455e40a9a0871f55361a34165ae9eec69b01bb80b3cf2", + "0xfc68c39df6af7c22806ab2213d5c1a472303e5402bfabc375835f6f8f321af46", + "0x83110a2a44f16a285d5adf64c3cafad6ee1bf17b20d18f1bf408fdaba902546e", + "0xaa5d58af105dc17e5dfc9052fdc3f9f24a9563966ac83fec7026b7782b0cba7b", + "0x98b60d88a8f2ce069617e6a3c3c91ee9978c823165e1c0ec3657af15fcacb80f", + "0x361f9efe0561376e31b4eacb1daa65886443be03ce5a66a064ccf5e74c1d16dc", + "0xabce88c5c17e767f974dda30ad58fb0440cdc209566f4408d9aeb68e8aab0383", + "0x1ca70826d218f8e58041263aeca77c9d5574de8ddc20ff7a98bfac5285e96eef", + "0x1dd79867e30e52eeed981308f4f79af41d1b9ce8921e394269507661319b8491", + "0x7e71ddbd67e06a9433262c54397cfb755ed0740dc876bff9fd72556f706b94b4", + "0xda674e4fba2ef1c1cc9bf435521022f84a6bbd9bb0353ce69d65f2d2bbe70739", + "0x18fbdec47501995da87d2dd2ec775542845aaa3d6c2858c893f742f7238a84f2", + "0x50cd8804592daf8df6137e84d48703babd271ea69e682dc62a66f4dcd4ee5444", + "0xc773fd19806ec722cf052b1fa3578be715781ede90782c3b172b4e6f88c6ca3f", + "0x75eb6527035827f77835402eb4751d69eec2a3fc92f6e752f9835f428444aae2", + "0x47d6bdb4a44d19295c63631aa98354d266e49124ccc91f2e1e39ccae2c4ca445", + "0xd794a72a1dd2ebd19ef153dc4e777ea89dfcde464799934b231572d81ecb11fd", + "0x8a2601b9492c2a94ab8ffd26d9ce39a826dc58911c5867e3f2b092dfd9177b60", + "0x84daf3d13f92c240113f13bd5dd82d5b3a89bc8c0dca81c6b0449629b1c804e5", + "0x9e1646f5964e18cd063414f334fbe89fc6061bfb4dba604782090059a0a16d22", + "0xd502c02f4a4bb608c0096ae1b87228ededa0933482d5f7fb4976d4ef495a240b", + "0x5201cb0b2ee7af4eade6ce531b1ef688f9aa6c0458263752289a23c4dd13087c", + "0xe427d9e4e4dc14e0fa457ad1201261ccd9e71df927c88fd649f4c4e15bed745c", + "0x0c63de7bd5e192017f23b1fc7695739a77dd7574250a2ea6b14aa02e49f2664e", + "0x7d0fe6e27cb51b2afeb026a55e59d0408f562725605c2302341e359351613edd", + "0x79d3d76bde22a4fdc8940906edb5bd481b4739c9087427ca52954b34c95dcd94", + "0xa80168e8a391df4187061a61a9185fe52f070d83f0c25fdad04b77e63bfbdd53", + "0x2f6a6842c43577596d8800b267e780621e398c7ccac427a440a432cd5de43bb6", + "0x8e39152a0aeace46fb3417a2880e5f139d498c25ee55ec2b1d6a618f1a6475fe", + "0x79b606c3c0096563fd3283f95dad5d1de225a3a8328e3cb3a2e548e3bc279fd7", + "0x35a4d85bd82615f053150d708ae8a90ea730943be97a63d30dbdc5b9396fbd67", + "0x1d1a5f0536582dff082ba363e0796be1039c4cc3bfd926882aaf51b8addf169e", + "0x897cce81a5605c20ca242db47a7a55f8a5157ee521d143a3d8f50d8e70e45e9d", + "0x1925f6ac8d899f869bb774911d9dffebe0d045a04272b8bccf1411291ade5f19", + "0xcaff6407592be55d51aa8ffc0f3c62d64e5481b7f4e36b4fc12e8a71d5fd8b06", + "0x8576a6e3ba042a557408bfd522e19efcc7409bbf0001643eba3ba6dc9072cc4d", + "0x4cf38962804c07f5f2d4d1a796ff0061e7028ae47ece4cb9f22fec95a20d1afe", + "0xde6b9ec5ed8c1f22424a92824653cc8b6a7e9ba96cf85a626fcf4e8b2f4a68c9", + "0x2701c33f7d01b005229d49cadd8aad6099169b1bf33df2b82a61a5f5362ab255", + "0x94345380075464527c3f51d3471749c4c39c7daee413039e9d66915e88465895", + "0x370fba8d878029a438d283ec74b3d1f2a303244f4172bd911aac074e6d8efc7c", + "0xc5f251d43461ce43979c0e9d620f7a39291ba644dcc5ab203f258ca7f167c52c", + "0x65c5c0b153820193c5cbe7774127cbcf9beeaa04f04529dd76b862f264e8324b", + "0xab08ad46dd097cad1ee59d7e8f3ca01861fa52daaaf2f6c15915ece176e196d8", + "0xf0db9e71f23ff4eb1e4ae878241ea215211654d690029f1ac8fa21727daadce9", + "0xa3fb88cec346ccdd3bf65447297305e0ce7bdfe379ff41c258c890db463c4d0a", + "0xc46923ae52825db2351c476c0a0d0b951a5134c9cdf087f55a196386bdcc0846", + "0x637b7631240e0a3417a21c4c7187d81014ae6e97b2bc07d62ac4c04f37a18a94", + "0x846d7c740aa940d583a8f35460e2d7b8a4ce87023d31aecc1a5343e5966d47db", + "0x4785295ceb981c812ba94fd9e67df49c6a86cde759fad022eabfd2a7ae5d68f3", + "0xf37c3c2439ad5ebf0ccdb55fcbe7cc57ddff13960a6eee99311f447f665be460", + "0x3e7c9dc76eda7de0564d8bed81fd1d9ad9d7cfd8df30c5ec39eab43930a6c943", + "0x35cf3d3d0f93e1019eec2fd022e8654f212600d422ebaaf9fa59983728dbad21", + "0x50b6199e470a31d83691c3e2d37d0aa04a2d23956cf29b1185a149d826acea05", + "0x652b3fac7129b5fcbb598df827480e13b86bc39a5a09bcaf1e21f8de49daa8cf", + "0x11d1fafd959e39aa59a6f5c9a8d3654fff4ebec2a57a4994171c6c84f9b72fcb", + "0xff843c62390cd6000211535db3754018afaf2e9788c68ad5c8dc93b0f61d2204", + "0x6af9e66f15ee1d36b9772996d08645a1ec8cfc678401fb54ec4d388d5c8cf021", + "0x6f4c16dbd1a7c604e714c3c4d38e4628f33edac9e3a5d08cb9e11e88ec798c05", + "0x68ed2daa196408a3e77ace8a3c6fc2e6c01825c7a2323d7fdcffe95adcfa2077", + "0xd3c37a1fd8c2c7df83eae0c8209cefa397e6bd329d21ae67ab5f0a272402d7ca", + "0x69dfd526d9d1e162a9bd9f8d201ccf7118f2de314fe61a2f2e05e875d92329d6", + "0x5897605aa6fca69ceae444306a3c64b7238535f871f1ca9f317292401f2cf706", + "0xac2063d3426be6514d5a761471cacf79cf97b2dea42de55b681d59c0f8058c6c", + "0x8d07a2fd01a83f9c66de64ae5d2fc4186074b9805f2370b7a2bcd71d1809fdcb", + "0x02878ebddc4bfe42203048ea5f9bc9866ac44f42f11d3e7db882e78c349b2a20", + "0x31d30f299ac177b730e458253a43b94de3f8be314230e00b4eeb0f4b97d509a1", + "0xb1735218b3b590b919c379ca10182b34ebc186d45f7443c6e934599e95cba946", + "0x1adfb07940c04836468c2d5f402cddf543a7841aa5648f359df90f448bdd9d02", + "0x802ff242add7499eb10b1d0bfc38d6348aa27531955e8bfa7050f76473c93431", + "0x8d64ce91111e729b73b3348570a9a52f1e59f2f3523c6e350715bd76a8503d67", + "0x7beca3c3110037f3fd0a5c710dc5325d48cfe8f1cc168fa23785c180f3b27349", + "0xa07c8e35d8701795d1f95d6d1341fcc11959234f21c656190053c6c712594e1d", + "0xf799b43fb0db6049417720f25564e7525b2291e89d60fb3c9e92696b27394330", + "0x6bfede2a0be1ec6bfe448a47fe78656530241e55d26217ed828adcd9ce73f633", + "0xd11b1c07c1e4d264752d774dcfc6c353346b90b9ba37b8bacbe1c98e8b1ef2df", + "0x95a93b66a56143b668a6ccb3a6b304afe71418e68b63047f6161871b53e1f718", + "0x632ed79d27e01e949da4ae7e5e4c8c1f6dfb1824375be1d6c7a05fff2d71c75a", + "0xec28a31b335f584a479e5a29cfc02ddcb698233373e265db227e7c1b900af4d9", + "0x149edc9adcf0ad9c61035cccefa4a319cd8a981a825f546a623eb4d962524a7a", + "0x49918e5ab593c23ca052d3c03f3882562037cb4d0b10d4af18087baea45763d3", + "0x01ec7d405bf593d379f680aac43277a2a98215ac8c5dfe130b3631bd899facd4", + "0xea0be7f254f085fde17f40225026f30fa876320bf897c1bd5c586a953bb11396", + "0xc57ddad85f64b9e7e79e210ff67d44a9465df102368afc03cc1da2aa20e7a3bd", + "0x9c51cc191a459962e7912f02e2433821606e1c09b1dc55ee5182be808126e227", + "0xc550f7e4e91e6f3789df455a24b646d7707663b79f4befae9f9f16fd80eb2886", + "0xfbb1efa19a2b4ee0afa4be3ef4f68836b4b852a0a5b5506946c32b08c845c13c", + "0xc81b77cb5500f3ce5de58da27ec3f642329cd5a027a6ebeff2c141f9335fa0bf", + "0xf23bd9c4a51300122c82d5642de745133db5ab08ec339747a3b6f9a97f4c3a99", + "0x1480ae0b77cc1eff88bc87cc4155026f946443fa918d2faf1ec81dd0bfaa821f", + "0xcd21d8047f18a4c892ae090841f22d4ce4f8e03ac7e1832c32121024f2237848", + "0x995c006b7e7edb074d4ca344319cb1da06c76b64e6ad6151e262f867445e01ed", + "0x8997e264dde0b9a5c5816c6fa3f8132efd10056b1f847ba72c75be04800da4ae", + "0x570c1ebf3c2d85481010425f2dc8bc62d1d6dccb9f9bd9215e87190bf66f24e3", + "0x4492e99581baad2433591e7d701ccef68df0c5389170ae8187817502771cca77", + "0x85c1b09768257978dc9c24906a590973a785513bc0316d5e964a57c09f89bbaf", + "0x9f6faad68d719e3046ca47489a49defa3a1f3d61326b956d5520e5c41c0a527a", + "0x9758351e0067980ee00af0aab0b0fc9edaf8d804f53f4c69c10acb64486b9ba9", + "0x40348f9b53bf3eee978c0cf22af5c2efeb9447b745c8d0734c458400ea300aa6", + "0x9cba9a666f519ebeeb5b9b9a20a061d0b66aacad6387f1d2643f471ed6191ff5", + "0xaef3d154125b43f06c341fe25d82170495c70b7f57670f0c4a00e54edfb3e4f4", + "0xa539f23918984be5e6ca28a99b654788aeaf42c24576a95ebedc21e4ec888dfe", + "0x69d26ebc2a2eea0738a031d849f72fc8f7de5556902657b0556c29d0b26260d1", + "0x95a9fca7bd3dfec3c57f07d950d64c03edfd35302880551375af717752f84ccd", + "0x6946a318fae47f215efdf8d4a9d742f919981b53924f907c3eba74ec25292191", + "0x751661605261a2ca529deb7a8063f2df80abd070af967addf65fb86d63d6bc3b", + "0x85df7277a7ada045fdec97ebd40606670df7e193485aef2ae941bf50e44ac858", + "0xb9e090f3baa973c05e6b5b13a1cde064300566948919f7859168dc21ed74cd30", + "0x2e179fa599f61583ca47f5fdc44faba8ab5e5838e67d73cbb83b221019d54309", + "0xe7bb76bb8788136bd5680d5302f9e4f92b519ef91b034797456783a822970548", + "0x08d2505b345e5b70737324d9ea3bf6274ad1deda98cb8c690deb51883ae49497", + "0xc4c067c8d28bd4f62b706c6454f45348eedc4f59b38cb4ac5ebeead53c89c38e", + "0x34bb696218c4ea89499d557321e8d75c3fab7cc55a294d7f9af072921f3b386a", + "0x0dee86332b8830878b9d2b4e5b8d807dbf67c64a5db2f9386b372fd29aeccbd4", + "0xcf93ad2c1b8f733978276118de6321ea9ddd057618145cc801985d8941c85749", + "0x4577d32b196ac615a33ff1cba1625485b45ee1ec72ed407d352035304076fe99", + "0x4b383a0b4ba13cf823bd176b40f670af5fbfce8c292e07252c3c265fe3cb0b3a", + "0xb5cc487bb996928513a77465a3622379e0b451a8de5e6bb13b658999e098d4d2", + "0x728a231081909105ba0f62db495b621757dd8a156a5a32d94778d99fbd0754b8", + "0xf397b88cb7e0dfd8a1a2084f83d8e35578150fbd648624fd468932ebf56969b1", + "0xece0c92afefcf9eb1d7811bbd0aa19e701677975e8b92db3e8fdde460c139e78", + "0x1e29270dbcf5f0ff06449c4579c83caa522825041f9c38bf6c1831f32543195c", + "0x2228292f2fe7f5635ac7d702a2a193801790eaff296504b3dae26516b19974cf" + ], + "state_roots": [ + "0x41442decd20c78c659fd49293ad4ab73d57a33109dc02e75c5a797370c7d16a9", + "0x5b87dca8dee2084d2b6b99c8da02bbb35619eb2bc9d2c73decb976ed7894e704", + "0xc1d9293c384a10d821e5d8517ade808e72c064875938d5c59ad7348841ee7a49", + "0xaad0f3804ae9233c553797fbbee56241c1619779927b3a87a53051661f234cbd", + "0x12d82572b234ed45fbb302b1ad7a59cbcf165e174cf69a4f7de9ac9ef56faa7c", + "0x70173bf5ffd205f01aa95de25d13107278a4e64c6d7ee1071d5b9f32c3638ba7", + "0x924aec9d1d9535f76d16631df97d48ace95ebf04a40e1364628f0584556190df", + "0x41eb7a27e3ea769af208d0b2086cccdfbebc8e2b02579bec0e8f5ad6dd64cdab", + "0xb6afbcd657a8b87f1cadf630ac253037df0765568de1f14e82f5ac89f16494dd", + "0x166e3130a4fb6f48c45e11205e6167728c993d10384e916079f5a7ad27e14923", + "0x2240cfe7ab1f679fe8268933f352f18f2cad0afd6876a58117289081c51d5de4", + "0x111570454c085c5367b876dae116674b4e6d8cee6e050632ca7a4271388089da", + "0x674f0749d7a473952598c35ba4a14099530f0ce28b47d596704717c2f01a4d59", + "0x9d4901b6fd2e83c14723f0132386334c2369ba4ca56b35440023a116fd0ec80b", + "0xb6bbf36d60635bbb50aef1011485d995061b5537812fa81950f19d564342a85f", + "0x83ebdb3c0eebe9e9dbb9b8364da0b538d4a9076f0ab4e77b01434e3501ba57ea", + "0x7629e0aa998e4654e30de3e7c52be665c323408c86a608981ea922a547be4d00", + "0x85211142a0f70205b8d7a8325e4fa06513948748372f658f15e5784c31a7e9cf", + "0x8d9bc3bc06eb56031667accc3e81967c8234c13557979f44dabbef884ae85acb", + "0x6908f0b9680422acc2ed3281b8e9e344cd9f40400a5cbfb13ce459890b41fb48", + "0xeef26a80e07867e341db4e1e261fc4235762b5272ee88deba0893af3e02fa883", + "0x47a70a454d903fcd94d8356750d0767c5828736abddf62b0f13950eafbd41a8f", + "0xc956c92d00844119c7401375c83e2e6883935e56ce886a3f93b2cfdef18ce9e2", + "0x6639a2f3c8329c7e187625d294c740623837be9d9ff45c0c6a082988f45edaab", + "0x0ace8dece6d211e891aad2c0ed3d2f6015ce721f60b8d6da18435a3e9cc7a573", + "0x7a73a8ed11d444a35e9559ecad7ca452710efc8f081cf1e228accb343ef80cbe", + "0x4a551e1476b249850654660b48392f4f1b87a594e123a5b2b9678d2207f9d561", + "0x490e2d33278875cbfb6ad15ab967eeeee6a8bae174ab511c2ca4834c43c23b87", + "0x8c72075bb7818da417944f504c60c6c7e42b62bb79b60ba26f1cd2a1c7b9b11e", + "0xe531b29c00e0e80e6211bc141bbfb1c574567f8783d4c734dd1cf42d4e415090", + "0x27b80baadbb9aa9d58f3e730f3f772fe33f4c9c4141e367fb4d9d9582aeabe4d", + "0x5fd51edec60ebc98e011abd12d439fd45d1b2a42212f47ef72231d4290ca240c", + "0xae9d58d70fb86a7867cc5e85fb71a8fda399e429f7000d44e1d2a38b62ca00ee", + "0x7e5850f604951ceb463692e94ab46df4bc4c4401301486c12f3a137d50b9adc0", + "0xaefeb70939522f0ae56949236cb11146ddbe64466d67c1d66aa2d3b54e91603d", + "0xcd054e135ce7c978807b4e21907e84bf40bc60aa057d74d14d355c4e51dd76ed", + "0x8dcc9eaaa7415ddf9697d45addd5b40038b3941744d8ce6612723ec3b279afe4", + "0x8fe804cf9a59740cd5515a6066d4cebec2817ca4afafbdbaed29c76dbffd222b", + "0xb1a2b99c06b85bdf1a1d1e6d1f2dccc7ac9b93b372b14e3497c31f2facb35f43", + "0x4239bbcc339c70f1e8f83c169b0bd4c38a0d5d74127d64b4b3f03c9c49dba014", + "0x5113e2a7ef197fef1eebf4f00884d4888b7f8591b0ab4ccba434f6fe0bb1ab17", + "0xcbd035c9f3a0b827db58de67fe6f405039fea74ee7098303991e06fdb8d8127e", + "0xf909000df7c0bcd0ea39ca4fe55e028cfd2667eb6c1230486d138f5c5a64bd0c", + "0xd9898e96cac9dc67deb950ba0a403f1fc8c1a93caa4683342478039ce24225da", + "0x253b8a05a9b32cf8965e784e1b3398321f9b3b58388555e9c80ca48605d483db", + "0xb0280a7346e4b0e39f6292bbc48c476fa286db6f8d14bdb059eb60e953dd15cd", + "0x67d71238f825f6cc3671905b0007f3a2fe2dd9c32216453d3106c916aa144d2a", + "0x9fc5de149077c80fcc784640f3b32054bd71c9e834f76b4018415a01444aa58f", + "0x630b48b845ca05daec2af6c9b0156c750cd90391f6c94471cdae6bfa6dd4d4cf", + "0x37dc94912161386a20375e71832e004e425e0a1eb2cdf2c4754d33ab888693de", + "0x867f42f33dda1591f8ee5c8dc2a8042bc5bfb3639e6aed404c3a86f6a6fe4a8c", + "0xefec826700b6eaf4d14d36b7c1dfc1402508a27607391f48bbee8902bc7bd482", + "0xfaa397d941704caeaa4945fac260a8479e68ac35334d3e0ec1ab020ac6513bc5", + "0xcd1b2c7b247c14b9e6a007bd426e2be1c48f2280e3919781e9c5809e0605f9a9", + "0xc28288ce1b5f7f71bebc8c10305b71e58aeb7ee03b59f1eeeb6e3041792d5821", + "0x8a8a3a6adfbc8669b5c49677cd6dd104c48ab17f47f9a7b2ae95abde64d49e81", + "0x499d47d3917824e386a06ee2b4d96be132b53959fb8ca00853b90f9731d6b60a", + "0x9811e162a593ebaa15cacdb38428b90ddc3a87e62ba70179272e29340db9589c", + "0x0abf7fe265b0e99bfa7b1d6d7cfe12ab7a46da076bb0af4090e19670e14bb6fd", + "0x8b77c1722df19142bf4279b2f047aee3cd9e433a2c05036014e619979202f5e6", + "0x292a467595925be4dfe043fe0ef44d55b1268aaedc8ad05848dcb3144f649dbe", + "0x5059fd32611cf282536702b160c6b69cd89119cc9dd4b60519e58acc11f7f6e5", + "0x86643a0bebc476b5f703a35d0b93e49990648dc434cce834bbb27a510387bc8f", + "0x4d75532e52d5792659e0d4622e19f75d57dbc95d171df0c2a6cb36ac5cfeb4d6", + "0x1d970594eaa209d2db88c1ff89a6ea304e10c12adaac1fd5bc515d3a992601fe", + "0x41f2514e275c0fc3f37f5c522214ebddb6556d59ac8431194d4a78ddf04edb8d", + "0x41f77b05161ff081022c0e609f0b78dd4e1eebb3779c81106234dd747773bc6c", + "0x13987a6ab3324df0a01071955cfb2cf337ec93072b459827a0df939a1962de43", + "0xd297513b8c96678de8f0efd85e73cd701b1ba6f05854badb2ed919c976ce06d2", + "0x8dff87e2dcfa03e0b6c712ea7aaa5d02801ffc717510e7f6481bb21c7ab00a85", + "0xa096cf713fc9045e711fc59a521e735915648d34097dbca9ca8be617a02d5e9b", + "0xce90c1d6f79f35e690d9c3b0dbea37dfb0103b8120279676df31540cf60cede1", + "0x2d884cdae9fb166f7d97675b5a4cb7c2ec15e4508a5a986965898943bbda1367", + "0x60be693c2a9a0e39bff69b8d700710584aa9c3448968c52202450276c722b213", + "0x17d5a83e93351a7890cf3fc887049f805693f0642bcdd6a386864ceb384c21ac", + "0x8a1648f90a5c96e84e8c5f2df2f18fe0d91c6dfcc1a65f46f70e75b13d224658", + "0x62bea7390196c5bb642bad34fdebb8722494adeedbcd2c788b8496955bbc3b78", + "0xf0dfe4708ffac1899ad2cb9d473d7821f78c028449795a32ea2ba8c872324376", + "0xf0497ec14471d708080a0a092c5632939a95b336254d205eaf44865cedfe5af5", + "0xeec9a12634cd4947985b8d747af4b4f3c515b8cc97299377bc85a798e4dce8ae", + "0xcad62ca44e66a55cb58fc9b6236e7c9495072d13019aec5f7fc2955a25a2027f", + "0xd239e6c64450d4f6c1042b3866d9016620e5b7a13c9595424cfde40f08a6cdc1", + "0x46d1bf451a5bf75e48e4d0abf152348876afbef454b5861ddfcaedb803766ffa", + "0x976718b8a202cb3ca7948f285714e8639ec5adb79ec186e7951f1a3eb987f1f6", + "0x0b6d800513565f664cdf9321063d246f305b02e883ce902768324f85e4ba0f9e", + "0x335df322ad5e4fe78caf141b6392f18079c1d6795de4a95e5735b7d20a849b4f", + "0x951d7b0f36eec3d4f59417f6a725a900c000df550b5935265bd0f341bb985fda", + "0xbc5468c9db27dffe7c2ce6e797c57c87689b1617ea8ab3c8d9b2aba3a17c060a", + "0xfd058090c63951a61f01f26869cfb30484b21d565e2ac6c502b379571b92c872", + "0xd836b5548489bf66742f783929d55a6ab8838805fb423fb8324b1dd40c0b24b7", + "0x23b9d0033372d0fe19dbfa4e5398791a8f2e98f20fc331447ed363f913e4760d", + "0xb620c0946f9ef7126fb023d47c1ee720e5a946af382fae75af84c36e44c9b846", + "0xe9b5df6ba85d5425fe88bdaacdf5a732fbdce38d76a784b83b41d7ff0b24d79e", + "0xeb8c079df4ff20856b996c0347df45ad7d9ebe44b7a365771733877a7b349d83", + "0x56b094d3a3900d192326cc81d786ec620daaa298e4465ac6f2c52b90c3813c47", + "0xe78f7e15b122642eabb3788174ef9cc7a0a4e07a240b4905e29e5dc411a57810", + "0x134f9d020b008e02e27b48304b6a1a43fcd7780273ae5b7b5ff0113575fda717", + "0xf13cce2b3cdeb19dcf4a375e8eccfed04aa4139f618a5b633cd95b8d9268a322", + "0xdfa46b9b0d9526518d53820d0eb7918f9d688455112b2a5d525ec58fd1de2cc4", + "0xde0def1a295768a4cf0b402e02b1ec002defeb285c497a91c53f203ded64936d", + "0xfdd4101c863f34830e0c03d76d2af23ddb4e50a4a59dad8e7637c1c91c3908d0", + "0x6cbb4bc67dc7b6b268d7ccc132aebb0f68d9a47c79b48e88960d199940ae4dd4", + "0xecb57513ebf62f55d807a30de485cf7bb9f3f913fc1884fc0f8fd4b385e1e32e", + "0x733b808e08ce9a423f1125afc2be812de84b3e6b2777d43b32cb5bfc0c757c3f", + "0x7b0a84a1f930b8cf509e5a1b07091dac1a6ed244873bd9c960189b332e827185", + "0x6919e8697fc370e76658593daae34a83c393ce5e718c27dfedf6dbe9611a3b6f", + "0x7e3733a7f562d1c0639f10d2b99fb26caddf5e7ff226ffe2830fb3d3bb70110a", + "0xa93cd3c240110b6fe8ebbbfa47424b62beb913b5b81ab41ef0be376aaeb3508f", + "0xf63e984ca6a4c2c209f33d612459d89837a023010fa051f3e289ef16f8a7e1d7", + "0x5c5b6e6be78305bda99a9d08a4b5e0148f28f0f65c9cd6a3e049249e18eaba76", + "0xfce1a601b016673d0e11c27cf7898d96acc6b38e37c5d1c7a6ec45f661d705f0", + "0x03a6c158104afbe9c924ea45973071026039776b6c88d3495b94f166ae2a4bb9", + "0x98a7884f6b2b59ce725f05bd6c14b6ec735ba5ca79c151880eedc870e5d354e6", + "0xad6d0c7a5b9596dbeb42c3fe87bc1b855a2294ab6f1e1916d65efd2d595e0c71", + "0xd1c2769e5a827f46efec68d4a9fd619c785a4215825ff79bb44d846d2f0aa474", + "0x3a0dae33f1f67b30a7871847768e97ab9e90c2a729df062e97ca4b25207453b7", + "0x90521e73043f7cd5c6c7dd146c467fd495c6b699653693f371ad420cd48817e9", + "0x4292862d26d566e1e5b5237aaa3445ceb7a802445b9aa1ee9c50fd3933502f4a", + "0x5fb102ee8b35bd2b25b64725fda596ffc35425d834bb53733c49d85be8e2f019", + "0xc8835a48bac438c21ac265b53b8c2158208ebe9d33bbe360ca36a936e0906b74", + "0xcdd590d89310e9965dac7f45e6e816480a6cd79632e62cb00167c801670c1654", + "0xaa60f3f7deea145dba398a900e66f6d650af6c74d71f8d9a9f56f8cbbe647594", + "0x767520fe02ce75ffd31afce3343f40b7623a1464ba6d87277b8c22dc0ffdc577", + "0x112c1259985e6a7cafe582c40ae42f216a1a2a06f4de76f2b60d8087a4b25642", + "0x99220d3cd68d6154bcda19854ed3efa2c3617d73214bfdaef9aea1122a5b7fbc", + "0x6fee0b4bc3657ab8ef8783787479bfe7717e1def6d5bc978ffa85baaf8ba1062", + "0x66f7cf480cfccf2bc393d26909fa121e278ba6c91e8d2c7f4ddc8423d27df762", + "0xef2d53349c4640538c289958969e76a7fba472c366a9e55bf06b90518cb27acd", + "0xa8f5924a3e15b29c4d10a4f6234391a496c05a8302ba2b1bb0afc4af72d766c0", + "0x412a350536a419c29c2caff56050d24a812c84f4dd3c2518e39ea39aac4b43f8", + "0x89d5057a7458c198ef8fb63a9bed3188dfc4187578f48477f71710fd0b1a6e56", + "0x4c5aa0d2f20bd0fc17bc9c447e1ace27cd44f6ab3a4a42ca479f22d6709415c0", + "0x098aba83e141381edcce5a7be5dd898c7aca4494897bf55768c9583a4333213b", + "0x5d9102c4a7efb253527f0bfda1f6f70e9e052db6a942bfe1ae692984fbb62265", + "0xbccfc63720fb5624263f6148d59ec17bada569e0355bb06b8361cd56a2416911", + "0x53bbe9320dd9301d5526f3b446dc6f6a2ee74ac49d2f8595ec4b0fc435c467e7", + "0xa999e2138c164672b7f1237ce31ae895491910d1099015f3adfde67e5dd1905c", + "0xb05c5a51dec78e4c2f2478b626a3de91790859a700177ddf74b56a4f866a990b", + "0x1e5da12a36bd2997dfd03b5704a3580e7140945411803e31002e0875151597da", + "0x1d239781f2b47bc22a850a175876c940c4b441f5361498b444b3000ab84d5286", + "0x732f778041acb1009b84b9e5e6f7e3cdf589fc7aa0499da9c3747d89516955bd", + "0xd8567603b2326786c092d568c097e47a847e104df8c2c71ae7fdac751e3f86b9", + "0x3e3d5fbb730394c654720c41615f89e3b0fee1b568fa47419ff672a1f93b835c", + "0x28ddd24837bf12b207bef3b2a8628ffb469eb0ce6acaf88f0608d8acd561292d", + "0x1e3912f9b6f91c0f5b9f99a2830d525b85c7977627763b9f8cfa23e705f607a1", + "0x9b926a8f1b574542692652191a3b0444840496719fad6a3416f58fab6422a8b6", + "0x75e8fb35227e567db1b6e2fc75d24bdb5a38f186cd73bc92b85ecfdbd2d56bbf", + "0x2788449ae8dc72d306fa17fb2c00cb57d6d17a1b386de7b5e1bafb2f7c975fac", + "0xecbe9cd37f077fb10e219ef7fe5ef07f1f46ad4b9802c24e997f2b777207550f", + "0x6418e47e95ea39343ab5d3349e501c2f7b738665a49dbb644430deed46e702e8", + "0x313924fe7ff444beeb4e1483edfb291b65bc7380e46cec7defce021770dda410", + "0x755e6f050abd9fa871a8f383b0c41a1327947463e6bcf33e843564b49db540f0", + "0xd54188c7b3c43b29b0e7e6d253b9a3867e0bd40b2cf3ab444bb60d50d8303371", + "0xf14e2aae2f64ec9e5bacf6f3d76e4d269e3d771b3caf932eef693a1aa89dbb54", + "0xdcd9e96116cbec5ded21ca4bc4e08d3d5dc425b77501c581c8f2a596b347035b", + "0xf59d1c77cd0bc30bee14adaa7599c5e723254b127d37d713430cd9f4d4aa1e09", + "0x47e9b244fbb86cdc1e6a44f7fa4c64eea8b3b808ea4637ad4f4b6fb2ae77310d", + "0xd1287bc6037961169d0b9905f12afc2c8372109257757a1ddb6e1c6f9fe3fda3", + "0xa255bb7080264277a42b8fe6a3fd7afda1412a47d31a96f251b34b2186bd348f", + "0x00e23622f66e76a8857116bdb4e5773275a0bcfc1998f7ed0db7d40bd800e7eb", + "0x562f1771dc2510c8e70dd53109cf8d79d284b3066573cadf1b8ddf2d378864c7", + "0x7e26c9359a1521b4f13c2ab1bc67df93a588d6e09e60a4eeeecb2cbf04834283", + "0xb7ba212f137a1e5badd1ebbcf27e12190a698a7be497ce91b50ea17720dc51ea", + "0xb86ffdc72c52f578f35d66f48f00777218b4ce72ac1eb208eeeca8a5d372013e", + "0x9ad6b9ef6312592f7292a6814d18f2089793434857176db0b8cc616006108ebf", + "0x4cff165a0567ad6d817888678442c8e146282758764253a365b47de9997aba24", + "0x61ebf3597b90d48a8102b45e02f2e01953f738c0d1bf6304a20a19e1cb7ca5ca", + "0x5271c1fcd30d26529f474258f4dcf28d067e9e98beeacef7c7cc01cad8ecac89", + "0x281463fad567c18b827e72756bfb22c5c8c258b7a3a101b8ae02c714f26e8042", + "0xfb8392ca83da715d64b057c7a513abed5bad89574d0d5663a33d2d48dd3aa98d", + "0xd5361cd7ebf34fb6f2176c4121699d5c2128af432c0bf6fac074b9f0183df184", + "0xe9211ce12548f5c7d8013c27bad70c198ba7ac41220e16f7c2155707bd3839bf", + "0x42482381d18616f89180b14a531a0db971c4d52958c4ee25b5b10b2f569f8fec", + "0x1031af5f3187b6c9133030c57301bd129664b11648100ace963c1f6b933fd7ef", + "0x7068e4aaf9a647cc275e0ccb87edb0bfb126ef58410f1ba7b3c1a8248fe841c0", + "0x4e202408aaa1c3c4e98412e4f938351b88bfbc2be6b0b00ea53d90b36d1df59b", + "0x453713fa7b50b01c39eb42743ae25cb53e0ade572f2f00c83e61599fae7ea1c0", + "0xa0f1ce4622ce5d163adb3235f5a7e81cfc1d17b5c89485e8abfba6d576f847c6", + "0x084402a9b65cb822dc9a8829d0dbd1781320799fc3084ca8cb7e3e29a76f6904", + "0x5dc276cba355b7cceb862947ab85533767e3b563fca521dffc83b1918ceb348c", + "0xfa483919334cec715ce17c940067b9cb5a08cad336cc5a38300d5584e79ad3a1", + "0x7fa76a87b3090ed705367e843b10f3bfb14a2a41369f50aefe664b414537df2b", + "0x24f6cb2cf4475014c7bba984b0d538c2bffa83865095e9ad9abc030163cd4108", + "0x80d808cba836a33f001e5ae1910cb65220b9effcf9c8fa7cfe433c8bf43cdb1b", + "0xa03b886db553f4af1026e0a8aaeab3d254fb475f736b4aed04d6d38ee4e02d58", + "0x251be11a15f9e111ac18d18f8ddc410036da9b35f3aac988c5037bad2ff9d284", + "0x4fee854f20d70191beb0d821d0acdb0ec8135bea5d0b9fe5631ce80207bfd655", + "0xd9cb46c77892e37726f6623d553bbf789ec805a42f7e13bd962bdc6fe5dca2c4", + "0xf83f2f40cc25e4eaec2db3caf23c789bc6c7fbb7be303f3283340a3d2d894c06", + "0x63357ca51aca29465284e2d4e4372089f785a6d2c811b491ac0545a15cf35a49", + "0x1a50c560affdcd97535f1de1b98361a2b097d5eb91ce910691a9cd03cab3d30d", + "0x126c61afc9c3920a8876f14c6780a6e3d6472447831c1a591d6d8c65b30ca0d4", + "0xd0d195cd149f01b36fcaf7cbcb99fabdd387d9be48052f657ad88e8bd1e11a68", + "0x97838267eeaf81c14235b9ebe29bb4faf2e2a4c1e16415f768a0257cab8d8800", + "0xc94aa02d444be058260bd4cc4fbafc2fed6fc8177edc8a6639d0741784519e88", + "0xa3bd2d340f779eb695788ad7781ca5d9a1737f1f482e05c6afb8c159acec0293", + "0x80b098777639fd42789d8881ecf8111ccd83f4e0eaba5e7365c65f43a5d9a1c6", + "0xe4c76b7daaddc9faf44f041f7f16d5484a558ff897d87b3d5d4c776c4f573208", + "0xdd5329351350ba4fa7e835a73a5685a26d2cada40b435bc3e36119f4a5c67ffc", + "0xed9ef8d98cc5b760ee767bb52346bd9bff5c7cafe882988257833bc4a08844ef", + "0x3aa468152abb67ee3c7b3f8086ee2ab84e1893821f4e207a1f115747cc000d55", + "0xb57a8d9ec270549351175102c0467a586ab9df65c46e004f2568669769026659", + "0xd8d4d15feeda7027e9fdd2c803a2ce859782a684c0b579bc9ce337f79a67c1ce", + "0x842c97e8d06f6eba0c8ad100733f969116ce43efd290d00199c95b547932454c", + "0x98af00448bbde52f56e9eddb8c812e13715ded688a420e8b1629fca34b5e8469", + "0x46364fb4d08afe504ca9833cf2b3153b0e813b16a95b5f26af7f2f652d0b9e5e", + "0xed62046ba653691249ff0f7bcab9be4a4acb867e4f7d315a33ea3b491f0ac020", + "0xca8f2d62cda668d290a0612358604311636a7dd52a1c8a6d9fbf04bb80c0886f", + "0x673d1aa9c4cca36e023c49769af580b8b5fe3990bf8f5a158d246d7b709c5c86", + "0xcfc33d452964b3f18ab450b27a2eb67c08a394f546c6231440bd8835891f9aca", + "0x72ee578af9b708d3ee163f24e1980c9aafcb9929168e35f3ef1aa27623ebd217", + "0xc93287d4a35c2fd5c52241da0a0648927c6761a4598de69500096f05de67f286", + "0xac2540f1e37e122c09656a3c07eab9fe3c0cc81dc5c9c86589cdb89ae48922e2", + "0x8ec5b63a7d8aa4ecd9b622b237cf221edaf339ef7b05b92710f2d42a5e597c40", + "0x00e257e8b85b510b1bd871b64ea0f403e083283d64de15a89f2b2142eec25c17", + "0xeef3047be2d7a148bbe245c53f65e26f5aa661d8fcb1a1af44123f3c20944bc6", + "0x46b1cb137684523f2abc0002fbfbe12db24b785b03494ef3529840e4d0fd8e40", + "0x40275811bac515086576390b1e82ec7227244ccb587e15b9e08594d04ced63f7", + "0xc9dcc4f57056b131d55bb6da88b9b35e5a620d27552f06bb54ab1bfc903be541", + "0x7e8fe532b6a74a10a71dcc31ad52eddc67cf27197f59af082a03f0067a89ab69", + "0xca8f6a0ffe288d377d5f08a10c268246338f916448c1e510cccba7e505aba8d4", + "0x8311bc2e37771ae3f1637560da4854824448532745770fa708b4041d620af54c", + "0x40e0c8c5baac3b960eccd741ce4bb1ca82f7d202cb93318d0c161d876550b7dc", + "0xd37e6792cb5e20580a983be8e9058c3447ab6478692a275185fad71467d2b01e", + "0x4cde84ad355d59ef78565bdce32ef71904a5a994a16046fe345da08886dc4d45", + "0x256c6e6cd3ea860eca3bcc4a098d26692432aaaaae80f62ad23ace2534b1ef1a", + "0x7a7edc0d2650bb3a3fe3d5b7c072333cc1e98cf0e6897a1af994ab367ce942c3", + "0xb67f27848f68eed9ecc533de5bcb43be1ac3260d94fdb45a0d34c6c511284b6a", + "0xb7c818254a0f852d95077bcd2ca246ed80c44a8a3c43e3c14f1d5815eba1fcea", + "0xf1da8bf3b578709d992f342e19626acc619c02d40ef3e4521b02f08377b0cd94", + "0xb18365c07e7b9653b62c94866dffa2f60c750c9390c9c2441b85158234da05ab", + "0x356ef84a9d099dcab1f403e1a840c66be00644911caabc9219f5cb0a84d7e5f0", + "0x4c9eb41d7107a2dfa047bf88da3fd7b9746915cb598c4c7e276c6b749060e782", + "0xad4e2510406b2407a06c329297c3b98981d8fe5543a796fe0d40f1e2a91ddbb4", + "0xfe0635876722fe0de74dc7b8d3f2babb4c0dd3b8de74534cccab8c58867dc14f", + "0x2b6c44c328917ba04657d3ee2906ca912d8c5ecbaac328135f1a40311ce8475a", + "0x38033af153bd3cf901ce85335490a12e4fb95148bee5977f7308aa4c392c578a", + "0x5c8533e6e795ca5fdec8cc1b593d20e36b4aa82bf664b30baa72de0971c11b20", + "0x183b5c569ab8feb63550dbb76336904508c7e5b2cc655d28fe1423fa28f3ab56", + "0x2307f6c1588d1ce59bd773fd17e948730d38b5806ad60e2485ec1ca1ce148f8c", + "0xcd9625d0b2793e47593392ff7a7cc8081656848ffd73b680c0750e607aaad957", + "0xad2abd6f00c6aeef182258da8b2f5bb3ba03599ad595fb9c5466050f26fc74af", + "0xbd9409dde7d80a4ba6be0eda2c5cd8edd268c026fa3a5364c11a09de10fe4db7", + "0xb4673b37a40f319c40d43364ec9b40579f10ee277ea257678fbfd56d9d0967e1", + "0xbfc57023d48d51de6e8002dae4a121c77211a1ab325611aaca2100ac307efb09", + "0x923d5d7eedb4734779fe7c02fe87b953f71df4c21e563a23b32265516de08b1a", + "0x9881bea9f345e6eb9d5775fcc54e3877d2b1443b8087be8c66919800deed4162", + "0x6ce781be1844c74e824d75bd117acc152fe5020ce670b2c64bca0185cc2ed5e6", + "0x4e58d629557567ba016c5f05f3661a9e7601117ac22fada67c7c600fe63009fc", + "0xabd927e268c92f8132aca8133b438a01a44222e43bf0c0ddba0c8f65527b4de7", + "0x21db7de9af6421b64015a4a595a2c7b0a3d0efc37cd32aa99b6463e70c066021", + "0x5f016d3eb32a2b9428a2e0ac2b08fe0b5c13f3c1d711ac679e1f588e1c7116d7", + "0x8f8f88e7bae996487eeb396511daa124aa6b979b9d26b6d7dca827ac11d13d74", + "0x7ff478394df062e9c4fe81f3bf485953392304aa5e55cdc34a07a09a82cf8430", + "0x367b69ccd2e2b7d2d74dc839704164cbf4513b56bf624cef311e0944ba585020", + "0x5860cf329d15f20631d78658e9439fd910f30c60a43ae1fed28af2da95e4b87b", + "0x42ee324507b298e9b7afdeac53f967e3e9784b6a38f4ce54f623448bc7f30b33", + "0x89b85cc99844534b149d07fb5c0897539db13f36aa9f2cce1e5674dffcd3d28f", + "0x0633d01596d21713217a8f7efc9bd8a714945be7bbd7f798db2b931caa3240f3", + "0x86fbea070876a0919ac4231ac89da2078d088d2ab5fb63debedb6033aa139123", + "0x9a356411d6a65504b8d62d8249675daa43b79c9051c1f1705d4e53e079217f6b", + "0x5b1e257261317a1331c787be1cb078a620e7f10bb9b77e49bdc77bcd51fc3682", + "0x042eb6a7ef78437b013e4a91d31db3ab11be15297107735eaeeebc6028c0c6ce", + "0x3adb0b45bf63e86cefb142546755aee1b63bb08dfc6d91de1b2679b48dc682cb", + "0x2b1bd683b54620a0559c5d23b18ded646f85e3b994bdb8a1926225790f744858", + "0x4800a998f2ede85f904144009b808a73b98f2f385f9b1616fe6ab1e8520b5a48", + "0xb4abecca0bc3d32621f75cd4f2302c472cbabc2e91c51d818103a4b1c9f9e811", + "0xb8dba2ee4a5cfd7a97c67462ae03a9f201201a7cfe1309f1295e99b6ed814e51", + "0x70f26a6cf347124c515abc946503374a399d2d020b1c927d10fe2018b75e3176", + "0x52c6f4ba1be42cd47e9796cc3ec558e59329edf4b4692cf7fc1865ca391df19e", + "0x5e7cd45f758b7d7ca55ed203265b97bf6038159a24838a68fa9e19b35e508590", + "0xe7c4a2898c3c64c111b93a515d0f9e180460226bb10c65046c1adf482a8f3001", + "0xeb091a1aade965e0cd8947237bc682f07fc62ee27833693ea2858ee4ade6021a", + "0x1f19e5154c27ab21daf6562572cb91eb573ed5f045f1bc0c10f0f212fc9a91b9", + "0x177b77d8739d41f3d3ba756d9f71ec852fda4af4ca9d985dbad79eba15697501", + "0x79c6769a8d7e648d0707a751b9ea432428eeb94557f15a8045066a0202271648", + "0xf84aa1668d833126238fd02065b137c1a171963f180df4ea39466d4b1fbd430e", + "0x3013004719bc9bd73c2727b94ce40cc48b3bbbe116c40dbbf29963284327c9be", + "0x28198ebced059d8d3ba073f9f9484ee867e272f36bbc6331684f983a9bd66df7", + "0xf4b22d37f4b392da27f99c6ad71eeda971568a69b56eae2640161d3f95c0eaa2", + "0xe1b0ca6ca2f664d416de6d88423ec30a99502449daf610a44da8b8e1b9b02750", + "0xd91cbf6d590b1edfce055f2b3a9e9db30fb28295a543f7241d88a86a1b367478", + "0xba7d675600d999e444a684018a031e8689bd72c976d982e7faa11b3d1ba79b36", + "0x61843ad40d30704d5e8401b7d06b62bda1e93d18efc7efc1e12cdbced0bdb523", + "0xb4b8838de7034185179a199fee6da9528a5ed3a71c767714d37fdea900ca99e7", + "0xf04e35d0879e51d13ce8a3c5a3f80d23e3027a5bb0d99a241032be9a2cdd655f", + "0xecaf350534b113d6081c3fe46102ce1f5b4a7d2351ca36e3215b319c16dce13c", + "0x45e6a0fb0203622b25b082d624d89b303bf0e5cbd58371dcaec839c0f812c461", + "0xb38f3e7a26ab4b69fb5a2c13484d191bbb04c9ed3012144cfd9e8514566fd9a5", + "0xae323e9262e23b5857a8d4add978af283483db83384d6f2fc436ca842d8d0ec7", + "0xd050372f4824183912a407bf70c908369b3fe1265566a8f11e5c9234644b110d", + "0x851eac0c2383806ff2b51718e49d8a45001b874cfb6db5817a47d53ede9d932a", + "0x53f508896244623fc5779d980420853899d4706efd37be41e0e8fc329e0af4cd", + "0xef57667c0ff2504671c911c2438ffc1c114199d1ce62198d6c320b2b3ae34ecd", + "0x842c503b0f023a6af7182592629aa09445caf5e5631a5c06805782a6261e9247", + "0x658ac1a8c8f9ee374817a8c5ba6811ade1ce66175061e7284a53246358ad6372", + "0xf98582f5348512c0972eb64fec3a6aa9619d7f0735c2b4c0dae4fad1697fd9ea", + "0x0fb3efcb4697ced970507a03257fda29240b3bff97aa76d624a6d94aedd28077", + "0x63f78ff3bf2577056f789d5e38b5bc7271f200c29e7b460ca152f2700e73bd67", + "0x3ba7d81ae4c98f0ce84ad6bec5f14a319dcbcf4e2546197901d02ededb56c8b0", + "0x0745d91e925715dca5a43ef75e1fa74525dd58d994b70c134f358e82550030d6", + "0x55a488aba9fb4495fb4e058e03e6c15275aa980cee20dad58e3578186fcdb794", + "0x3da02eee2a21b4665e1593bd38cf325a0ee5c271a2feb541d242717ca4182575", + "0x6ad7894c8408a204533c1ce8ca79ae1e26481ea54ed944c1494d4be2f0018c32", + "0x11999dd348447a7331c5595afccfffe6f0b02b40851ce120863b8b4ce88fcd09", + "0x5c3ad3300db2fd8dfbb7a4277828a9d70f0d465dcace6c7743efb4a7888ca00d", + "0xc5a81e8c0f71619d2016f1925db16bf6016a04e9ce7f0c1628af829ddd11d749", + "0xa5a4cab82acf8c24355d67108d5afad9c9ba1100f723fe8e33fbfa6e0106243e", + "0x92c83091dd622cab814e45d9816b7862f1712e29ec23f7b658e7e1279cbec785", + "0x0c653abf4dd1939dcaacfe13a49af866ebce5c47b1683b7c60756d401356e688", + "0x90466fd397a4a9c3fd671bfd41e54926d3148281633fbacf14d081b05f897223", + "0x9d6c210129a88e9a99213f5a610eb817ae6ea99528af1881d9e87219278bf872", + "0x1b2f32aed1774013893ddc1a007fc4891b5e6b54c748148f050f16a31409550b", + "0x09bd0cf4891f91e58a2824e0bc44f3688b70a84bce5ecef562807d65984fa95c", + "0x00bb46ed370556d9d4520ec8a7b858e4da8a8f4f61493e3e0ab596572a86e294", + "0x6e3b0c486d22d08391d76f3616b31db1ab0454b7cb09486a6aef09d3547efaba", + "0x5b6481bdc116f786a567e203a4cdf1215a2cdc92f9fc215f3c3adc6c44ad82c1", + "0x1cbbc5617850f6d6763437ef6508bed4edd67a403ba44a9fb6aebe1193bc691e", + "0xc3927e1333eda3b46de78e0769ea3dd7e8eca52b060b379007cf39ceddca437b", + "0x52899bfad50e44c71c8520d90038d9ab73b760fdc66158a36622e9218be85a49", + "0xa5e95f661d485bfed080abffd8fc54582f6afdd8421f0ad1ef8973de434c1457", + "0x9c96cbaaae4553f354d54b48b7908347fd749593325ff5cc4c19e97038197ec1", + "0x1e27e2e4d0aac6f2a12d9bb08cdc875c07bc31d544b4f14524006653d3538803", + "0xc3ede049070b750cd84d5a3b9a3e6bd1b2b5ff6732cd899f919cb65211a81780", + "0x95793164c3d5d06a897fb0a4f2e01745278f2d80cae348f47f155c2aee3dabb2", + "0xda60b422ecd637fb787a79366d2574cd186b70f1ca2c1a2a03191625509c7469", + "0xa1b8375f046d575f4b29dc5326db9794aecefcb58e7ae3fe2802752950df93f2", + "0xd4445b9251649bde8c72e8052ab107be0ed7c82eec07e830a5ee8324d23f6fc6", + "0x3244f5fb637be9e1e91e1d3b2bd923b79f00c9902588e4b78e3746935594d563", + "0x76d577ed07e65ad8580cb23e245aecf66d36a763aa3f0558552a178362402d68", + "0x09e3da560d5b3a8e1e2142b41eef1b76ff6c7450290f100df79d869ced620532", + "0xb1f9382230db91375b539f2ddfdd9da58d5ab9744a43a0733d65af43e77f16c2", + "0x68f3ea407b2528717f1beed51fa80b02ab6a7f147f284f9a3894f9c94ff7b186", + "0xd0b79e9810d2743c6bab01a25f260dd1943b7873d3a2be08e6df2933467b53f5", + "0xf7afb6c3b728f811f23e22c974d258d8011a11586c2ee122f60c321dbac4e279", + "0x2dc70694e818af492457700a51f73822ce99473c4db6ddbcfc88470b2c6a5e6d", + "0xb0057cb77c4cc12e11f286ef37973f3712641e02a41423e7d8d3dde3c512f949", + "0xb22fe8b1dbdca8a9f40fd3176a11f3c93676b1c08e0f65e91c8fc96e29ad9d45", + "0x3757f7f3129031bd9745ff468d2eafa469dd899b90178f9bc624e666c5713460", + "0x6fd1fa30cca98ffc0db6faf7ef11c854d32fa17d343d45205a88a120c0aba6c8", + "0x0f85501e18da1cf36abd3b02bf60521ddb825d93cd9562c2a3a30bc86a584c60", + "0xcfffe3b02af51fbf83d5a56139bc83841fcf0874ef3f1dbe049881fa9075de47", + "0x0f18443ff30771f8e47a584606e042a1f467618d7d91d559208eecf6257d0136", + "0x0ac5da76d63cc6e57b6fde92819f578accfe22e25657caf8a785fbbc3ba67145", + "0x632f74d1f04af66b300d24b4ceb44938e76d37cb4f6de1c4fc0f5ab61203edb7", + "0x0e83ef6a8b83ce62e7f007970fbed8d409adf9439f1fc7f6a87fc9f70d02388f", + "0x82e3063e26e13a47865f4c0983d32c62a2c16d5304b3711b490ba44e71d88d2a", + "0xd7e39e408bfacd4ab89e940636a85d36eaea3c1ad1638fb20c8c0d534f0d4a84", + "0x39324c33625b49289681986efb4a7207c2105c1451a44753aa3711c7d10ffbe8", + "0xaefbd20898e8c8a51f5438efeefdf3c81a4db74b46f4c8225a8603e681e3a0e0", + "0x9b2109fde180f35fd2754f508b89d8d5b79c5d82e1861275f0ed4932a8270650", + "0xe415a2abc68ca3a5149ec432f64e2d1f9af6af7a1f2116bfc406b948cfc213c2", + "0x039574fdbb3ccec32500f376416ee834b7a44ddfbdeeb7265cedfc4d9f92a802", + "0x4b91d8729145f884725f040aeac949b8e219f4ab335ef9e544e9047e625f6e0f", + "0x6a080d6db7af339e0f87f814bc17a72e5343656ab6ca6b837b5be1b035b4380f", + "0xef87ab3e8aa2f0430ea0258956a65735c4cd4fa86abf041128686d67fa1b376e", + "0xc7dbfa50c98d03bd7bb271431ef2e1237cf85963ce1605b6c97c6c5ca85bd153", + "0x1a6911c75dc8e512dee7a166807e5c1cf6e09b6f59ab4ba4f91ed7ed2d8f8c0a", + "0xdfa6730aeb0358d0ec719899f31ac1832d1242bc358631bc981476f04943c36f", + "0x1300ab63a5d59c580b45067cfd2c0109137ae713a0f1d7633da56a4adfdb54c9", + "0x3c6d3c4ae99a9f75527950ee0d69a537e84f262ee04818c1d03c3fb6ce025d7f", + "0x34cddc4820ff372c5c031b0cb7c13b37274a545186921561209a6874f46d5b14", + "0x3ad6f4174122fe0f0d1179207b099b423ba15afc1b2a2df63da00d69a8c64108", + "0xb80004eb86b402da61743522f1e58937bafe4b801878fc491f7b29f06d347b23", + "0xddf023fa85f86625cf0d1cb9ade4609c7662c99fec994a25869d66126f1ef7c8", + "0xfb7f16b1cb3c9a61b0a7d20a9f5fd6df490fae2160d910e16fc0ee9ef368fc6d", + "0x0a577ed6832c2016de33ea8c49d21ed929cbc83ad4b95e6b01cbb40216593800", + "0xd2abdf0260e4e32c4a0d8d3c05d518fc09ae4fd008091155c01f2280def60e00", + "0xd93e71bdb11e546de5628e403d6e820a1f57afbb5a46854a3093eb30b9d34034", + "0x665ff2321c3ea218bf66988b4875977da9acf3fe4fa2715e88818875f2308efd", + "0x6a3311e0c7b4d0632a6f1b4c623da55e98fa666947f8d7f50e9fe2ee91375bc3", + "0x380a0d80649d5cba42e75175997833af9d657f4689e135075ae3fd3fc15c8b18", + "0x305653b011c08a81b7c7fc5eeac82796720939a06c38f4e265ed2686d6c0b1f4", + "0xd5387cf9dc2c70f847376a83d585e2af02cfd93197db3e462bdf12ed2a316777", + "0xeaadc76dbc9b94f2c11c5687f7328e7e5246d814aabd26b1fe1a0acc325da1c8", + "0x534e3b92d51b4e932136f562a42848145b04bcff2a6370d4b1c0b3acd70dd7f8", + "0x5b96907198380add4781394ccbda4114311c3a1ad81ad9e4d992c1d9d3e4f612", + "0xed24090d755fa6e9a6175d49f41b4302f4f1753297c75974304209fa21aa964c", + "0x6873e77830497f31c4509a45056f87d2b60a61dbe18f32309ff1ac6cb3b6ea82", + "0x8e461b514a7c58d747de86d856c8ccdaf6f0e97bcb010b66784294aaa68207da", + "0x1bd8b3a8f401d50bea67ab798b82bb38a1d4ae60bed336e1e8d93886d5df61bb", + "0x22cd748315ab7229091ef8422275072b0d06b7c222dfa3f0b7e9def936ef60b0", + "0xf949e6166d38e12ab76b0f9d3e61d77bad57bca01367123f2c60953ae4bcb1ee", + "0xd65c7777bc82fdbf2b0a174b5995d73d923ff2925b781b674f0812a50af6e482", + "0xbdff04ae0ca130b8272c04671bec70ce47822766d25e3b5e4b861d9798c130e3", + "0xb725ab514fe0f191403c0b7e72b8534ac113b58603b533fd9925f287c364c7e4", + "0x71e335bab0f7b8dbdb366f9f3e7a4fca9e883b70bfbfd0b2ff991c5f03aff38a", + "0x5713730d6850fff0aebbd3ac5b0d290ab5f0deff7977e0e0dcf1778939f4459a", + "0x27fc7bfbfc4a17d40dbd53f7cb30fe2b788933be3118e62c5946f16889deaf35", + "0x66b0233e68a1053db5020649cbaf4c679f1b7ea101b8b08e019242e32fba3685", + "0xb31015a4026efb028b56deaf60ffb8174ef5fb2c43b4306c5f18e8720316c88c", + "0x65d9c75b529ee8a7cda1af975e48d952eb01c982113cd8b9285642e88829b88d", + "0x830c18e7415585e5603577a0d21cfab0c74eb5338e063eb43c2964d7589d387a", + "0x114c855f65a70d45972d8d516765f01152bb45d5b4324d560dc2c6cbf6f73ea1", + "0x0db16aded9f5f1ebabc3646418c35a0408652741780ef7a057aa9cee2d671967", + "0x59523aaa070af482e7cc3553cde51980c4306fb50e322d171f7e5bdb599b333e", + "0xada529f4ba81151a7e319f12faf368bb78bc290bb45c5bb232f275cf487d2897", + "0x6cfa3d80627813f8336f1bd5828323e2cf083d53f140cfb6439d88fa3a185d28", + "0xfb785247f6074d92c99810953260088325b6e1aa2daa86d07e1c2e318a37025d", + "0xd6015ac5bc36ff2ca87382c722f4f4265a3e6394a1ebc7d545739af258720e69", + "0x170498efbfd80130dec50b3ebd2620baa5413be64a2ccb7925182556812ca1aa", + "0x32e116e6c501b9ba314e2d1abf30d5896831046355f61e4d39ed4b27e87d4784", + "0x442da7907cf96899129222e785c81e367eb948294c8b6c1e6bfd58e1024d4f7f", + "0x39c9a51b29bdb0fd33970cbeebf56c235a52b4ad7641e5251761a3c18a7d34bb", + "0x874dbd33817f34a7101e0f10633212416dcbf10f71039460f348e416a081d357", + "0x3d0577d2a88d1daacad8db27bba734b234938aaadb0794e45d125e1b5d9f41d4", + "0xc85a176bc16154ac44349ea95ccb40efae687f37d3a3816908b5f1f5d3f5936f", + "0x7c8d4fa2d466ad089eb75defa4b60a6aa044ace10abee6237ac50acce309860b", + "0xde335ef692d9dae27d02e4ed913c7bf1373cd0435add823efb759d9aeec59f8f", + "0x5eb169a9390cfe29b2a4ee6fa48e27138a6a2517bea098fde54333d999f182e0", + "0x9cc49de71de6a3e1c10c2a8503cd369a18a707b30528baf58149cb28d6276ef2", + "0x726a6383ed5a8d6467f38a135c08b9152f20506cc9f407d529f1cdd0c731ecad", + "0x0245de2bd2b2e3df26e81845262fedf0d61817c5ef353cee5fc06d64c2175bb8", + "0x344d352ca2ffbd7a6dec0b8b4398e84b4becce6795de47106ca74e305850f4b1", + "0xb0242bc5ebc2eb48e5a89d776ad32f261b81a506ecdbc4bf19ad77d7074b4165", + "0xe33ebcce2cdfbec69a6f4ea4925741661c82deceee440b747a0550770afdb825", + "0x2624398c3bd5b0eda96ec0fa1fea3eed1644db19ad88a9a4f45c16e2e5d582ab", + "0x00e6d453b7b0200a5cb199cb66356a4b1639eac7e1883f48cc4555c4f816362d", + "0xda0ae2e6a1b7fb59fd566c6b47934991d0fd8aa05224b8ab02fe312434ae9dbb", + "0x3aac26d759bd84ccca336267fe41b69cc962e6f2105a1bb41105d49bf0c610d1", + "0xb5877628e5df03aab7b22264d117b02938d886c98e252d00aee87f5d2f950083", + "0x22c33e937a810f67ef2e4a3ebc61bdc53a09cf9b3b597231530a06b2a9db12f0", + "0xee2c03a7832aeb1a99ebf1ccad9226135cb27bad61fa10f145807ed5f6622314", + "0xa8073aebd00528d16bb017ae960b73b85a0bd6968ebff182f036aec7b07d728c", + "0xa2eea90343a4d98608a18541596e20da36137545109ae88e491ab38e79e02f65", + "0xb9f1bf9a8f2615ce16517c9d3676b75f1b2f1cd189564f77e73d0dbf85f91c6d", + "0xe3a938ca18177ddcf45943519d715d1fb78676bc66863d2d0d52171c77e63a7b", + "0xcff47cc51f16c55f0f3b88c67af232e1f9b8e180102305de8e6d75cfd785ca6d", + "0x480c7ab769ff902ad4ec56480cfc2191a160a370916652b2818201469e46b401", + "0x2a7bb3b953d9f24d46777eb2d01e83328dbe814952b7bd6531f764a78565b28f", + "0x5d9013c90ea22beb781ddf0b4fa4cf4c31c1a99d9676c9440b2c5c45d5b9508c", + "0x9e326de156f2139a9d459f0b363df0b6483cf81a20a42f94397629ddc9106619", + "0x9b4402f5085d9e60cf2a4e0a59c17eb7c041f55a002ef607394f8cb903c72a9f", + "0x3b7e3624931ff42c30f245a5d019b51e50e3b84e378804c48943a3b6f835c152", + "0x81585216a027b6343d4b9318de5dc911a44eaf8cad9dcc56521f3e1246ec9a90", + "0xdbfb212f204bb0462a81b40d4192dc175e3eba283574240280b14ffc19d072d7", + "0xed441c36baadbed152765988ab05585e8169de2c618babdb38d26c0789a054d7", + "0x215ce68db1d61ebe7861b1592342a14c6cc666263f3e92b88847524d32b828d4", + "0x8d554c7868e68d2e109a88edb1cc2aeecc118eabec5c99617615609175356ebb", + "0x1f9331f3323e3a8a892e72b5505a4063189f6e595b6b2a5f60d1389be0a33e70", + "0x54898809ce24b2494b623ece3c76da299c74bb9fb3195bb011991913e5f2f947", + "0x3d978dc223912eef2ad1373798f65f5e025a57acef0dbb4aed7dac6c09d77f24", + "0x810987a037a87779cfc929de7168e409c968f09be62f3b90e1c3e9be212b267b", + "0x8e1f20847880d4ff5caa96c1d18b06b9d9d8b8296a915045d96cf182b39071cd", + "0x5f72556a48936fb95a535cbf7cd475da6bc443108a6c6d18702f4062a74185c4", + "0xd2d01d0ab4072fe563900aa56d67fc395375d25483ffde861dcd442c039aa00e", + "0xbd76356891daa85b9e6a66b3d2e0b22fe0c4ea67e6e4816e789c23df846bec36", + "0x8ac80c8237ff90847662b4785b6bf7d626fc480b20848a9969610e227c041fc0", + "0xdcb80b318571b1e2d2dfa50c2fab935c8c5dfa553121432340b54d91275889df", + "0x2a4b21bb5a36686142925f2f8afeba62311bcf8e432cf9fd38263ce92957a71c", + "0xa9a1fd9a3fe7c4a6527e2da219d412c8173ac4c26c03e901fea0018982c40a25", + "0x9135f23437f33b13fa6e2299c7e798fc322a29ef8688c63b92f3d0b01e2892ac", + "0xb05b85ed566deed5b1ef72bad0aba341971d73323f6b74ce96702af063dea7a2", + "0x592d9f71bdffb031d9e27b14af05fa81430f74aeb113f40b97648df3574a2947", + "0x6bbc09f87c8429937a1cd2d74073f2d5f285f6e5a87b4ae68a5eef3a26c2cc5d", + "0x4625edf45f54047e247b4841e1e78754bb621fd4ac6594b769fd64bb3ebdcbc0", + "0x0a307096a9ee102901643ea5add98feed14f6cde99a31270f927a5fd00b757d0", + "0x4c10382982adf76a940b56d28b366bc4ceab5ba9640cf7e9eebe4ee29a661ed7", + "0x9c8ad95f498d9ccda6232e9578e23b8747ae5738c1365b8342c6b9e6ef721fa1", + "0xe2ecbd35bf549c4147e96b6325fa3d5818ff6284e83a0fc196ad2779445d9105", + "0xe331d2a663bfaef3c00c72a97517e62e0e3659bc9f9ce718ba5eee44ff4d160d", + "0x20ef9b9c3fe7bf6f8c00542598c42759e1d341de8af33c580d557c3a2625f104", + "0x409a245e4b3cc07376f97a274012366c42e7151020aad88b297c7eda7b218c00", + "0x05f917499598b666a2208b5df901bf8b22cd032bf348b633469115f4ac8c9116", + "0xdbff1ec281cb6f706831ef3d957f403373394d23a6a4781434278395a6352740", + "0xb9cf34ec3012c95bd81cfdaf3011ad7cf2583f77a13d3796768436bde9a92530", + "0x2b43dc7629a0918691d7589cb02827a1e9aa61fdbddb927712eb5b9d4e697420", + "0xd4bef1d5df8d53a89c2021273f74e8ea25be88738cc7d6c51b893fdfad2a60a8", + "0xdbc32591a230af05bb1cf7bdaf31b09af821dd0caecd2405e134f047e03d9f1f", + "0x38993d8ee05a450b0bd5d972e1165f0fe546054fa0abe188bac924702174a406", + "0x4e1eb2a96889bcd9246e27b1dac5acd89debc4cc0300cef11e4e9564e97afd06", + "0x2bbd2ecb6270ee32d8e43f84ce555ece65d2bb11d53ed4feb398cb0f0d3715ca", + "0x0a571b90a65bdf0f2007c8ebd0b60d23906e72682d10d09fd9e15a417f374407", + "0xf53de1079460bf1b24e237d30671c3fe25ce6529c70880bb4c258694aedc8109", + "0xacffd99fe013566485a3080cd4e87bf19a59c2c39f3124d4fab116e6177ad499", + "0xe8713cf4f37fc1cf669336e0f19bcc73f48ec780e88f072eee9b4764d90f6b2e", + "0x9abeadee21bd0a0e3d785eaddff6fe99e9cd696a0cf41899ab72192d4abccd4b", + "0xe3b27e91ce1d091a866d73e87a32966c047cfb9905ca1f57aa61aa8e9de08e06", + "0xc6153daf207225a14c86d794ba49263642d148cab19223bf1a215705219b4ddf", + "0xcd25511baffc6f1f15ad2e644d170333cbc97db76befef2ea573cac63e6bf687", + "0x18da501ee3dafff8c3bf09b3a4c5ff00b8408c834de276439e54251f7a27d6c6", + "0x2671c6a9fd3835609c816239b8fac76a57b4e7224d652acd2f960624c2498bd2", + "0xe60ddaa96e4939d8c08d8a14b841ceac1a931bda7f89e6c665cfae67ba23fe8f", + "0x497d57ac2392b53dd6329667f56a0e18520baa4b668a731775178d35d2dc9b72", + "0x747e75d2ed3df71a08bb693750814d459cc52178a814d8ac01f7454285cfd86d", + "0x6f05eab26f65abacc81c72d6a00f188a91e37eb99c2249ff03dd7d2de2b30dd8", + "0xf9a00ae18238ed554b75c351dbdefea23f6914b85c7a0e3f183649e245396602", + "0xd28a0d9c81115d494b550972e4917d3babda98d2a0344606c1adb3d336a60c05", + "0x06cee325ce81db6c70f9d6fc3d0d44e0ee6ba30fcae8f814070796c4a0f5f7dd", + "0xffd031029ed6e17f41ab85eb2c715ca56b09b6b472817dcb7e33e6632d6bd1d2", + "0x821350a467017129354b6b97493f484b648d6a31382bcec402130c61395ebfdb", + "0x475d4aedb7a4de4f6b04ddee8d6fe491b72878050c6459941d55c0182eee49e0", + "0xbcb327edcfc641f35ab564c83e28ee1b2460cf86158b44c06900fb4aa5118ef8", + "0x7c654999abc08f34e7ad69773c626b4045a01cdd43ccc0782d14437f3dc0ba0c", + "0xdd16dd29b62466d1ba8a21a5fcef56c6ee7736bdbf3806dce6c662a6f438c801", + "0x3b83c48191f610dd4a93dd6b57e44d344f236b9083c6d0d63b195315d402cb11", + "0x0371ef0394603656c4c7a5bb8cfabab7e21b895fc01735eb1bcc4b799d76d818", + "0xc8950361a067946667e29fcd91c72fcaed9a06825cd9c99025ae671b130aca19", + "0xb7d62766c7817852bcf73e49c935207e22b117f3d4294255370d68c132f10a1f", + "0x7cba2d2abffa24fb8ba90f5814683919ae084ddfdd79226e2260944c3b9bafc2", + "0x350995dc45c3fb7c776124fc88dd4666fe72030ada3c9d5856ef8ddaee602fd2", + "0x9b7655a3c9514907f42d070269acf6a8e36871920c8cf65f8b59881ebec67c0b", + "0x273e65fd81e5530830f8d89580aa579d272bceb438608d89d0736228c47a0ed4", + "0xcdec959e7343b8144848517fb6f822fb9ddb80d496eb0a687ece87c0d35c3861", + "0x4fd982679e978864691b78cc4d50273120c74b7619afe24c318dd2835d629e8c", + "0x4c607deb04f1d46707879414012f797603a033a4aabb43481e819193a838fcb6", + "0x6ebb44d1ed3c1d19ec33005feb09142c8c867354a48d75f76fb5c073e1c30aab", + "0xa5f43a9c697f103eb2b9efac392817b369faceba27dec22e16387419db3802b9", + "0x8c10909ab4cee65c7252ff87083c2045e7591d616168579a0a3479e9e8cec76d", + "0x4f7e0baec2098f57a954d50c20c97890273abdf0961c89a29ab85456a591c5d4", + "0x031fc0d16936415a1a95c3b34b9b262b86108067766e2ef7026fa81eb2e6b007", + "0x094b699e20506226dd595429cbe6b82f5904b7cf994a7ddb5b50ccb127200bb6", + "0x2173fa96d0f0645623212e68b31209a5ef23e1253d3efe4bbfae5f70d5d3eca4", + "0x03bd3f94a7d103945356b7e8704559f5f7906d8b32c40685c2ebb605dbb1591a", + "0x50af81a5e6cd492dcd6b86e3723d6f62a8e35649c76957d3ea4c414cc0679215", + "0xdcc1ec2892d0c1eec614b0b6780a38eba682e8aaae1e3a5217ccea0d241216b3", + "0xf66fa14ef3c4d425b698834692eacd75a357bfb6244ce87217e7fdebd5128c19", + "0x59f94fe3896a92fc0c2d3f262e84cb33ae1137db43fede07c8d7ebbac99e76a5", + "0x56a6f756a742f4fa60eaf0212d3db652370a6e80bf0023ce93adcbdfb2ed2a4b", + "0xc37777ecea04eafb3bc7aa96108f5cffb0fbd272fc5445b50da3e37450e8acae", + "0xaded1fad4b632b7fd97254be9c2cbca2aac0b007b743344b313fd368ad777aa0", + "0x66573ace6ea521e8d970a5b57a8dd6688e2bda8990e94437cd9b57ee3fa78dc6", + "0xd97b6afc49a982296cd6bda05bd80d345026f04eec4481acd792eee8f50a4453", + "0x98c5a5e9d7453b28e5695434ea1e9c10098afcef24d9bc0914dbad1bca6d7b9d", + "0x46694b8c7631d69736a4fd2b98c3064b0b3f91041e662ed57bb0fde0c8a704cd", + "0xc82b772390d949ba53204a9a9a0c622254cc3f4b4cc331b1af80b87756037f2b", + "0xd40c1d3786dafadbbb361d771b826138767188f0047ccb3d446eabfb25a79ef9", + "0xf242ba35362a15a1367c6ba80db23c1b1b08a8e1eb0c2df12df918378464a674", + "0x64d306f3752145d5e592891e2fe224f94137785b0c033e30ab24080ec23b5993", + "0xf51ae6eea4350287f9b0c118c1bc8a33fde3ee0194faf7c5a76e10422e17a528", + "0x6006b0c9986e602b3aed3a5b13449773132c4f2bb64583c46cbf03f18c5c854d", + "0x5fe407eb8f5110e62abf6c2b9848aefe55f95c4da6126d71c23a0a667ba21723", + "0x05145f4921584aee24cb57325a211cbdf136542b1bcfe2f3e700498fbb688fd1", + "0x8744f4dbe496a5e87a716076bd0fa0370762c8baa7da41675fc2b3b735d3e401", + "0xc8ee444ba962511b6cf0db6ec92cc6f682fe9df8dc19729d07fc572ceac537f6", + "0xa676e0fab8cbc82548455c33d8064e2dc14f056a23bf52694c45ef66dbe01d90", + "0xf1b3ef710a4129ddcfdd245b0964eb4f3d508a6c929204ea5e4a3b70f1595892", + "0xdfbed81886437b9a5435c697c6562b2253037ef96cf035f778749e213c996970", + "0xc318eccad3cbbbf1fb5e718c5bc006a85a12a3d046404a21b339b573192267dc", + "0xe0ff3dc11126c17f39bd6c645871d0751ac84c615e43d2c1cb57546276487800", + "0xbf193f63e394e53c4a40ef66f015c31b31c8d9d78523e67d18856219c9bcd027", + "0x4e144fa72b2271883b04a64c2735dd536d35997597329acb8b5c0eb337d16c5b", + "0x4423fe1a0bbc5d20455f1f184342435238df39a61c9a33ecaefdfad08244218c", + "0x2a4bd2a1eb87d9cf008977c8291bda32d6e152bcaa1e9c7be23c3c76f5abd9c9", + "0xbebbab09fa87de0406d8003c2c12a9c2a8e5cdfd3647b5d2421d9e3477d4c0f9", + "0x6cb7179c2bcbced294915051094deed7819b995f29025d157c30606ff35ddbfe", + "0xa13acb664a5b5a997d9e3f47b671d8e12eb9e2b97602de29591c3b81476f0bc1", + "0x7f2011f26dbde760eb6dd3e718b2459cdb9b2c961d9f443ae32898150409985e", + "0x7807867d50a3150fb6711794de640c099ac7beb5ade0a5cd6b2f1f68e670c7f4", + "0x68e32e3c07cea772dd9f8a51773d4e91c067067c690ffce9926679866b5de5dd", + "0xbe824a75c6a8db3071e415130e5b41825d874be05bbe3b9510b37bb75aeca83b", + "0x7f536fd880df4c3dcc027b9ed56436ab083368e11c6ed7af49ffdf1b02bdac74", + "0x0aae2e9a99bbca9acc9faed488d5233e395110bfc01fb711a88c413dfd2077e8", + "0xa3f68250312a604811154be23115ea682af85c32bcd43cf932e41ea0e31eaa5f", + "0xe25f4337604e76b39bdd7016254741026c5c3b41d229fee1b4eb5a50a6c3a40d", + "0x3e7e4d2f78ee3758b6d6ac179db647bbdfb2d5133885dde83a68d9d0680b0437", + "0xdd4b4efa05bb105f0668dc5ebd1323ea3e5a89012c9f4abe09905abc40d5096c", + "0x2cdb91594fbeab425839f051328f7f4d9f5b478d9b6b3e90baf3ec51b6cb2272", + "0xe38aacf799ba21d346ec961aaabd99b141a6916b42f09e56a8ec5c3effec042c", + "0x76eca65281c315957101a2fd91b961a83806639e8af1f0426001f484bce43af4", + "0x7b4a6ddf8357c0de5e9aa547c09aff2d73420358f5f3496c122b98aee0701389", + "0x40f60bd97532d190e1b6fb64b0a5044ce9207d00f9d4a29704f71e8e467efa43", + "0x86a903c1c802e93fac1603bce146eb6b438d87b7223a530b3ed1259ff731ca09", + "0x965ec1eb76b0c375ee0d08a7a5cf48c3f853ba129101ea970c5b6d11929dbefb", + "0x498bb01b5f440508a60030e11fa0cb4cff4b679c65372628059f7f0ffc0547e3", + "0xe3fd5a91740e9d903d06ec709eb877863e6863bc256928eb4f9affb961d92196", + "0x6312bafcd9c011c7b1b3926218c6902fa235ccb4d4c078cff457dace2d1ae4db", + "0x01b085646ed17d06067749e443ca0ee51dcd888a50fe3a83f541731cb9f2a593", + "0x8d96771d5b2ceb0f0050eeac63e4ea54917cd5b9c696ffe463b9b7db8d2e9482", + "0x2d851de624116186d573f05ff3275555fdbcc1c109d4be309748c6736f7586e6", + "0xcb24237f995f63bc09b6764e1480e182507a4755cb70da7bfb13954b198cb627", + "0x3fae54ca8c82e8e64861f46a02e204f542c9e65a52617cb00c4221a1e7d0f4d7", + "0x475446d0ea4cc4f398a70fd4a6f536d7d995f07374f8db675c1f7655612b317b", + "0x82c25c7cf9f1bf5b9cae2226a7303e63e6cd74720b31b771d88fb0611eea1210", + "0x6702068535b573a192b5077de6484d429f5630f6ffbe71de5c44a38ab85909e2", + "0xf2ace413eb1879f2186a50b7a0dd076550e4848f1c8de94ce691648e0b3afe6e", + "0x9358ca413bf29f4c6b3b01aa39d0d50882075994d01f81632df0d24612e01a1f", + "0x303da0d6a9e7f6f4987a6f27ab1891629a3065854156d1abd3aabef7fe68c580", + "0x2e69c760b8c899ccb797aa666c31288aefce5da125a3b5996f48b48c3c657745", + "0x37513f9286f52bc2da92ef60cae1d4ada5d797ebcdd5769548aa30718bd77a4e", + "0x0611c8aa11ea21b6f92ee13ba18493b295b3e6afb02cc1aa0a282c7561f44a6f", + "0xe62b0aab8bb2d58f998d228ca32f9c45960a472639a7d447f892a186b56fbcda", + "0x4611c85513054872dadc7482c4e7772cb56a79602756975f2c6a12c705406809", + "0xe2b22a54d5c41def710ceb75445ee1debc37e2304ad7ecdc7529ebc54036319f", + "0xf83d65ded56956e5ae0753458ddb24159ddb0d463055e103cf0d352f39eeb042", + "0xeeb85de2024de91b206b1a8ce723c1a6e05116ec776b452a9a42db7d48fbaaef", + "0xa1089e3c1c9198f265d4a70f857ad83c53da2215a3847ae181db08614c5ad6a8", + "0x3b9126023c4280fe1bb17030418763f6d49fa94ad6c79f65357c01ceac83b932", + "0x1385d0c8c975db065e74e55b50731206666b1fac5d8990d6a7f437b656ac40cc", + "0x67606f18a5f7738286d620f7ff0c45020f60bb7388a52d827ede4c94d0fbfc6c", + "0xc2cd564b427ed809a85e80445e3ccfd4b1c14d89b80ea54b506631dc4bc495e0", + "0xc5764a2e0a893bea5e900e39a599f10153e3f9ce460a8b3bc601451cb4a946d4", + "0x82015627dce1b5369bd2509ed298db9872cc93337fec9acf86b07b344bcb4190", + "0x5c0c924cdd9ed7fb4a41d5192537e129d4dfd09a80edf82e2da8c66a5631677c", + "0x8911609ccb83eda51cec846f1677f1a7dcf33968df061114d5043b791a26c6b4", + "0x1c2ffe2d794d08f7083686a453a9cd472d31ce8612f179312f77469202efc579", + "0x66e1786858dedfa093a3ba005277f627650eb0dc4a4a671134f3c73ba9f244a4", + "0xf9737062e34e5c76023a56b5c81b769a2f55af4a8890bdbe8db5b941b819bcd1", + "0x78dd4c12c7f3298d643bf0ebeeda9febae2f629f1d0814b4127adf3a02a1a87a", + "0xe08336472b684f840e9f0c33bbefe2d4d632957304e90792e6c32a43e83bfa38", + "0xdb7093e07f806ea90246fd2fc5875743b85a2cc009c53dee59f4ce784ecad5f4", + "0x68f45bb6b58737967c84481dc8b57be0341ba9c48c6a15e90833196749c05765", + "0x9652218948249ced220d9530003af1c2b995cdf4fe4768061ff8837a4ffe6640", + "0xc239a7f445f76f3dfdc37ffc7e928f3592ce29f58270a6b56ab60ef9ab2ba0e9", + "0x9740852ed0b468298de381505436522bde44df974d53c114e7cfa96b3ca987e8", + "0xd5c3bc7d7d255c520102fc838256708a46a5db87756527f8cf346ecda2121842", + "0x1d715b19a234ca2a7d4aa56cea2ef585d8111b8d1025c67f9380f9c92cbfac35", + "0xc3933bad8e0a17260494bac3396ecc267507d2eb8a5376252be3ccefc3884ba9", + "0x1dd98461c7c74f32a12dcbd9513e7626cff49f791e0a8bdfb8e80dde3bf68499", + "0x28eb2d14a563e8450d84b077a2a9bf15a75f8af0cd864437c0825579e83888fd", + "0x20c878dd628e2fa4189a91a10c3d526108eec35f0d8bcfa3b23db912da70c827", + "0x08d2892387ffc02cad4c9c575a1733edb05bf4c78c69e85a5baf2d83c73f0c2c", + "0x520718ac301cd2cde60c7c66143b3266b5f9ac034c42a70a2ca6e6c9b0009657", + "0x02e6db9af6c2fe5e85275c7dce39bc8cba1522ea76a459e8f80221cab51776c1", + "0x2fdfbe0ff5649ed8083d6015f56962055321907a69f8565093c255c56e87e3d8", + "0xe43c7edc4bd630be01739ef1c2ca3c692d485fc69de32f11e79b6e7f91d2ef5a", + "0x0531ec536e30de8a9e0485fd49be9615f3f9fb1d019752249312e21d43d962e4", + "0xeb93616ebc3cbe000c993743720aef0d8137e412bd89a71a39c16e813a0eae8c", + "0x9f63b11ad6ae9f5c260c6f1a1a340049dc194d6de5da635d622a2c7daf812939", + "0x799c4e76ddd47b696ff230efc74e4cbbacc66a5dab9d288d29e05410e694a8e1", + "0x67d305e5b5df1a966b69f837260cf94f93a784c1a8bbf29265fd6f05dd709d69", + "0xb119aec3ca2c4c9037cfaf1c388d2a6117c6be3fb854a081e54965db0658e893", + "0xe0c9d2b240ccb07874d5a11e5df32825e7dc96bf9d2e0d4a51d86da37e72c3e3", + "0xd0e900c82a84257f1bf25934c1b2da63ec7d827a9b2aa7049021966aa71545bc", + "0x850683dcc2f818c4516d6a856df1e956173b47ac0b7bf35dc4aff2079dbc6917", + "0xb5eea6fd8f67fd18142ae6fdd7a3dc528a30d4cd9e9f30b81a6ef78c5f2573ea", + "0x66167cc181dbf9035a8d847132e93cf8707e40fc340d9047195e00bf9cc720e6", + "0xf587794d6d07f112adbcdbffb8b2b2187de1ccc09aec37440211a14e59efcc3a", + "0xf661a3eed07d32dcb9e458c57107d7effc71c3b2d3879341d15ae5109c562796", + "0x785227ef4ccb621218f19887d32d726b0d013f31572201195086a663c6eb20cf", + "0x7ce64a347a61b81fe8dd14c6d4383b79fd9e44d7a401fd59e464407bacb537df", + "0x944eed38df97e3ffc5457f9cf962950e4638c4099a9c6d278fe7d04ada491810", + "0xbbdab65b313c20623a0b5873af47b1ce9373b067ae3e4129b068eeef3f0ed8d6", + "0x77e5927f72fc99cb7f4f28c62040758a7b537b6c4c7a3192eec63c5d7feeb6bc", + "0x96c791c51a980e2b4fd2c2f9df5074e0928b65600620004d98e10f96a662aad3", + "0x6176d82463c8ff177cbf940254a5265b0b5ec46fbf27518250305e5673e97ed9", + "0x70af3df65f024160a3a7b4d141109bfde5bc469103279d5852e8f5cd64b7184b", + "0x576ed8545b0a991ace02587f7de3d62f192769a02472188101620f44d9e460a3", + "0xce7cbdc4faf5e2468955ade6afae6ffc6f5d5386a17a8fec715d65e12a393a8d", + "0x30eddef5ebf9a5ca69d85fb389b64c4c60c06807c1f0ffcedc7e836a3a80a303", + "0xc64b56919934479b9972288c1274558e049c95ec8a5f8d5b27d4c793c4e04eda", + "0x594a87302be7bd5d1e6a329572690124ccff6e4e8cd0863c89e3397e5c537e3c", + "0x305ffd8da8dbce0a76ea17b6e5aee1b18c2d9612a645bff9b59fc37293df318b", + "0x04c15e731ca0cfd5f741ff9295f4bf377fbd0d7c40b24b4cd23f17fadf190d52", + "0x88fcec9e34a9022d96e5eff3265c696e15b0617d70fba5ee65015822e66dcdf8", + "0x79ef2178dedd2dacd9a1cd934586dea9ab5d8a649d1577f1232f3412fb75fb79", + "0x3d220a4ed6a37a45fab3c1deeff0d75c276599692456af0a956a4f37dab39116", + "0x073521d42b08caaf55591e1c8ed546a3d310a25ab4a6ab22e123515f468a2163", + "0x80b49751aaa6cd6ae89729d7024eab6b3d533e6e1120d0a86a20ec96fc83ff0f", + "0x839112b33d17a1d9cf97cd5e8744e11bf4851340043f736700973457877eaab8", + "0xff12a0443928d6b178009d2a3727170ab4594b53927fe9b103548e2ff0d9b694", + "0x42b7d1d7a7f110715b4f9d63968af44ea3912a0750ab8510f4bfd90bfb60216b", + "0x5c117eeb9d2170b1022ae4cd5a58ad2f8ea14c86af9734e8069ae7eb9c49d075", + "0x07cfedc40f2c1b66e5b595ec3b9b0626b187dd51e7f23d8b1949c48effb70f7e", + "0xb16ba9b6e01dea476d1a46caf1ad706c0b7c5fb5738d5bdbf8fb9f6d07918b1c", + "0xb3d3c49ed8b25a9e7207a2288e602d0b30d03067b6d36fa17200454a8ff9dcc7", + "0x37333a5118737518e38466900047000bfb5161bdd9e5e93c202be7453a6c2e10", + "0xdeb98ee6e6cabae76ce63c27b25b2fbfff1ca294294a6dd799c6282cbc11f703", + "0x5db3c3cd3bfc6f2340c6e206534d82ebfd08c06a502a49674aab999d30920ae4", + "0x3614373c17bd3384a866f9c34f17f28f6c1da424a368c8b1783da81615988ed2", + "0x7fcbeeb498298339031dcf53e2c1c20eaf9505ac5493a9c0e54e66e53244a17b", + "0x4a3bbe0d938043a88dce6200f42807e8b0a61834a86337856ee180311f0a5e9e", + "0xe076fee571b10adde03995cb9b6283b7e0ce6774402d87ddec6cf95d6cd416e1", + "0x74e17a7011964724fc6326074aa57d1bc31faa0ae2b0f8a1b2a1f7e213271550", + "0x97661ac2666c20924e5e5f7844b6dc0e3173d27c75ce8d7f93fa89adb7ebbd16", + "0x9e75d5fafa35345ea61e83c445389427f1ce01ab843c8c1b467b65cd79e312e7", + "0xd8ea0a48aca3b95640d09046571ffe99b3a23c6b966c381e1d2d5ccdb4a1dbe5", + "0xadd768a37f5fc0343abb36733c5ea41c7471f7c75530c1bbc47cae51b777ed20", + "0xc14170585f45ac86128e23b6e66ec7b64bbe310371c7898939ec210cf18c8c66", + "0x7b900597bf6ed044744f2e220750e473926f509dd0b1a12219ae101c2fbd79b9", + "0xa03b01f1408fb2b1d485b2ae8d0bc11ead09852b91a8d9e772018f5387d90a7c", + "0x441e56cfd99857869676c6857cb5b2fcba9a5b8d4dc2731a875659b65e8a441b", + "0xc955d0d543bd158e8e7b895c8ae3c051e2caa0b7d8bc4acfe2ed638bf477b2f6", + "0xedbbbe2b83308e3292f300abcfdfc4b9ae02be503c647e25b4f8472b5925b8d5", + "0xfd8d30e530de2edf9e6de7ee3d6c8b50dd44a3b8a843c56c4c1f8c34034c8ff0", + "0x5ae0cc0617e336b33924c6e1370c407b4a6102f1291d737882bf4788dc1e154b", + "0x267a662185ed093460e150cdb6f33b7e82f76de1bc671165b153fdf23db1855b", + "0xa0c959deb9655da42ca6dc515f591c88b41260fc31cc8bda256609495f5d6bba", + "0xc926b3c872fe23dcc292500815fcdc8a9da15622118538e0a1e951cc85a447ce", + "0xde525a7365d0a33fe2d1112d434c1d8c7048cb401497853eb769f7cc32cb062a", + "0xe9e6eaa3fe317ccee44cf4b5050315ae65e71361d30534d981839fab6ea3115d", + "0x2984d9c29cf69ef9fa0d7c513987b51a79ce61f7fa2abe516ecb5c6c9b304c07", + "0x3fc16e70dcbcd6f00d0b44a80c757240fa6ff8e15917dcf58ca9a68873db3771", + "0x0a18a234383643026c7ef151a64dcface72af1b28e5fb143acb4532bb8ea2927", + "0xd80d9705c93136ccc353ee3ba68a2b61befb067f6a1e5dc5b9de8f72b07b14bb", + "0x3c0630a1a27206d41238897e7e736dac520aba1090f4e20511a5691728033dce", + "0xbcfddf46ec0c837c9a0b6b26024332eb93f31c413f147f7a6c2b730a3b1b9e99", + "0x2b57773e698c8a7f161644e1850ad389311515270847adfe60f0b3e5641072e4", + "0x7ffe83e4da4dce81437bfd0f88f8f1beee8d94f8e6391cf153572e3ea345f6ba", + "0x45a704431bf998e07c7893b88670155a80778b8cc8253fa053d2cd9a176f403f", + "0x8c45ed4838590f810a904b8c4fcf29002989311fbaba4cb434defe786477c373", + "0xb03aa770bc7e567f5fbf43d118e41de314fa3ed1f100bec38f76c22910e10a83", + "0xd1397957c635cab8130e8add469ce347b5f4082511dad08d2891e48d387d02af", + "0xb810882af943ba660befb40ea0db5c96f19e8107bab2f17c4177617365d1b8a7", + "0x92111d150b9331ff1d2f9e259412f6ca51f25d2b2666897249689cabcc005017", + "0xf56a2a2ed104b4b4479ba59b2b73ffd30c690ea2b041327b6ff70dd2138ae889", + "0xcc7b32cb3f28ceccc26c8d9774667a69070135b1c5f1b8fef9b23ed449a87b99", + "0xf5bb05c5f50de2003c4bf735631c573853a32eaeaa6b840b9f9e900910104378", + "0xf6919be9161648ca90cf443e3688ae9e1004cf8736d3862b1833e515c721a9b1", + "0x81602b654ad797c32a6adcd6770832f347d0b7e32f0ff2763302f0eb2122f962", + "0x8d4ca89e646d481db8db85c6f08775948cfb16ef91368778e4ef5e9f88a2bfb4", + "0xc06d440ab50d5f160917f6ef48d756417cc02da1cba91eb2e18f0a978b37a56b", + "0xbde3dfd53822a0e3e50f8f2606fe464c5122b475b57955d3852e959e592dfda6", + "0x3b45bbc0fcf35cece8e600c3a2f1c5790cbbd0500f6a1cc36741cea8f7f348b5", + "0x856d4b2c75dd0c130da4f1dc5bdb4b387e9ecaa17d24dcba19ced2743720c279", + "0x7908dfbdddd49d32cf514fd6d0b5a49753178b8a82845d26f8b8af3bff44b354", + "0x73c0d443cb017ac2dad1d6e0eabacb6e1cf48f6fb81f01036f4df45b9f52d34f", + "0xd1bac2429d2ae58b54ccbff5ce865f94080bb9ce6e89ec85c06d41e609502efc", + "0xcba30906741474b3c9e284605f5c41950d54853310da095a374c701ffaa18044", + "0x5061e64ceb975a4b8035cfeee08a0a198cb08d70df9ebcb2c1668f78fd123bfc", + "0xc25472f5556e4b377ceaeaad31f505a08e422f854734aa66f033d6d221be6ed3", + "0x744f9195eaddf70465534fda05a3b8362fc5387d5023f89452b353b6da83d52d", + "0xbb76ef7faa7721aab7b59e4a7d4df41b4d90e5d65bb1e7d4ddc8072dd0fc909d", + "0xbcf0ed67dad1c83a08a114a136ab6a4f6d1611bcf5f87b146ba342441c9abba7", + "0x5a2f28e61c1d969c4ec6576e9f1d7e4c914cd0425393cb97be195b383c5c402d", + "0xf419b3d705f817d8262ac16f271b1f40a9f15742d0862a6d58f87c0a9ee7dfe9", + "0xe8080ac558f677c925a3703dde87beecbf9100694b1aeb0395e7874b1ef6fc70", + "0x71474222dcb27760093678f6028e307d3ac201b42f9b6f140cceb5a322f22985", + "0xb844b10b354af63d5d87879727df82f1931da8e7d41deb9c6a154a0ceee4f444", + "0x7a37d64898c49a3d585bb55793999cb3401e2daa8d9d677a06522ab04bacb142", + "0x8e265a61ba377f3645c1f660266a3ce0b40ceebb86f5d6a6d9919dc002c330ba", + "0x326a27885c5a9dceb93cf5812a24da1b40251f39550db13bbd3fbe288d4990ce", + "0xebbaa45e3e364af670ba422527614095b4e11ed208a26854d529b70a56b3c91a", + "0xddb1e2b4ada45ad6a4afde955cc71c77e4d0f90834817905e83b836c280ef2d1", + "0x0fd2ff7aa5764103a70434944fab4cdf4906caac6d113f586e823360b9eedb4e", + "0x06e824b17dae9f6eb3326279ed3fef2144934089f779e3c899dee14b62bb2034", + "0xc9d99bc8abd6d07debffba89226db04982f2999cc8c7dac80b59c1db1050344f", + "0xb9401bfba8b2d229b63eea8a10c9c95622e9e5706e9ad3969e259b6a1b8e5c18", + "0xd2489b6eaa4ae388f20a71a3e9890114b91b50d4d73f70c9f7b599ffa09ed990", + "0x3a62acd8376093b53ff5904d965f072cf9eb6dd6b8ccc1d023e99e077586ce0f", + "0x0ceb8601ba45085f5a2f97cb38a1dab945f1b15b2c46c8746bc0c136b91d7e1f", + "0xf32ce41f00cabffd8497750fed24824f0b614bcbb523df0385b75db4ffe7ee0e", + "0xeb8fdcb1fe3a63f4d9613676c7cd94853b429ed13d7c8d7dee2cbea7c83a9ae3", + "0xa2a687deb2990f339a2c1d57ed82952ddbaf202407a9d7f8024d16c36c62ed64", + "0x9e2eba3802dc7d500cf248a873ca536de7eaa198b6e7e6fc27f89a47f95ea71c", + "0x3c2a8495eec15aea32674a8ca7e2b4eeef4e2572300ae1f869da0053737fbceb", + "0x6580ac6f17f762f1ee10e2bfedbc3359c087b8ffdf01a6ba8936a5ba063688e2", + "0x1d62b1daf1286cab62957ba040fbf8c22877153cd2817a61f28767c4618be90b", + "0x8cea7cd371c88460ad3c5b84903f59bfac40d417e8e52635463aaaee88f1ef5c", + "0xd1d04b1a3ced0ce107d724ad237fce4f4d6496306e26e824ec97b011153fcf43", + "0xfa60a54d214953f03f3c994547f312fe0a34ff7b74341b788a9706b9fc351811", + "0xaa487849f13f1408494105b00563b11d54d539f142f864216874592de655b3d7", + "0x3c2c0b3b1f5cbfca2134659761632244e21a3a060d109fadc030043a24507566", + "0xc63d4832f0ca1af6bc8c73685370a1cc0afcff8597a0ef8e94e16f63fd34f8fa", + "0xee18e9bd422e512317d9c1bc0aed0c59b9ae9fe46cc57fb697191cd100a5e83d", + "0xea193665057236a54fd2cf37c0c0aadba4cc7a5e0f67bd8eb3ca842177cfe9bb", + "0xf4b096c22c635bd96791bc2d9b337aec62dcc021d9ecdee125dfb50421660e29", + "0x117e1bc04e42908408a662170ad8522d188b585d0965eb7474fcd7ef5ef77afb", + "0x0210a940ac3e0f5102bbae138f0e42bbe6eb61acf154556903fce0fe781782c5", + "0x2def0ae13e9e401cbd3e26395fe2f69dd3ec86bb7fd73b77f513eecb1b8967bc", + "0x799fdd2bf22779f7f57874de3b1bf1bc72992ddce0fd2bf8336f83ee3dc36c86", + "0x55947ef6077deb52d40682c8a20dd9524ac27dbc2efd880697399b04f06e70b6", + "0x7c97310b3f6621e2971d13c05dfa5686260e7bad8a12ba98e91d328b17801b8f", + "0x78e3203701768a0b90801fe5edc6c1005733f90ea787316636c40a039c65cc9b", + "0x083460777ae88da0b911c276da9625bdd6dcbb414bc5477ee1bee55dbae0ad8e", + "0x8d103eff6d3eafdffe026161b702a8e3f19b57ca4b9877eab7751ead717e353e", + "0xdd3775207317bbc62138bfef9a42c7fd32abbf5d045553aa95d44afb9ddb9fc1", + "0x643c1706a3fe094500126069681020d55c0c41f71bd8fe1f28397817dcf795bd", + "0x07f4b29fb7eea21ce630ccc0525330651dab81890fd887a64aadb5d60a2aaea4", + "0x0e2eed5f75587bff3a422c9a22fe48ce022418198e1c50f0b84f500dd8884616", + "0xcc97a4217f13ba69059cae753dd85027dd0f06e7cd0e34c360e84ccf2c95ee87", + "0x210a0c4db91b7dbb5d9e63ef6006f7755fb4ae5a9f6bae94a98e68a9ec3a42f0", + "0x1043383353226805fdaafdb1e0617644428dffe8fbcb2380183ffd0db8a6cdf9", + "0xde03eb601727676392c58d138646a16e5d3b211e1d1dd383dc2926e5759b5de7", + "0x77779ac7c2efb28e35a97ae8c9ac32c3913b692c56b6e08b76d1cff6312996c6", + "0xf04c563bf9009ec2deaca11d8ac730c9ad75cd2f4d50c8aca96b8dc2ce475ef4", + "0x03cb85e0ebaed220c489c824963852ab80b32f4ceb8ceb42f77ba911d12bbead", + "0x57a5874848e7513c3897de3c0257f5010878cacd33e51f5a12f47e72b3bff73d", + "0xdfa46331fa221db09ae3c9f379181195b2339d6d38ad8a2266220a70a1fa1c5e", + "0x2c3fa79b11bcb8aacea485ef33a87df84cf2ffb38102a6848254ada53a0e5bd8", + "0x92ff1046d7575803ff5ba240e6d7257851668c83ebfa17251b315bc932b637a7", + "0x3a68d0880f1eeb0dc0afa12caa6c4e946cc8fd7922c13ec66c8b4a892a15de66", + "0x13017f2a6dcd02040f1f74e0e4ac374db744a84b749f9681e2ed07d3f8feccd2", + "0xd093c9e99fd918e56e1078049369248b2ea465fbb7167bc682022f91775dcdfc", + "0x316d246816a2900249e11582ec49d5b64644a2ae62ef7ebb28abb96320049994", + "0x2f613addd218735fbc481fe5099225dd62896b97a4754d5243fe56ba8b99f9d5", + "0x96eb252ac59c8bafe974e0591403ed7312a78c5f5f17a3319ab7fc14ec797b71", + "0x7574f5602c4a6e4d6a9823888e0bb938ec016c69433e1b60c1927933b8fbf10b", + "0xc2e0145d31a406d26c956f7a571a4afd1ef5bb5f1d868d3cdde184116c2420b7", + "0x4798500c58a9faa19f188f8ec04a255d73c4a7c26faee51d10fb24990020a995", + "0x30de800ad2950238c2e63a9883d7580f97600e113190923facff90436cf5881d", + "0x6d98548150b0ab5fdfedcbd79fb02655bbc95a129a957773ef5db4b3364b96d2", + "0x144abe6fe7135ea72653c00aa99c4108c955ba35793e225433b18bdf01b3c115", + "0x0ccc9fec6a5e8459be6044b76f1cf083e81cf62b47cc16add1d397ff234d85b9", + "0x0cd2f9e6ac34d707b699f4f2fb07263efd5065851978f9e0404de7b5c4f96dd4", + "0xead9df4c5cb64cb7035893c5d1258741a572a47e810b580a0ddcd7866bd6ebe8", + "0x24e3677e8ec42c9e0933a75625358bfa0a90ede4d09b236d257ee005a006249c", + "0xba464a0783690b1b9de94907c8ec8c056f53b0ad53af28853b4fe17646c69e94", + "0xdfd0e298de9d0aada71a8d35cf04a3da21a55cd8cdd75cd6972f1f10fcb0fcfa", + "0x7608b7b6bfb50166bf4b66d0c37458e6a43b7b58aaf1ab49907c7e9a801c373f", + "0x47b12ea742882d8cd556edcf3d9fc7d7be5aa96cee5899a43e24040677b336cb", + "0xac5ae3f8b3a865998063ed90df0e3e065c95d77f0bb555a57b55f67a64de4a63", + "0x15564e8ec584158e04b575592d855894a3bc73f2da9c54926a0fd83ec8a401ac", + "0x572200097e3e2ff212cd6b1651a2978c24987fbec8852b21b7ec98528a0c1218", + "0x865e950e465ab69ec53fe9f843d8748f92481b8fdb9f2378bfa1def29da7fbc7", + "0x07a6764d6b7dfef4deb8727d5f6865c81890930cf747ba747d67a5439a4143a7", + "0xc18b8d963f9543217c37a0c5355f994c82538d7e046d5ea7b692d5d23fd74b38", + "0xbd1b2bd86d912a253a5aaf9eda35d07b6d636e77600b689eb736dd6d6d1b6c6f", + "0x2e29cdc46141c3fbc22caac8f8f7899149c5492aa5d481d83db68fa887d829f1", + "0x36a702c46b8d50eef5c57613693898b266904cb2b197fc01d24aa345db6be067", + "0xd28693822f04d2dc797bd85263bb594fbc67ae5e89fb4d163a5245b77fa15d38", + "0x9edb54eb4b381569612ab99bea7176abf5a71652d702331fb48fe8c8d5ff8e23", + "0xbf52d22171eeb3f11d02e4959f8a582eba99f280c6a5312b7ccba8171c4ee161", + "0x835ac2d46adcf26b5e039ef4241c9ee7b3aafaf485fa5f36d87be5f1fafb26ea", + "0x75a81cb8e3b5123d568bba09ab3931e3d9e7e0abb459bcdcbe85aafc2f0f3125", + "0xa59709d08da1732ffda9be3a975e6f5c6836780983b18232da867b3a3f547b96", + "0x7154ec757d80214d70cb8355c608bbfc76165499f174e8ff675ffe682f9e50a7", + "0x2dddca05e5123a5bb2b2c3f16c4bef1c191bda04554dc94cfd785a6eff6d7752", + "0xe3cb3abaf53f892a50c398d207d90a70958ab354ff2fcb0d8f21596382217c2f", + "0xf0a3b2cfebd40d26c256b97b756f8483230573ffe4668e225e902042dbf74d47", + "0xc16e0d70d57494cfec8966988bb96a582f2cfdd4132d6136d0971b8e9bf5275c", + "0xcb125cfc6df4d38ba971e7fbb5e0490ac30b7c2c6decf02008bc0a162e683c37", + "0x27b9197823aa2ce318c6f9ba7fad435b7cd35fc8d270cffb52c10b6d9b0b2cf0", + "0x5c9efe2f30f944a595c251f9bcf170a52adff34fa3fc98f292f9a7c2705ad173", + "0x3dd533217df74a164fba55baf80b05820ffe048feaef4b51e3f01e769ccb4b03", + "0x88134fa44efbfe446cda3f1ce9dd5e0f65cf396f6daef96357af3d6410019dac", + "0xd4d3a36a36ff1673c9741bb2880d09e41058c07661964ecfbc589ec70fea7069", + "0x3136561fed8dbaef7124398a9b030527aaf8561290d2f6f69be4d5b16628a84a", + "0xbf0efa744699c638bd73eaaa7682d7f547401655cbec6a6730e58a5e9a1f9d54", + "0xc8692e1b4f342a2909d90b246536de3b8db01d0a36eb1a86ba18c069f092d178", + "0xbcf93465cdcca9be1a760921066770aa25ba8aaa15adfca128898aafa893707c", + "0x6a81c85c76dff858d74e4a024de057142523db5e179d40be1420d56c1c45ff2e", + "0x0631001898c43240bb703138f32033d4400b6af8b15829c670588ad98114e101", + "0x35e06520bb821687533c155617fcf57b8ffc295ada9812265bd83eda8a365b6b", + "0xca92643b61312f3f10b5612d6bdd67dcd0395b39b7e3c69d3a3ef6cd14e495b2", + "0xb875dffc3f9f8d2080f27f11ee2101094315a808f04270f45e46fd2808172703", + "0x6048a061d918ccf118ac3c5315805b8e548431afd403688512e8e3649358bb52", + "0x36505ff6395e42adf3a03bb9a56d0ff49c689687b3b755fe760e182da2df3d3e", + "0x7a090911d0919eead47681bb4cdcdb2e5b5e7612e8aaa4820712066d03727988", + "0x0bf90d9c1476714ebd085cf935efa2113280cb0b94262ebbe25c2982752ca894", + "0x6828467da37f4577db74f49e9e1f90c7e3347d55e65e1c6f492fa4d7eaa9ca2d", + "0x39152fa5b6bd3d32645f514cebea486446eb3b2e8f8c17e7d7489b3d1b7c3867", + "0x8a6ee6c02b8ded8f6b1a1e03472d4ed5c8e4fa61b90485111937f383c9b3b3d6", + "0xf688553ae24c477d18c5057d1e6a4729453aff72ac6433defc3616954978336d", + "0xe85c901c0293cc6b37e907b5bccef9c55507456c39e5d10cd4af83931dd48832", + "0x2e8b779401eae59f470660423a0505db6c0835cd184edba53bdb6ac27f0ac070", + "0x7cecae5ecb354871db9565908b6db9566779c0f61d596d3e232124219f26b323", + "0x10aee5642bc9f28f632eb459dfd1c4a0c49e276f8ac1d1a9cc5970f46edcf5e6", + "0x370bf1b7f1d61b52d3a0b1d27fb0a2a408e496ee6cc10d3e8dbfb3dce3ed1e76", + "0x8bd89c95719b303ffefe64310c50d6aba72961881704f7701ca0307e19108dd8", + "0xd5d7b34356a93f383d4fc60e637275144e7bb81d8318f11055ed7166e38edd73", + "0x2b8f4c957918774a220b17c6e3924588afdce11b630300478f47e0c3262af445", + "0xfc9780ae4658b4eb7da299fdab53294cd10d040c0cb831a91d2703b2157be9a0", + "0x9909ee3b096e0df123e8da5e22915d5e357f501322370ad53f93544b73070b60", + "0x285924405192b2971d4e268e40b18f86772eb067e911e0ef9f109dda4cb138f3", + "0xd880ac8435c099351b91ee8b453b14729c2d53a4f9fbfb5b3c94894cf0721331", + "0x5669c0588c1ceead3a4f1dae91769d537911b18f89aa84db0f0ae72678ddbd91", + "0x9b8f4958d666e2a49891987b7470e28e57be9d7fa89c4853e4ddfae524bbe75e", + "0x7639c620461f4bac132c581a7281ceebe8536e1926349d37261491e0b6efa7f9", + "0x0967511a019adbbf047dbac1faf83f0b7d06ef7ef054fee79bdabaa7c1f0baf0", + "0x4f2ac928c10409d3ac91f00e24d5b4107fb4155ff7b1bafdded78c5722b76c54", + "0xda1876b4d5be9a33124f34690191bf6f39ac1055001a79764a025afcacd0ec04", + "0xe29ff9517d187f2eaa0c4c79520d19f6c4d27f51c532acce20bb7a9640b9d74d", + "0xe5523c39af1eb67cc894af41524c654bb062651818b361b4cf9b302a37fb08f3", + "0xd6e1ad6e2e44408f8eefd3b473f725c4b5a4cfe588e0c2653e64934a68d4eb4a", + "0x7b865e3fe6180e0eb3361720ca0fd6e22b05617f014f6e425ed1106eb3053a07", + "0x8e660a83f9278af9ebbb2c16481a47ea135faa0ccdeb9a92bb26e509b0b9a000", + "0x021de004c49fdd80fe4058c9c42f19b914dec4e653c44c39359b2844e46f748e", + "0x684c1af650f910204787199614c54e28d161863852f2e408846d57ab0fd0b3c6", + "0xec4c400698ac83ac2cfbcbe4440117ae6e1713e8a57883957ee34c48e74a86e8", + "0x741f1c84fbef0a99ba8641943ecf6b0dfca8c216902ebb14642e8762caf632ce", + "0xf2b32e6015967d8960dbd9ef09c6f007b27435d17de86b104431fe26f45bca50", + "0x459fa564f5f17ec839bfd7a5b05b68a27c84b58bcfc1a3a92a79c0c54eb38ea5", + "0x9b0878855d3712c363ae3d5e16c11b566ccf1068e24283cf7ca9cdb77f661346", + "0xa81510769ab6171bf4d030e9c34d7b3485b01d9a9ba4550f644b4082e25ec7fd", + "0x139c339100a0e0eeb7b44cf06324e3f52b6a4c6d7441b047f58a264748fa67c3", + "0x3958397a60f751cf49eb6e401ec270eecf9cd71873c215db65d75b2cefbcd5d2", + "0xc681e3dcaf703dd2b3436d84161463c3f5e20573e2dc768cf90ff5eadca297c3", + "0x9294d6364521d26bed54416b7a14f27c8c3b9afbaaecc38c5c6ef359210cda87", + "0xf02bdd96c4c5da7b3b37f85c0c755114b1781994be6fcd15037ed70b077dd368", + "0xa920f24be1a17dda9f0d7af922cb100a087365a910c3a6a1df2f02c94788eb7a", + "0xc89741fcd7b836afab0bb2cecd6f5b099410905cdacba566f2280333d6623c0a", + "0xae09194ddd3a42a5ce7c2f942008b1312c856cc0aebc848943893afda8deff53", + "0xe4cb0977875d5f59bdb702aeecd11ca38b8e0a8f4efb5344728af933765dbb8f", + "0x4f2cc685e7de74934a6b9f35e25e08f7e9a8f451a564bce67fceca8908fbca56", + "0x380197b6ed54e53089ecd48544278f158a8c72c4a8c102057d9f24c6e90e279e", + "0xf1b66ebf34858d2eb6a7c2094c00b3d766c16966f44731684fe7895f331e985f", + "0xa6b5f00c304354a29209f04b10f38c6f84ab26506c4653e7466338dc8976699c", + "0xddd9d203ca818cc47afb918c7c88455a8ace6ab68904ed90faca55a68a9fa888", + "0x744d985148d70921e7613b929a6971da10d3bc28497e1da5eb3011f49d9a7c8c", + "0x51420a65377dd932b25f82d0dcbe830c5268445247e3af34d26dfb17287f3676", + "0x5786c2ead0ee4afd9df6ae8a8cc8ae1d8afc09347e82cb68842a0f621bb14f46", + "0x6da272771764f1b94efb4fe97ce9b43c759c00c4838876070dd7c4b939da413e", + "0x2bb4422c501a6cba7d7eae6d50342c2abe1a8d2c6c3496ff2356da03fd005b37", + "0x620b0e3b3038a93ea2716dfa7f74e2d9875146579f8c82b1b19b8955196a64af", + "0x890c92eb94dac8987ec323e085f9ab31f07b3bbf526317d7d39c37509a5657bc", + "0x9a44aec18d53553b93df44b0083e092fc434bb3df81115005481fd9758ab64bf", + "0xb6e2f3ec7db857f8e5696a8b4c48be3e64ea615291f1bf0b4d3df98e05b33023", + "0xb9818a538467acc1b35df920d0d394a4d3813a6d8c5e2f1efe40d0cabab4a757", + "0x480ecedf51374a329fd858b33848db24a55a2b95a4fe5fe15c9ed98d1ca4022b", + "0x7a7d9478a49daac6e31000f10b035e95295613b59d57fd31fdc237c0dbfbfaa6", + "0x3ef3987253d36edfcf464afd3441201bb3f8100a2e1c1bf54cee90e45b846693", + "0x8457c086b800b68d05ba6e513ecffdf286b81b3bc210743e168bd89e6de93d8d", + "0xbd2ac6f537c6df2e9e091db677f34ad754176875651f7e09c4138d22a8614a5c", + "0xabcf18c051278a0aa83efe22500c4069d0154571b65e362a14142f401168598a", + "0xc8337d1456e5635f8b781478f64314a121f43e31c2df1f9dbf83475f0a576040", + "0xa37ec2566cbcf12992db2408e4af0f4cb2a284d6e3a8cfab810d8d8e8d0063d7", + "0x10af8cb5776b179157de1ad307e7811cdb94f4733b54a20d4e10f3f7c6746642", + "0x0fc3e593ed3587246a432d820e626109a4eb5781aec0e40dc023b85289647a0f", + "0x866cd1a3005d513d341ea0c3fc35d49d1e7930bc4fa10eec1725ac9885447271", + "0x5b110870aa860dfad7a9e9c2d081c5a4f41cb2a636bdf7500a9d5dd49ec0c94d", + "0xe6bbab105c70d5aaa17c0a4010b0bb50702e7e464d6be69f8edf71fe4953af47", + "0x7deccd14ed3baa901c8329ab97d123ed799c27b8c60f7043854c1c3488b38233", + "0xcd188619aa4cee10382811aa829cf8a1062b7223338103fc0156c2e376f3366f", + "0x83c0dfdbd5d5af95a1517e10c77ca61f2766aa926498b122f80206e8c2f9ea40", + "0x8b2ccecb67ea382054fac429b19199e6e344627397a759b985fdf94f09df2a50", + "0x4cf3e065189684cc568b7010168e04c84574fe833af3e51333b95e67de6c1eef", + "0x081c2b592fe1e339dcb51191c3258135d3c0c617460885630a034685b50d037a", + "0x430e7b924ff6bc118590f28ccaac12164e2b1a09bc8c3e1562a82b68513a789f", + "0x21c8b24a794a5f9fc99d2a51c15e392e2a5802a66687e938db46b31cae1afffd", + "0xb8cb4e8ffb37bac33e88784730c8b9382d060b98f74b800833457543acfd468f", + "0x4564d781b9026a83b557e61aee1d4cc3020a3a2e09423f11fafec08d497092ab", + "0xa65c0666c60d9b93837b9a27ade3c9ebf91158769c8962752b0b3f0065666c33", + "0x96e76a53ee8fcbdd24919d6da3d4b1ff4e8ddc5936873f1a542eaab1f3993598", + "0x93bd68adc2e1559699bb259a9f627ffe11fcae956f1c744aacac9bc28c559128", + "0x3b1eb710dc7a631b191985cd1c177a6f602ef5945eff6c4f05af4f1cf9bb0b39", + "0xc363de884ad97bb1d1ea75925408340ed9f422c68456a6f0173b0900121e2ea2", + "0xe71d1614bb78f21551b58411a7a86e76c5e8f42c52cda21b4e9507e76b21176a", + "0x601ff5e6e991c30a563694e00113e0d2e0de0904c27e8202a872cd8795bff678", + "0x467612a80afd872367d2b64fa53c131b445dbffa1a79e7813cbfe46b29d18231", + "0xe81ae8ec67b40cd2a35ea30fd4a4587fb63c88e5b92a398421a152ffc97eea7b", + "0x016e855144b425b34865d1b51c142f44e2c2928d3bf64231e6f7f685cb52290e", + "0x82ddd0392fec6577e7aa000b62db56ee7f879cf9dddd29bd0e9eb70304dd3701", + "0xc3c0462c8b761a340b3c0be27ff5748c22ffb82efab10c9c60cdc86b9ccd637e", + "0x28e78a902986b4c16aaf23933c69c72c4b3a487e0a3fc60dc90174a153ff9d41", + "0xaf8543903b0a072e38ad3a62bf38c08e73fbe4b45151af3337eff9739ab1358d", + "0x711055cbd1b5deaa0030a848e09c09269ff687d8969cb417633339cab8e9231a", + "0xcfa730ca871b8a1dd1b6558a3a34b453333608c70e7f217e972c8c16e68c2d25", + "0xe285a76ee79eaeb34d115a0dbd453a9de2f7862fd3840442dcfeeb3d162c2a53", + "0xa3832eca2ed19748aea2f255e2273635a7d40fdc5a793558fab3bdcb890170d4", + "0x0e6ab4d8e47420c11ab80312e20e19c875502e13b464ba156b2f69f8f286648e", + "0x6680cf57a47adec1df3081e302c514e02a24afe3607d83ec6b60fdd03bc426c7", + "0x953f30bb0bb64e49c033b97dcd13ebe583b1ba605a0f0097cdf307163e7457a7", + "0x463d1d1081fef6b89e6066769286393a291ddf65242564e8338837a0704e33a9", + "0x1086bd9f1c781714f3bc91eb0f7c3173bb5769efd46534729de82e36f6567e85", + "0x06ae2c396593ef7c6f72ee27515ff8c568951fdc9da316b6a773c55c44acae14", + "0x844ac62a25feaf06793831b467a6b283cce8227d9064913662b7bfae37207acc", + "0x5690c713c5c832d318825f309de67d2f85c926cbfe73040526d708f2ed5aa124", + "0xeee32c6cce93ecae3b6329fdf90a98e852be8d4e99cf95c240eb0d121e3ed8e9", + "0xd7c4c7dbce2185b772be5c0d44f6d9631618e4c49cca235d290ad5f820afae12", + "0xd2bf5f3711ffeee00d4c784d26309277ef4fdf4e9fed1d832c64ca350d4ac1ea", + "0x0ae17164ace41c00325c371d1cb3b32d81fecfea3bd54e130e053447e3639ba3", + "0x4c7065efa9f9e4807b6881557593804a702e4fe3e9b30022a9442f4a41dc15d8", + "0x832104dba33f09402f8f0cc6d85481fff89eb28e7e21526c6697cabc0f188336", + "0xce93d5f5d37de086b905ef1f7b3c69d5329f2b08ea96bf1f1e62b827dbf7d3a5", + "0x60494259800f5b4cf629d779f901440c0c938c249becc6c6fbb244a701b6b69b", + "0x0b15c22d2264986c1b6700f5c18729cc49092216cd337fc69ee6ff101efc5e79", + "0x592d59bef4e34a7ad53d88043edec08050375a868d16f5efa43ebbe899aa958f", + "0x215f4894a9791cb51ffacff81666ef79b642f71339d15731e16a75227de5d02a", + "0x890dbc066302e41ed91444e39b418ade01b313b5e604f5a3a14e8e847211c2de", + "0x159746302c8b6a32188ea67524bfa1e3c971e360b66951b390ad12b574788bf0", + "0x9f399ac0a1aa0dbe3c17259ca396b0e5204126db37cbfe75237341612e5c8c22", + "0x979dc87be34baec5d66d6f07e496e355b23201bbdfe6e69730c81585feb0bb5d", + "0x91f93dbd72ca42fb3dc47da95d15bb6e8552f3bb45d64d58dcca6da6f5c2c594", + "0x00ecd50997966f02171e7fb14e5424b4e31cfb7a995bb6585d09f1eff15b9f54", + "0x50e06aa8732ca2fed034301ab5fcf7052f54cda399d73230b54e6e48854a3a59", + "0x9dfc04301936c0067c5b3b3fd0097e4c33cf13658697543ed3abbe8b90503f26", + "0xc99674be734d94964c770fc85aaf29a869bb697c379698b948fbe84469931788", + "0x910138df8b19b767a893ed98139c5aa4dc597fffeb735a91f558795a6f148a86", + "0x840e64e5b55995ddea3c438400be865ee7a8c31da0a54cc00cf620e5ec8c2749", + "0x932157ff9e54673586f4b693f94d7575cb5a88ff6e54dd4cd1f600cb96cb77ae", + "0x5482a7b48ddc5de5653adb9f8af4617e2870b21ee7a08e770f467f6c5f5a750c", + "0x4a26f81e4b0172a94fc368ca1b04dc272dd0c28404cf26683ab55ae12c217bb3", + "0x860f300489db7d6780a08489649f9e02d6cf89ca97eaa575edc10b7fc0b78b68", + "0x3521f6ef2a1a231a737aa47ced70ac4fd9c66b7faea7a22e44cf29303f9dd6dc", + "0x718a7d2d805e69cf2458f14a040bed18efbb93c01e9060572f3d729e1f66ec6b", + "0x7572128766a378ed4a72fd335adfac861ba06c0cad07a8c62f6550516e7c7f14", + "0x8269e23c9a4f07a1bc928d334bc34ddf7ca0ccd7875fbbf653590ddf0f98e7b2", + "0xf0664066fd331241b16b00439c7700225efb141b56eeeada3246c7dc7a9d49dd", + "0x0c88a9c07b2328b370834a05d6759bb47c73d52f6021b8d62d7c4c72161e64c5", + "0xf4df9bd6ac0366184beff3188acff4aaff3046a80ae03b982c15981526bebb96", + "0x46f09f05d35026797e347143f1ec55098f0c9c1e7d683a6f4d16f1223d33a04d", + "0xf704fbc00b773a7abf2a67cfb86af8603961aa451cce105c509a8d4dfa707d2c", + "0x1b6e6966fb8395bdf14452f9967dfca99769890c8712f5dc2bcc10a3e05aca0c", + "0x3807ec9c4a56721c9f698ad54d63a690144529b3dc03a5efd078ad503118d4e8", + "0xcaa687a2c00a8656adde839a7b8797c6888dde86c5292b578abbdafd149f1f84", + "0xfce67961c29b81cf9aaf4a0662453dc0d0983590ea64f69c7efe94f50bac0915", + "0x6597d47399e8d67f59b768a11bef16768b32ee8ea13ff3b7de59d90b558f3088", + "0xe558d7ecd15d69c3df9f1bbf461cba258378845992cbc0910712ecb7ef5254ce", + "0x6a7bcdac4cbfc7e8e024009817b6181edd7578c456a5b6f8c2a78d61803c27d4", + "0x39e2e1bff9220f6fcc3685bdfbb69afb77f2ff88f0fd07e6b339445ba0f05d2b", + "0xf70ff1b6907a2536a97b93095d94f4b364929f38f94e5245bce8ae801f9dfaa2", + "0x0fc6debfe26a161f4a0ae4cd41df2a03a16d3ca1e24846cfa40dc8305692c120", + "0x38ac4dc257ed9271c92cddf1479dee12da28527b53753f519c90e71ccb643375", + "0x08a1369ef8928b60b0cab16da8672674fcf1ba2c5f02bbd820fc9f330159ab8a", + "0xceb1cf00f3fa34ecf74fa189669c0bb4a5289717ac9d8608235f8c98bb19ea11", + "0xff01b384c71467a459a9ab5cbbadd6d14bcd619dae30386df59c56a8ed159e4e", + "0xacec9e6a1d849a1909905a6fab792521dd6d233989d7603801bb8759275f5a09", + "0xa6b8a6bfb0328690129f8abdf00be2dbd0e05ba5bb9cf6652f99af949eb00b01", + "0xb435914e0084f1f61f87f6012f30cb7df0a6dcd951ec223e099e6e88ff931463", + "0xac9ca80cb960bc59224bffbef71514e39ad031fe07ad665f264551efa53de273", + "0xbbc4d380b44aa29c9257d9490d5c4012ad86483884423f7979c6cc4b5afedc7d", + "0x4146103927925c6a7c32ef532cf56280b9515183322137d8a296f2ed0d12b667", + "0xdcb36ac5ec2223d3507f6992f763cc38867d0bcbc2e9a916317f7e0e041ea93e", + "0x216c198d0c8a7af0dad6e18cacdde40df21c4d8d8188712e8a40ed1a94d66545", + "0x8d3fa6511a1042b16abaf94b4207c48b408518bfc95cb576a380857ac5e77b8a", + "0x9e83ec7a20b841345a4471036a390fd2241ba708efe6ab27dd7a1c071aa74f49", + "0x6dae99f90116f44f3035e567f596fa4804c12c1e662f34099cde5753e9a08514", + "0x2da6ebc6d0ff94a2a73c0197f3d5ae8b4834c786502ce043ae459693de75165d", + "0x712f7cdd519f26bd9c5d85df14b260040c4c14c8552e25b146627e1574978b08", + "0x8a9facd2393e427559bca451045185b48c48be5422d8e6f911a2afaec8b839f1", + "0x1c2f87b5a2bc786393ec2cbe6fe7507c31e2f623a0d9249e53b5664973f29fe0", + "0xbcb95a7c018add1de9faa0148167291b5fe4ced890ecf3c4076a718253b32499", + "0xa342e2a2bbce4ab0dcb1b5765478ea752e0dad2b6a170d1cd5c56379cdf0dc7f", + "0xd463c251720ecfd776d6629f485edd46f8660d205696fdaf89dd5dade88c22f5", + "0x706f6f15c57af8a155807b6126155a5dc466dd5b37ec34b343ce2892afb6d458", + "0x9b9f77d9ab18220ddeb610494476243b4d535bc900bdfabc95df54818ea7eada", + "0x62efd9be7562aed275f496c174c13b730592fc1c98553d7feefe379104c51c85", + "0xe63e7f337234b3eafdd8dee2eff2f4841fb86afa2b570374e9cb969f0c078973", + "0x0791be5ffa4f583e7dc86ecd6c3b46c2b9b6f20a4bdde2624c4ac531773937b7", + "0x7315ad896fef695a1635c11bcd7b50656f6f2082e38cf15a3c128bf93b0fe052", + "0x943784bd868102c82f5b6cb93dc1e746d38569847385147dad31708dc1439fb8", + "0x68d67ae70125ce8baa059bc4e92179d465b8503a3e1221528906074693bc5a13", + "0x5193016bd3d72846973adc5b22d77be073f4591e18d64f4f6029ce682b3b5bf9", + "0xa84911a8839ea2bd59c44788bcb24c4f1360b6a90bb3201d8b0ad9909f3c1d49", + "0xa7b70f7cb81f6534740d288a4162375ffffb99d9739db738ff9d6d7fc2c1eac6", + "0xad31e18d3df71902a3d814cf105e496d32c751eca60c32b078903b3b7e661b8d", + "0xe19bbcd62a853c4257c19c64629e03a5ee8273e5de01d86a864cf79aeba4675e", + "0xfb22a5a5a8bb024a78bcce7e6c3fda31884b2853494bbde8db628e0b37583d1a", + "0x25f9f0d6c6d9e30206a493ba05c00e8fa1c9999763e202fbea12fcdcf2484367", + "0x75f944d20d393490b3900701f91ea5bb58e6054bb6c1b94145166123e1381e04", + "0x1838394dcba636d89f9465f2c191926feff3bc58236f6393feedac3f2063b405", + "0xa039dad3a4deff703d67b8a54f66e4983395f8d353937406597ae679cda058b8", + "0x9177935200c0f2cb10cf5025ae5907241fff3a3ca2e2dcd7110639966feaa866", + "0x3b33de4231779aeca2aa93728e94f5e6908a09e807df4d85c123fa0a00975473", + "0xcebb579a20f1d2a2c83f159e9cf10684957146d491e9171cd5a4f3bd315e0b17", + "0xd972f45846d6fbe4faf11eb0deeb90bd8a80d8ab21a91ab70267372d983c8e90", + "0x578edbca3d0beda69f188946ca7eb91e3eaab6fceafe0e4e909ae54ae75982e4", + "0xc0c6bcd5f3543bff3475242ba08d910cf443633edb3ada81a9b51cd094cf0a4b", + "0x6bf28f8d00b2d6af4708615f0ed9c9edef5664e6f21aef999eae3952f952f16b", + "0xa304812d88c96d950afb87a7de04c129516afd0e8694768298282125852c4366", + "0x226722fdbc845b7a6c1b3ac90f8c58b4c94bd636ea81559908646cbee55070a3", + "0x0fd46c77a50113fecce92620867b9bd0b0651ef3f212fd65f46185bd9ba70c0e", + "0x96fe5719d670ac0a7a1be382f3d249eaa85ed5e0c27c6ad9217cea37aead4db6", + "0x14e93f80c8eb7386c7b38a7e7600d1e6c5ea3edeb02164c15a071d3ba579bc9b", + "0x31aaadd9262e8605c37b811c6c648a5b6f6df174dcda6b5309d448e08a6ec0af", + "0xbc16b82f520f7331083467db4b45a448b904fa809a1829988590a0edfa41b04e", + "0xb6fc794d89779b58f03ca63814353f72c68c0bfe8ef66d10f70275ff7b459075", + "0x927a9b1beacb40d44049be03c4d10df1ba8ee3dcdb82d138f3495706d7682406", + "0xf4adc814d142d477b9b30408af369e17e78d20d9b41dab8395aae8572af0cda6", + "0xa13320c4ccd68a54254f658ac83d28c4ac31176fb2fca571616de893974a4923", + "0x0298671cb8079f35287c57135d06a52d8df0c0eabfb7338fd7d82cbfd20ea712", + "0xed50ce4477b89deb9b5a8f45ac42cc966edcd3220ee58209ace7bc5ea3f4969c", + "0xe26c37bd0bdd761610cb39540bafb07851bced9c3dd2217f014cb68cc394ec99", + "0x43c7af23f009bc0005b20049f5e3e4f03040ce9937fbb65ce01bd39f228c4be8", + "0x1b9f1382314e40c07c0fe649dc2f2b7ea2afa8d0f3fa7def36d14a4bc48707fe", + "0x8d50b02300b80bf8fade097d19b0591ac517bdd32771ec6d79cbf7cc41e81bf8", + "0xae33f7f250a9a72812120af8abe1132dd0d64a741dcbcb3ca9b7d50533cbd219", + "0x6928356db4da3ba398dea15b0ec2c1acf37ee1a73f047e9f657593dcdb14224f", + "0x0374673a22d628f301f227c0feab29d5b2cd7748be788f052c6c6785e3e4d11f", + "0x6011cf614042e0554a8a5c8f590b903808737b9fac6b2d2278e9a3eaeb0ffcc3", + "0x48a6f8d545a91451787224f7af590ebf1d870d3c93da1c505f245523b8183434", + "0x5ea89101641b480a94bed0010541743e1f570f811a7b266c1e480f18b22ce167", + "0x3d5923020b3bb22863e06c379f0d78ef66952646d8f562e578f43439d5760cf2", + "0xd9af887900ee9afaff4043535d8b1d608f5ec11a946ebe13d9eb162fabb53e40", + "0xad80c9484b8b92a79865e030a5ea0098f659a5da5934c38373754f62a3a57979", + "0xacde09653cfa47f92486e151f76f74b08228c1da0fa045546905d7361f49ffa7", + "0xda77ecdcfaaf602fc6ba34f0f492e4000fed05ba34bfdcdbfd62602789ce0958", + "0xe44b39aa8b8398314943b3f28f39f5863742fb6a9a3213ab1e748f125ca8a2cb", + "0x387543a0975dfefb6580e7400c866478951e272654bb1c3fb0f48d2fb67d637f", + "0x8ea89124181f321b17901e7c404e007b1b9843f0d50b60e81e2caa5619455382", + "0x87d2c666fdd000aa39dee0a215f0234b4a531ba27cf6de95fb8efa06fe5c05d6", + "0x0e482abab232f119ae6260dece04de38d880ffcecb19ed844078f4bbbee5843a", + "0x434571f6b5f62d1fb063c9514fc0c20cad67ac0540447ed1f555c5c844745aeb", + "0x73a68868ac6d188805c2a7ce251b11f175699d3ecb322fc169db2a36228acee5", + "0x7090abf163ccaf07150326acc8b10da28daa525a4456794b3b067da03358ea9e", + "0xa172ceb41d259bb815be2620a6575716bd8c2ca3260c1579f5f4db96de74a550", + "0x17d1db778d7de5467299d69791acccd7fc1e6cc1e62dd378aaa8438c73d57a18", + "0x1ccc72c36893e17c23608ac409da56e192fa8e35dcecba426b0bd17d369194a1", + "0xd0440baa8aedac9fd0308e9655dd04edfb22f1ae90cd35ae40bbba137a706f1b", + "0x6006289fcf02ab153536156ac6c439b0073c1b49c69c06024e0163516404fa3f", + "0xc12516aa9a31f679799527ffa02f2e36d01c3be439c3445b0c6b619673f4001e", + "0x596211c275929bd7d3639c48dfc011acc792067e935c3860cd17fab9a006a821", + "0x5604af9a2121980cb705efbc3db163a04a88bbc39077da35f7d060708da766c5", + "0x523a943a001608d1d4268c24b5556b849674f4e5dffceb592524dd8a31f5655c", + "0x8ccaa03bce55bbd29af0b7ec4108ad06893a619cf9f2e286b67c9bf0f9f84ed3", + "0x3ccb6f4346e20ca0b6e9ef8dc0a7c26a54a0f61daf8efed0b24054316c03a4a4", + "0xced04f680fd006f9fac6266a764ad8482d19e8e4109b9fa82f552190170f552f", + "0xb38ad2186868ceb7961e40ccc3baaf7cfbda877452db678b822f05915f465600", + "0x57d6775827d0738d41b290cd77d44a469443745ec70f25e9bab3a7f188f131c6", + "0x00ee64c1281236978e87ed2e2c7fe8617b3c05ced0b82e79cca3bc660b019eb3", + "0x8b7858e58a8bb34cd3b9340f03f78b3132456a08bc83a066be417c3d82204818", + "0x04495a386bf8af1744e97aef743b73834379c5eafe077f030fa9bfa5a12de65f", + "0x6cecd87c18a694a3abbba9de1ef0a7725a17099452810ffcb1fd035d40a93055", + "0xa2d750ac6ca67c801bb951a72f857d06548a48295a519604488b6582b5f1f6e7", + "0x064aaa38d98b5a879cf928e7001a8d62746c5b437a165038365eef98183dcf38", + "0xcd078fe03d34471f76aacfc022dd4f122a755ec5554cf478f73d837c9239e001", + "0x1ece440fc142057799963f99748f6895bbdd4c47d51174d4b0197c5cec2a62d1", + "0xc3bce397e4f69b3f2c6b1cd615e41613b8e583c2e1d298d817101aacbbb0c815", + "0xef1a133311acb2ebd92a7948861718134838163aaa5c2bc7d1f6b17ca1477707", + "0xb1d4d95a177d0f3afd04158ba34ed155913c3e712b8437867368348b91efbe62", + "0x88fde11369835d0240f0684d957204fbd98b935623ca0265d23a858d0e2c2135", + "0x6a04d9929b6919ed8bba862551bb74b62b24fa7c509428d9b5a6d896c40fa158", + "0x7f6fc4793630315dc4d347ca71f1ca722d11bcfc9c07636f8d3a02b9e6004aa8", + "0x442f229b6e958d99d89dd0e938ad6e3cb5e84d4e3695959cfd18225954753448", + "0x209aecad9f4a1fd73802ce869b148307b40e84dc4938154c60a68347e62dde17", + "0xc8941bdf3aa9bdcf3e25eab7fc6c508e41a0d403c6c6abc5bfe014ee7cad858c", + "0xfbecb9499e48e44981294fa5fe56b0cf065e9708f7ef1be01079b0457930f86b", + "0x193fb1858276c31cfab556820dcedce66b1bdd7ba10233592b43c826951212f5", + "0x2adb28d6d7823ffc98bac6721e1977e4371486a14a5b63c6035798306deb1435", + "0x3e646e646e67196b4adfd6e80b652b60cb4e1d03cbdb5a7d5a139b744f025bdb", + "0x3ed88ab755080aeb98f675a17c4ee1390032473882d5e19becf33883863575a6", + "0x7822576eb4fa4065de22a84c487c648d59577a47c086cd79e2c5d59bdf708d97", + "0x640aa8de2f4f2c0afdae7f8b9fc8e4540e4eec3b0b8e31b4a0a7b44c8887e13f", + "0x76ccd8d5a1eed96634d6952796d8237c7a8fb56a82fdf34891cf4af72136d5c6", + "0xca2670528f53f99070549aa8dd9aaadf125c9ce1341650deb2b51ae236ceb16a", + "0x0e71aea4d47f6b9eed59129ebf245699bffdc425b8a6c0490e35dbf1718c16db", + "0x83b3374d33a368fe5a3864a713defbe6377ac69d1b4ca1e35264e99bcf9df96f", + "0x73ed0710aca1ae32c1a9f4f9cd247348fa0f5744fd406c06a75580ed019e4e96", + "0x6dac61116856a2f3224114ceb46232e69f922ebc4fb2459cedf91e27280ca677", + "0x72926f0575a84d36aaa53268f46d2b2eff9ee4ef0205844b4a49247eb3af77dc", + "0x2a504cc7cb17b15e29c7d984a8bb872c350b61000b6e0df4d9102e3c9f7edb26", + "0x0707ebbc1887b3fceab1b585ee8789cf64a7a58c768274e1bc8762c412e1d2a4", + "0x4e543d33dc72f5b0241ac941b70b479f88bb1509045c454ca2e9328f9bfedd6d", + "0x9931459fe7f4b297529355e1c2e632e561368480b543d6dfbb57398caffde564", + "0xcf8fe41e8394694dc65d0729b211ae5d8c1e8824c8baaf83cae452c8f8f1975d", + "0xae3f68908d41265fb61f8834129ba78ac64fcbd3dce44aa8edcc5368a3c7c7c9", + "0x2152445c6d1983979ed40b1f80286db81adff747ab2a6bbe64223d4271470be9", + "0xe61e23bf479ae8c6f890bdfd57feef2eb2e7b9cc0ff93a61092031f5538530a9", + "0x484deda87dff259e6d826e04035fa9c1d621088a3b21f624d63d30fc1141a774", + "0x7df38054c70b3bcf10676b2e280bdc01f6d7ff9c4df7bf07c65a2d43bdc0214a", + "0xa64e234de34b0ffdf8d9719dd98d7f26f468868016689528f4281535bfe20a97", + "0x8b48d6347287a6593bccd31cb0dc4f8711f697ff97a22089fb8fa82734167001", + "0x7fc47c5f939dbc036df6f0b983b95f4c369e89e038083035396cdcf0516d1b52", + "0x21ad139362395518b62ce65e34e53185122240a04c69413f6a1fcf83f48f6578", + "0x5ca07bc9e97ab9b9fc3a20165a0798155c1ebd847b18e4f3c1abcbad362bccd8", + "0xec34fe821dbc91b6c6a96bcc63cd4e987d150a614509abcd9e467ce9a27faa7a", + "0x38d14496e612a8d9132a1f68dfd99ca50b26e8a5625e7a1612bd9ee172e4bca3", + "0xc04abeaa50a782d67c990e17104ff378b3c0e46c23516359c4343f7323334e20", + "0x1992ba8163a01e176647fa2e60bb5992b35ed4e08027f7ab0b6a1a8685dfe5dd", + "0x54cb24d54bc6fc85600365b71fe27fe92b8ac7881b9c95b05c18fd288bbab016", + "0x57f487aec691c64c0221027cfc1b5619348c862bc0563da3e5c778c3b34afdea", + "0x01dd0bfd1fa886d2c11b62b4c7aa57d745ff18e4d15836746078eff4888e31ef", + "0x5a7cb8b8bc084516b19eb51213bafa88a187f8b6d7ae5d248e0bd994c33a7826", + "0x9452bff3b395b0508728d7bfd168df9af57c8615efbadaf51bf3dc694ba66ace", + "0xf78fb075647e7bd6662cb1739a186dfc589af29da84c4eb089eb8be22b2e41b7", + "0xaa83a2efb47807303959dff0efd6f8c6e7a99f778ee0b13b49fc0887df3a9c26", + "0x19d85c1e8754408a81bbec822e6bca97a5387660fd788dc26dd0333ffdac6a5b", + "0x87b32ba25dd3b316d78429c669588226a3affe799e608711b623569100267804", + "0xe1b8be499cfbe7e083de23c9ffe79c38e553173846c67c8c7fc0307d7bb3d16f", + "0x93cf57b34dc41b0e8c6315dbaabdab8c50b65abb7059dc445f7bc2391301b1e5", + "0x60adee3020d7e66f1afbd9d5f6d739a6db501ece380c37814f25c3598c0526d1", + "0x2477ae188947e9a0a161b974a54ccb8682c7bc1dd982a26871c286b0b32cda72", + "0xdfe4abb2503fea58bb0c3abb4d8e68a0aaad11cf4ec23bc42cf8813be83578b3", + "0xdcddfb4a1ccd2481d1b8a569e557e8f42f8569de2d2fa010e3b1dde43a179aca", + "0x2717628d33760cfaf4849e33bae16022df63b7edd538815fbd330ce7343f4815", + "0x237a6066448654be67b9855511c7d17e746cc31bcfcb9227c9e402f586ec36f5", + "0x0cbc3baad601538faf8697fdba1286382d661f97deeb8d147af9743a594e7a2d", + "0x59423d95f7c6d8bf70a879aaf7a1c44c8bae596d6ca173676913bf13d4d9ebf3", + "0xa942429cc8c02bc8e6567b08a57ca3482e910eeb3425b13c78f725ffc23ad301", + "0x00da56ce380d35828241e9bb5695b1ae278e6f8232b02485b078ef8490fc38e7", + "0xe1d1f726a9c995148144641957c70046af8d9b806a9b50cd1270795a07afc063", + "0x0017b1d88c7b03baa1b67dc4d8cbaf54d3e587dd38a11cb670b2aafb1adb3059", + "0x2012c7d5f6ef9a739899946b08d8f2511d640e69bfe5bd30bf891099db06bfb5", + "0x94c5961f1372d0f2dd3cbdaea243e632501c4ac4d0ded37d4d6b0e5fc86d5841", + "0x31f1b38e46aa457b324b7e8a6a529491878ce829a9a84b6221269027ac2ce666", + "0xfb368700561e55f8d31277aef71199901c170be7429c0365da903ef5c07c0979", + "0x045b8bead0d86dec2dde29f46f77448d7087dc852a76d8264762b36560a12848", + "0x1624d6027093ee2984224b3ac6217d670e0a47ac42dd111dceec080e548312fc", + "0xe0c5890195fb6267d297dae8d3142adb8b6383d6481596197d8b61e9efcb8683", + "0x12b367b7a026ca25fd1f2ef6ebe37b5641acef1aea579687411b397ae84cb440", + "0x4f59bb9446c37a423fb270833b3150bbba974d205b53c5eab777fe848be1b3e1", + "0x692fa6d4432c43323d74970554ef020620cbf7a4c0ca26c8a6aacbcebc9bdaf7", + "0x71c8fb8443baa8590b74a4243546b5ca32ee22bb37e28af10c763274836262d0", + "0xdc3238d7bb2161bbb1fe4e8795b1bc7e8c3cea2464eb5a1d26951fc632eb35a8", + "0x25356b2af9bbeb670e0bf3000c2d134ed35bf9cbd546abc5040430bed7da620e", + "0x69b6fc8efd0a1e165761ec2222a44fb68842c8f2c5cfa8983f548924e4dd5304", + "0x26906f8171353659dd11c990264c5a7c4e7b8a424557480618c19b303633d390", + "0xd7ccc0c9a789ac93389afeecae8e4e689727ab9fca029cded62eaf157a5d77f7", + "0x6ec497c67f9ed9bfdd2ae443b472251bf5c824f94a5ff03d7a7f30564ff6086f", + "0x2895a825c098ade9e31f47a6f8cbef4535d06c681f56d197c6814bfc490c8e47", + "0xd0916e5a2cf6efcd3b6b04596be84d42c23dbb62b26a21010ecd9ca84fe5abda", + "0x2ecc38180785f68533ca1775d581b1df0d4b6f2bffa617ace57bf6c434ac6da5", + "0xcbfca470566cf1da2bb71370cba6f90a57ee7642f8f36d2a21ffe02b8b05f48d", + "0x3cb39475b3a7b45efa6ccd038e3fdc2bcdfc0b65ef712faa9447a62916dce288", + "0x634fb755af99f1d342703d865dd3b159c32ae944d1f7a3d903a2953f35881e99", + "0x5eeb494a98a3bb3555f7455798853487f8f2c12ecd14eea0c054fc66abc9d772", + "0x7f2c46ee9dcc2674127c9e977c2aab0638020c221327e306e7276c6e315ac139", + "0x62d9291e43a21a1cc736e3263d6aae294da1061b002fd59a082cae0761124e24", + "0x14f8dd404f51f682051b06997fd2ac487a3f2dcc714632446067e97a5bfae684", + "0x99f41e093403811cf7abed47396eec5523d14646a85f328c76280a4bb14d3cb9", + "0xb3a9f111d239f14bfdcd4fa9c35402dcc8d949ca48c326e6875b676299e18f55", + "0x35c27c5ef3200207f73b7c5ad8bc9908e93b4a2ef018fac3bd76aac0601f7a1d", + "0x04256dadcb96f7553ef99cacd2c2decfd9ad83d3eca7179a983951bb3edaf3a2", + "0xa679533853f281f0d22a277643f2af9bccd04219dac2c61d453882613fa4db7d", + "0x76a1f5e8d22f407eb822f681eb5be8a9c6bfd6acc0474ad9373b6f3e445ef0f9", + "0x0620d96d14485c6359a35f4fab84dd0e382672021f30519f918afed4cd994482", + "0x61295c1388bcb587f9b7bd905bfa3e3c5d5492b52bbefef21f6a872c3de17412", + "0x181426166e4f89425616b8f384154bcc8a941a47751468853538fccaa1aa5f01", + "0x8834511b5db3f5fae5346ed791fafba7d4b59ac80436d61d0ac94e909cf15e5c", + "0xccf1637b9d05860ba16f3bf841dbb4d2fa41433811a5e270a112954dfbb560f6", + "0xa398ede9ae3426c928244f2066205900d5ebdc9bb85407389a026774e83df783", + "0x77bb863b13b46c80f7b47cde6a54b45b59dbe458db7d54daea6c5be0f74107e7", + "0x76d794f68117f2b014320a560ff7c8541a0db8ee0ce80730257688c3a424435e", + "0x74defb23673cf7464a8620664e78bd0e53a7a53896e1564b9d553a3ab54c7aca", + "0x8cb767460f19509b1b9ad33ac9dffd12ea2ff67c9300853c9334c1acd3a726bd", + "0x637acccaf1364e00f36d6d028abef67edfcd5f944156f67037659ee4c766bf3e", + "0xd012203c62d28a5f415ed804c850a9c5b7bc6c9df6f512d8d931a7da29bb4338", + "0xa663180f77739aa22d07d292d20631045a91e5d182ea80ea12f93fa748ccace8", + "0x7a53288cea1665471ed16294ef61cc11a6def53b4f5fd79a86974bcd0c19c139", + "0xbca17065ffc0e0e5a2430d671468dfddbbddcca41321fcdb96bdb60d6ddc4c15", + "0x30062daece2db158a7a226e8a66797e90013292ac0bbc93f9c3ad0cf0e6df7ba", + "0x564a140b1356a224e4da0faa8a907d0a2fe460acd2d5a1fd2f83e8cfb153cbd8", + "0xbb8d78fab384a9698c5ee49892830d3d56edfd22b26a15e640b23839578a7966", + "0x724e230db460cf6a92ed70fd0479edf4408873befd350f8abe08c15febe086b8", + "0xb01bd5f4a397980f17410c010bdba8374c6d5ce26e1b806e93ac1c4cdbe698ea", + "0x99df8a722e0ff003b8ab4ca508b4bae7aaca60df3dfc3a0504ca019c6bccbf6e", + "0x0921c7a62d137ee06d370601ba87e44df8054d26353d2ecf5e63a653ef6a3bfa", + "0x63da3a84a4b5bb61c15ea4b89edb86ff23223ad5764ba87b801ee312f99e175b", + "0x343dab2d9a7abe0c070f86aa54c44d480fad6995dcb440721a66753fa384e024", + "0x66bdea03c25e5809a873b3e0c15c5ce1c78747965c32abbb268b743ebd74ccf6", + "0x470ceb6b278a61e088e7a7c7931e5086e0b329f0a6fee8c633c30fd225924239", + "0x61bb88e0cb7acfc2a270c46221acf0a76a4cf8b43b78c1d0cffc9797dada3c48", + "0xfc53b699d354bf2d9e9ee92ac0d4e69d970b4bebd0f8ecee29ab108497676378", + "0xd26dcf9ef8da58a207dac6fba8591e58940ccf34b008251463045371a7c98b00", + "0x972d06b11801b52843903b69d00ba9818d38a9b566ae84e4dca0ca47bc6a0b91", + "0xb7b0e70fc7666b385d3901f57f4657b12c31e9b8eb8de519201b4032dbd7ac13", + "0x76779729e7d3bcfaa84b63080a4575621683dc67f7cae15d525f6871aeb2efeb", + "0xd667534dcb42540246675f8cdb124a1cd2e3f1486de684b722b6fbb7229ed675", + "0x71736c95e5bbb48e9162ddf7a7293c1e9b36dce5e3b26a0951c3ae547e5d0361", + "0x3cc24f931833a0034da3855074ded0cb6dc6baedd82919f2df3a0d34313859b2", + "0x08938f9a37b29c22dc5c43cdee976ae4b988489d6c4eeff607b24a3fa6f9b44b", + "0x268654f566fd87c943504b35c4d21aea04d5cce6809d9d10316735a0fcadcbc4", + "0x5c60a654b2d6be770da5fc105dda92bc0db9b3be798810df945d655ec4b7c4b2", + "0x20394496b7fa5ba5b025fe17e29c6095b83a0b329eba7e21858d4a8b46189784", + "0xb95f9c68c41ac2de8207e41a15f3107e3f2210951da945fb993416c8f772fe9d", + "0x98cc221e8ef7a3a92aae879700fb211da1bcd245b072cdd5c53a6de1aaf59146", + "0x76a0ed887e796ead0a450a19df5b02aac9433150cc01608482a123412c1fe61c", + "0x17226cd70f53ccc5f41e28dd96c41f1438359d4fa62dfe505a4b60d83b08cd09", + "0x3f82ddea1f7a2c64cc8112ec4db323010403b26e44081a9b067dfbc9b578e220", + "0x0d6287ffef6399107a3062e8db5c55523a2507d5045b3a84417bda6914bba81c", + "0xe84780c93214d3723ed8f3e5ec9ca9f434c3c9ea6b79ae83d51f2a536914e51c", + "0xf01fa3ee82a3dbf40a7e5f5b3ce52bf78648d7645fcde0573377cc22077bf4ba", + "0x2a853fb0d80a1d07b448b0ae8f4d73bb64c9fbe85873b41e649ed11430a8631f", + "0xe964876f62cf58e8ebd4b70dd2ad9a57e50adac44d63f2945a40f54e0290b8e1", + "0x4ed8631328aa0d68eaf105a1ef31e22dc9b3cf3e44edc20e8265f68e529c6740", + "0xbd971608e31e4d58d183b81de2e197e3041901a29266f687630fe6bae202f480", + "0x4440f1730baa1f4f03dde03f21fe1d3615dbf9323a8fca189444e085e029a707", + "0xcf930e97a8efd6515964ed8d6351f0f859843c62d057fc136aff3dbe415e1ca1", + "0x9a6c91172ee28f6cff6080bcc8fa90715ad54ba9cd0b0f26f540f9b185a09cbb", + "0x86f0661a87bd52e96d88bdd2b446777f2a6472507bf0e8b398c45c867cb30601", + "0x32696cf3fa1214c9a5da73de340bd25a490ec89ca1690f09496c85b2ca89b954", + "0x1bdd5fc95c11823808b7affafe6405c96799a659774b4bf9a9f7cff67f52ec67", + "0xe72976ff3a4bc62cbf243f0b110bc406bb90f1ec04ab3997be92069070fbcd83", + "0xc68712966a98ad04174746c0da1b0d69b8da8b7eb6f3ada098783601d391f4e4", + "0x0093897b258f84efee5edc65adb982d951617d379079ad960b57987950043d2f", + "0x692d943d742fc131f82d657b954b2906312a5b0b3cd627d329cf284e66771e67", + "0x31ebee929b97622e9567b585a14a714787bf745251fe51f7d5f88138ad93077e", + "0xf9d189e5439fcaf88f89d67a1ae7bef44618c746584e124801f4270acfe726ec", + "0x54dce5cab65b07b5bf37d10e3965d326d464eeefa4068b1031007ef77ad2fc48", + "0x429b3907b9b020a44e10d6c620e64d1e88878d71cafdae5c946c66a897ce3ede", + "0x51285d83a160cd89d0bd36fc48c926919d87e82e376a2b3fcbf03add9b7624a9", + "0x6199988094cdcaa05282b22152d5a82c3d291f9336ef4fd637203ec13d9a91f4", + "0xf9c4270a744927700101abe67eb9d5c146afc14180e073f86f9293fda924b003", + "0xf25547ca5b919891cae6776d17468739180c0354b96653f3e156d4e4f135c27b", + "0x911c5ec523a1bfc81944c064a01493b52d1eb2be5df8af4f30139698e48c0753", + "0xf0d2f628d7088375b9a545989694788e1999f103d6b196466f6ca2a96be2d685", + "0x5a458ff3ee279e50f52ea580ae5766765841e1653499d9f0234c88dc8ccf5e6c", + "0x1bd6428c89cf06056685ddbb5205b9991bf8eeaac165fcd02a70474e03cb13f4", + "0xf2f35fa38ad9828e19c604b52ac2d4ee01b1088e66da3f52ca3277fe0ce0ccf7", + "0xb65ddbb0a9ce9edddf09532db54b56de553751be8aadeca813598de56aa12ec2", + "0x8c0522bee26c98ec2a049f53f343edefb809b921875931a5d47a0ccb5b285674", + "0xdf4749f1a994a965170131ace80330708f2080eb72dea426b45804a038e77b83", + "0x31ea1fc329fd171020d30214a6f2e8d8aad482aafa3756d1ccfaed6a7054a73a", + "0x3011eb62acb861c1afd226a67626736926c0fdf3d7643e7ba55b336d3c2ca803", + "0x6b37063044fa78fc11ffc8d96522e1affc6b06f2ec9379143db16255c781c189", + "0x65665b162cb2286ee89eaaa434099f9851110b98520ffc4bd710a31f324e7c34", + "0x1c424d5426d8c889cbbf3f64e1061127e5f05ad31c055a55cff8f2e111eec5bc", + "0xf80aa7414520506bdd7881daa94813ab404851d81fa49147c8c52256b1b6b3fc", + "0x66bfd8cdf3a00bfdb073ebabbce3471205e1cdf41396f9d1218ff9e540ee50e0", + "0x7c66c0ccda74510c792257c026fc6c11fe51aa090e57f211cb2c5ccbd597cfb8", + "0x2176da0342f0c9e44dd7e3bfc26fddaedd3b1b9f8ee18c0bb5ccce8b139584dd", + "0x71078aa1399b2c0ce9ba3732bb8672cd0d388963ede80388b06456794b75f821", + "0x19e82a443d71ad34302eb753a2452428da8c9189ba9e16900bca6cbf01210773", + "0xf41c47fde2fdc795dba9f0997eef27f66ae0280621f1f396e3fea8906b4742a0", + "0x7f431c087466be3fb302b3b29232b271d0a18b5b504998e62e8a7f578f918ac0", + "0xc2cde91d75405dfdbaf9e4a66d27a964186af6857df952f69d3579975beaaef1", + "0x4dd76b8ee6875d527e55a6f5c435592d7122d51a34c2b880d7f2b899559e130b", + "0xbf83d8bb30eaae40abc74199a4c61bd9e4bebd53d06cbfc78d74e7b2da3f1a3b", + "0x0328cdee2f8e00cbb2146f83f1d93ca45a2f3e49fe81d110fcc4ddf4b0653f97", + "0xe870a3b5a3f63a5c4dda244538efb56b21436b5f0313c7d006a105f2547aec56", + "0x0528242ad6a1b56e2a7b64640400e3fb5124093ae0369fb7996cd88a4a6c7e5c", + "0x347035927525fea4b5b88b4902fc19bab0be35143064f9bbe855cc46c8d7dcb4", + "0x0a70622fe45d0c3c5b04380b4a3922a47abfbb35157ec78d1a222df59b0309f5", + "0x9cc0c92e96f3f50b9cb55d32d7649e3c20cd71c8fb2c2b3e40354ccdb671df64", + "0xa48d7a7b3c9bd52d9e115febb65b56e2f249ce84e664b7527e904e1de6ccf480", + "0xc89ad4fe8f3e284309211b49688c13c6096a8c04622a68bfe2411274eb3c905a", + "0xff3683a8d4e0f7f457b9a959fe3db9b85cab98b6ec5dfdfb5fa601f697406ec0", + "0x2a7e62ffe24e80c510dd44ca3face05cf144df20e3275773778d34162c215cf2", + "0x1be9df5681c73e4bfbc807a87f30007d5331bf4b6661d342cda4f8abe627c004", + "0x1ed82338c539fd8236b719167d22cc0a66d1603ef217e3712c6e901201142b72", + "0xccc6e4768331cef98916a5edb0a1c3571790c24f7022c9360da1294931998d94", + "0x27d35c52ea68cb4dc3e683998ba4dd439193a3604f164697d7bbe4c6b81244fc", + "0x33d994d5823899440dbee3d4ed92d64f8022e596fadcb263447f55bd72a43554", + "0xd35d8da8ef6c9faee767754d299828e1f92257fc3eb149559aebedf9a96a2243", + "0x6fc6d929c57683664b37f8b464905ff4f9b71172cf70b72904490839eb3c635b", + "0xce23b30edb6fae18ba0bc82f42a0e1d621f6f383189c36f63c2392c021f89374", + "0xf5c785c61ed9dddaa7ce2d043c9956ebcd51721a9df3e3420ad64be8ff2aee98", + "0xcbaf9bcfba9f8dc7fed4577038940feaf530309305c38d963d29c847d69fd1be", + "0xe7cf49f270b8d129d6dde96fa3fea524c3388f53e3aea485db252c65a9318990", + "0x4b0423bcf1703c9b1364e381bbc063a3305f36b803986aee55532191b4de1e18", + "0x6c5006b37d2fb1843edd6dfad4a23882ec6baced8a1b628a552b56834c910231", + "0x74e60ddacdf14a5522668a09871fcf0fdf05b5f6a0936d0235b89ba7832f4701", + "0xc023ad36d9c33dc43560bd4df7a8b7c607c0c84101957028f29c8c7f563e5a7c", + "0x1ef29703091b1d936ecfe5d4a8f7896e3d089fa743ef2b1bfa084e5fc34faf4b", + "0x740c2f18c4bf82f706052db0673fa4ab65602bd4035f07bf1c710ff15566d53d", + "0xb145111cae639fa03f36ad476b2882834d7aa8ad52526157b1081e4a0a1d3f6f", + "0x4cbcd01856c3384cbfefb3aed796d52de9492ceec01ee9c479df7c949ebb2a0c", + "0x7c780820113c6be42241999d118ba7f39004ff6c4b530a0848434ca040b81bd7", + "0xe5aef0089ae7cef361e60bfdf4e26e5900c5d03b9988988dcd2c908ed82ae623", + "0x4399d8150859f85741948e0e854732ecafba2ef26f0f5061480706db79e8b5b1", + "0x8c14c43d490b04d9eec08e04f742f5a5549cc490ca1b0ad06fbef418f3f565d6", + "0x510828e45635871cce5beb8a0ae0ceeefb0085d98652f34ccbcec3eec72e838e", + "0x3c0e8ab3ec7cbb1c657b20d5da1b3ed9b525bcf32450b0cd124c101f3dc6c760", + "0x59d6630d966d35ceddd7e2102395906f71132e664c56d743e56aaa729d7f8401", + "0x03fa251c4190537609168a404f6f40f13bc4be4c7bc9cf431b86ef2b1f38f71e", + "0x0eaab9f6ea6763b2679a1d9bd65df404c244b4b5c5a8ece7663d872d82a17ddf", + "0xfcdf127652e21f4d8245a9e45450c907f4133b093b5570a693b6b493fa17dd52", + "0xacb31e0c83344acaa06b572568c733268fa05fd153172a5262cd69e9552e177f", + "0x9290f32e50627fcf9747e27fc62ed27db66830b7d062e41572ab6fd17cc4c16a", + "0xb1e2d9fae85f54a998a815ae877295006c9f6d73ea84ec124515f8d9ab9b742c", + "0xd7cc538f713a92d5abcf9befe94a660763ed61156d13cfb5ea724698df71a885", + "0xfbac48b95b4ca05eaf970daa6ef49169a6884e7fa80ba3056f882c6cccad1747", + "0x5f193c4b763e2cc7c7b2b637b28c84c9d17e36cbbd6fcd95e872650251ddf7fb", + "0x4cb1313975fafb7f6d202ad021a40cf3694de2705c5a16e355b014d2dad16594", + "0xca45c8497e931895d8685e0f4af6af4231504377ac5e8423afa37186965944ba", + "0x0061dff27d4a1ad4d2b061c9f275076fe95a65be1924471a94c94675bbb25c36", + "0x290001ee6f4ca904d9bc097545ecca6edf01fc568e51f2b8d5ce2e461f7ac1ce", + "0x76df987ca196175e664f671f9b605273e5d6a24e9deeb08c10a1c2596ec504c4", + "0x5327ca776a2f595dc8f1b1207949363a183e60cc754c9a87b486dc653838f013", + "0x754f51b2be808eee194b09e8a656d07d893aeba8ca158f06d187feb6025e0450", + "0x820fe5e76724a13f3087ed88db28975299903a0109293f0a4aea83977bb2f540", + "0x34eb74d903bf75dc21dcd7c7ae36c31ac029dedaa5dbd0b0e9ecf2cf6a2dc3ed", + "0xe5fd788bf09d234b24579639569677ce9c2c1c8f9c733813a91e9969fe756200", + "0xc8d22046be5c94b225449f9466eb37ad1fd541cc1cc60db5a8c7f37329346d6a", + "0x61417d0ced9fc8a0f118e59157819d2d2713d0a45b7189e91335a8b6abc6f222", + "0x8f7a7f79d44fe5dce6cbb0b7c7f9d189351f999dcfb50e33079bdc2324108a40", + "0xb3952b43905c9147a5256f1c0efbb612558a7ca693497cbff997a37bfd8d12fe", + "0x5a86aa9d5fa938ffce0a54d040ba5203c1f606035a67bb26cb04e721f685a98a", + "0xb5c0942c1a12866acd3bbc53a921d98d123b952a9a7bbfe84268d842d23e46a9", + "0x186942f6c748eb5be3723b47b457fa912697996d0958480b8be6a027247a032a", + "0x14e08efe536d1c1152444b10909f05a301a2492e8cd21b5b4e6f452e2f544e05", + "0xae072dfa97e16f42539c8898d935e59401c06a8541d2ca24973e4d5cab749732", + "0x54bb1d6e4c0659c94f5980df2cf264f0c71275d14823f0bb44a8d48b4af254df", + "0x0f33f3f6cf9f8dafd21dd3c6483fee271c146423dc4b7d94b2445043c71b4dd0", + "0xce62b4ca84daf821a5285fabbc8318b0a02a464adcb6a3e8d76403af1a70e346", + "0x814b6d18988b7b2646a6f455b8eb232d3618a3214f5c1d164a88b3e0b58f9f3c", + "0xd95c6383f7c45a391caab9979879768829049689a0deeac30d30a70fc9ebcdcc", + "0x219cc7fe638837e1d4818f3c929a7b7f8568da7196a43d553f3c18429e112efa", + "0xdadcc66d75122a2b67ed1b1b3f7c38a97a494d08c87ec035057bc6364b410edd", + "0x8b9ba293432b9f32fb4489c4a04beabd38481e25763c8cc4632389ca5ab44608", + "0x1826050abc7a0ca6c0f1dc5fccb552e88b2b0d0e32cc4f17963d80d135617acf", + "0xafff27b0f61fde2f47e4528fbcca266a55fb2980056c7675256b195e2fa70c16", + "0xb3ea0e3e1e41b2f818db3d838bde8a40938d5134db7318c9f1526d69af2e7778", + "0x6286949aab6b25f47c3ffa05bb6030c1352f57ed957300530da633755e7eb735", + "0x2f64cb5447c3f32be7899d61efe68ed0832c60ae6a62cf4a22ae5e328e2ba398", + "0x0a0f6dfbabc694bf4876b5d0f38b49db2ef57a80f435dc7d8d1149d99dd88610", + "0xa701424dd0ecec94e7b78bfd2305d4665d67e3dd81cd079a9a861ccb5dfbb693", + "0xe5459f08088e70567005af63af14e452276fd63d0ac36af91798082a7531244d", + "0x834be1e42e783e86be925fde8662fcb5dba521c100f3b4eb01a9d14ab01d1e28", + "0x127d52459598b251f4019926fc88a954e994170ce2ee2cc08f19eca0b4b2a9ce", + "0xacf1429b271b5a3d61f6613c82abbb2ef472405a6e6737853092fdfae3613701", + "0xd3a1ad3cdfb1717e778114f4c34d26e80ed51d9f07e85c9b9f1d84cf56c4fbf2", + "0xd707fc8c7b35455d96ba89d546cf872f033e7e95f123b7d51bf16bdda4b9a87c", + "0x856538795bf5b782b917465e5a736f300d4158e7b413c594b03119f577444778", + "0x10ca6137d8c727acbfa588b0926877d8b1192e0e38df12964c63fb393e8c3f95", + "0x5df55dfa1415edb099a1a207cfa5b7185fc68289e74aec2f1d61e3ff5de8d787", + "0xb61efb2ed3946ced18013daa220f367c3d3cf3304fd653023ec328f8fea4f553", + "0xda17b1e881cc52a197813b121ac74e6e8dc62ed760845b1de65ef97b76eec6bc", + "0x99c22cc4bc23d34ca3e6c45fdb359310b41d29f4cf5c8a96958eafa379962582", + "0xc1d9cbdf2a6552dc32bd1f1a8e3183549787e226f52de01fdbc74cf64ef684a3", + "0xfd5a6c2f6cd4dac77882d562c7cf1accb6bacd264ea50815adcba2a95dad0193", + "0x943f42f53407953d2718012c23d00832e19b6df60aa54ebe34969feb531cf602", + "0xaf2c3819938c5c8ac17f637dcfc66e5d26d2c35ee06dbe2ba852254bf10f90b1", + "0xe0e557a3dafb52bbf79abb31fe59b217bb174a7b14ee42ed5ee69439c557ed1e", + "0x41fc90a12737a78330745237292facd23f5a25917bb973b87b8de61eb11b5ec8", + "0xc000b86a8b912ef3096ba95b82510e246cf68b7be9a88d21631975e4929841b7", + "0x8a0000d048a7e60f21ca1c44174eca3781333596c92aeae6ed5c24e729d004ec", + "0xd60dd7f0886f3101744ae2cc40d6cb8643bf5d94c8c90451b1fe5c795146f71a", + "0x6d01301b9511f7b2cef724dab46a61477fee0a5da1e62429b4cc66b919017475", + "0x58c7c869f7209ab6a7b39c2bab13b2eb1779e7e3cba217eb86ce5fce7dc6f9c7", + "0xd933140155e10f5e0025282b7819717552bfc1aed6466abf0c4ab15251c61aea", + "0xd9d30ee9bee3c1788b8b13836393a9c4045aa472ef95c09fa1d8d9191d454ac3", + "0x106b9b3b6924bc772b14d493312321b9740ebcf4829139962c314609b380d316", + "0x2faf2ed070743f36e3eb45071b7df250a96b5cf98dc46ca369a036752a63a423", + "0x4df7cf7c1b6fe54aa7b8f775a2230d1a10f72a4218c9f0e12f2731d5b933a13a", + "0xf7e7b69d5efda706a76a981db699733f33be591fb9501f477dfdebe491ff4fd3", + "0x4d53f06ee22ea51c1171d1d365402842e28be888b1a964076343a2e98202b419", + "0xc92afcb42dce0c6da88ad5785e6d6a0e0e58d3fc36d4cb700250536e4c3e2cea", + "0xb061cc9ad7a7f8de712e30ffb946f83bbedfab4f1b6ba248c51b629d119f620b", + "0x8e024f5290152b4351b1a540f3066f1670c4c0baba62476b23bb15b3487765c7", + "0xc64637ea4bf32d830078601133f7c879103cfffa4615cf1c8ee8d602caa51beb", + "0x5ccd1f6fcf7f5ecaebc78c9627e2b5f0e920c264f97d95b8834e46f4328bb2fb", + "0x1a31e0e8713d758dbc97c378ec9434a27677617b73c1a674c43f8f208d280605", + "0x533f3ecf7ccbff9120bc047981b547a50656dca01ff0455a66c3a56cfd3def71", + "0xcb456ca41cd0f5ebbcb2480bfb20fbc17123bbc6b779081edf9aef1d95d5b58b", + "0x0321e995b53ceb5d7d5f093d0d291c7daa1ca30ae0a136f7038a903f819842c7", + "0xe18d3489aed67ba43f9c545ae4bc9105ec7c33f4fa7c49b3375f2ec5eeb951a3", + "0x3757cc0dbaad2bac8f7dc41e277ebce8f53473dc3381d904147137987f83ee4c", + "0xaf86008d911e49eadb679b88351c979032e1a5020fe940f3b65198a1d6f26878", + "0x38ad86e33b337834b99babd00efc0170d1c6865de309fde4fe0879422dde639d", + "0xd64ea90650bd46ee010b8ada039291a61cc78e4e5a3cb4f1a5b79b1eb6c166b9", + "0x8e15ad09a84e88bc6312b5a73d647e8a0c3bf0078795020a982036d7f5994ac6", + "0x38853f5bb1fedf106ffbbad65f66ed99d9ea0411c14b0026095553c8029ef46f", + "0x1d7868f6105e0d78e34a1dfd21368be0ddd4f043f4c05f3ac3a31ca0aea80dc4", + "0x74a6830a656c6722e8615ccc001ad6bff5a8341ec8662d3602c35015f63dad73", + "0x46528cbc52a6f0309507976aa65c93544024f64b026ae75da0c7bef31191aa4f", + "0x73be4480cd8081f344b2f56be16d04877e0cdc71edb3fbd5c26d6dfa57d27cf4", + "0xcdeeda84945f42533860f6203371a59872b5236109f76876a65779f3e1f3874c", + "0x1658c6988afbbe2f0c273e38d261e08efbd320af929511258d7148e90a66cc22", + "0x5a157c7278bf560b3cacc75c843c61343efa0b75e1a97f8938e9a49ce8bf441b", + "0x568843e2394c91b78657c9b68d635aa1b6c30906e05114f5eaea20fbe4f2bd20", + "0xeb9e70c7ff6d081ed97c42ff157c76556f39586db32ab91cb493ca0872c0f17c", + "0xa80ccc15f92070394e6452ebe6df7c4ffe0c5668de4c81873860b0b1381ae4e5", + "0x1d85e578c5dba030f497585cb9d134701bbc4085046e4c968fec4156f4713764", + "0xcbed8a4f2b83261b7fbfb1fc5dcd4d560fccc6a4288b6a9304877cc8e85d150a", + "0x34927680088e203f772197e763a26996b23c6eea4bb0d6d09f8a9e920aefc066", + "0x7985b7a94593ada5636e17b6a2dce9d99617a448c1ffc54a13fd925b311f2e2f", + "0x8a5746c27e9be82097050fd01aabc10a5cf066485a28e77a381ede0151d2c9b7", + "0x3ec2b4fc53b806a87a73c40d5420dc177083421df984564492af73080de359fe", + "0x6816585ce7840d38d6e33e94313adda48269346bbe3b3325cff49dc192c9eb24", + "0x00cba17bd4bda283992a00863fbb38371b159478a6def46d36e2c81050ac22ed", + "0x62f88c38609877f0c5b911abe95f6646a500a4fc877ad6bf88bf7a3918e2bb9f", + "0x9693ebf19d39c6dea52cfb4086bd2447a1d363323067da0d60277ade5f47ca52", + "0xc22a8c02389b8e21eaba441ff0efbacb257f79063efbead300512decc30357b7", + "0xeb19b10300937f18021ae31e53f264534069dcbdf3208798cec5524169d92a00", + "0x831687da1d6bcd00f8ae8590ede843b3343faac1b8f48542421c54cec28c2c1e", + "0x993378fb33ce2d8d1056ae79acc8645c38a9407aabc4bc7347644dbe89b8a4f2", + "0xdd262a1ca26d6abb541168418cad0d718fef0466703130e2d8a9e3fa455f2230", + "0xcbf97269626e502f66af25659aaeaae6b0e93a22b900ab503e25074a34c2cb1b", + "0x72f4db78ad94d67c2157a1ba922875244ae5a146b38ca714a9d3b40b818d6b22", + "0x20e81a9aa2addf6de3a9d5fb7b4b2afd861761ef1670c99d18c95f5200901765", + "0xc2ceb9f7aa51c61748450232ccaeca846d5d05d589884fb7bd576f0820d49cfc", + "0x87f86a768ec97ea1c04054c2edcb1ea7539c7fb68c6e3e182383504a0744c784", + "0xa8d595469eb82bfb5dc71022447ef799294d6054fa88019052d251ad9791c584", + "0x7df3cdae9740d69539a91da3e3020429c95f2ce9fe2b0daac85bd50268305923", + "0xde497eb487189b130d33a85d4c229b55c73839dec8e14ad72acd6641c2879094", + "0x9d84b00cf74e149087b0837a678b259ec15254693d4234d80d4a82b7ea632153", + "0x3f5dfc58c827e68fcc23bec3c7cccf21db0586e01ee566930057cf0e48174c44", + "0xc196e64a138db63efadfda3f8c7ba5513a68cc9afa6853d2b8fdf194b81ade80", + "0xdded23681d0a22f0cf96e58441d4eae6d08187eef22a0664074bd81b18169411", + "0x42627957e7a896c715a4a1780c9f07b6b61f21ac1ce3ea2841bf190ea382c20a", + "0x833b2cd03bcde046f7b758bd5a72e37b3b01f1620c6d97b7bff2edd303324601", + "0xb4c9f8d545525a35e54f4c0ca5608d127e28199b150932606507c7bcb997e247", + "0x6adb224588914608f9d740bebd9356925ef65a8da36502d5799f2efd0001a63e", + "0x8ad4fbd2f37a1aeb3c2a263a4a2766cd4ccac20a3feb5e20f27551c7f1935bb6", + "0x8df918470f98350ddfdf70fa96353a6517d9ccd9695011f840d58eeaadd680ca", + "0xc1afcc0f4cf8d868b0ff2903f4dca73ce39b8811a3cd4a1b44731e875a3389a9", + "0x6052152e178900718793bda893be312cd1c9afddca32f0993013554dd7ff4608", + "0x85ad99a11fcee10a9383b6cda985ac8dc927a066d21ac520dfe37ffb84d7709a", + "0x009559e2d87d7fc1fae82d75e1394a2f5776349e42e5a64d541ae4592cd42f50", + "0x68778d7cf43b6fae043321de7c56230c1e16b9f3022bbf0d395f85d48e6bf7cb", + "0xbcd3d59807a53f461937e851b9a46b7de762e922b370a7b9562baa50bfb343d3", + "0x31f2f2e33a59198062c89bdc048b51b19ac0807cc28cb9bd4a517673647b30ef", + "0x899a0657177d065a713d85a5f29f464c83b98cd5594bbf87d8a6ee03dcc97bef", + "0x3aaa29fdd3d21a84b81f70d8e1ddbe819cf4005c10e54677479db6382a165caf", + "0x7682a6f078194936c1310630ca388f9ec31fc6b24026f9ba6fcea0a37845e890", + "0x275444da72e148518eebbba67fbd930b871b7b7c563831f053da9d90116f9f42", + "0x1d023240e3f42bea6b37a9b7da6e81fa599ee03a546952997d3a9ba254430c7b", + "0x05ccd369883f4d7605b6aa6f49687b52f3b0448e842c5d19003a11b601e8f68d", + "0x17c99227145ba15b09fa44744ea711b21699fbf1c2d3b59d85e57708128c9960", + "0x934f28c81d2576305841f8dd197bd6eaa6040fc5753b5294fe181b3663c2bc25", + "0x73b0b9815dff2be18e3a717367aec4ac15ccbf1fae24ac454b1db0f90ac9bb39", + "0x9873043697f579432807582ae0e2443f55fd0fe81a83295a36f3a52844185a4f", + "0xa14dca616ff5c64f468345a7d8e5813ffadca52a196868a3060c913e61a19fe5", + "0x182ce2e1469ebe0b118e20eb99b2329572f269188e548b7ba580da36ca58da02", + "0x22938bb0d91e414c6e587ceb91ad7502e1a74f0edf1d660fdf2a12f925fbb394", + "0x49afed4310ff706dd3c011776b3d0e8d812c334e971c5dc9eaaed5a8c8138ad8", + "0xb19dd60cc4952b1944b1d1001e46ae55f51a46572c7021b26faafb6929d0707e", + "0xe622c4a92f595834b0bfe1a10dee7271c4190dd47314b72165adfb5686db9958", + "0x44ccf8ffde37117923b6923fa5e2c62c0710d923a5e04c1af5cd21bd9854a5e2", + "0xa04cee70b0866ac3ebe5de9e6247359cad48a112f89d6fe54268bf86ea13d944", + "0xf7693b482db1106344fdfd00beee8437e4c3c8d18128b703578a74a87396ef3e", + "0x4d63f8ebb1edbce345514b1a6db5bbb321cee0651a4ea81e8ff92e5860991085", + "0x8dd704d506e78097e4efd0d373c510f80eda5a165f1cad37067c25bc88847764", + "0x4a33e42c0eac9bfef4c28c095090b4173fdc94a0c43b623cf2aeac5962800e70", + "0xb241939f9ac929f56a2395746e791ca62c0feb3867c99602018521375ed70137", + "0xce22cb7a17ca34c2dc73971159dd2078de4a211b2894a69d3f8d2b99751506f1", + "0x6c5e038c11c4770ea850dc2b7237547ba9161ebf00622f9152e56268c43a4dd7", + "0x17aae1fc1bba86edbaa349c3d0cab16e447f6031a2e94dce645661db43f57e2a", + "0x7626129ad7629a88c0c361b0febcafff11f01220d6194b075055b122c26b3929", + "0xffea96296d95c8cf46fde5cac8afd39cbfc4ad8c23a92776ce314a3fee4b2419", + "0xd18857d26dece45c5d90d70fc9c14137eac81cbc52604d8216c86a84951f2c85", + "0xe02bcf8f99dc68c9897a3d52aaa23691d8d956f5041e943e5f7621b4da1936ec", + "0x5a7182b6fc3f2622dbb7ee8649794a4eb673d982c9bb3bfadadcd048ffc1e74f", + "0x143521b907af81eaef70b2ec4941d44fe87ba5174d1e21a38a845e09f1da5439", + "0x82d6fdc1c6f128e78b0be4e36feb4bdb9ce5dca1e609bc1c38186f6096abc2d8", + "0xec12032ac388aaf32ef48331add4fbc84e63f9f2366db8cd8335dcbb5863456b", + "0x011799fb5ca4b840c8f1e3df046dbc1603bba904d54bbdf08139aec72b0ec84d", + "0x3e9bd6c51e2de38b4baa70d4d9a314ca56f933f01e96733fef46e91a00a9ace3", + "0x855f4ef539c4913c825b25cd01e260854f02bdc140efb77dca0188d9aa8502a1", + "0xbb8a257b602e5db2422ab600c76e28e8fbfd918f131211d94d08a094cbd6060a", + "0x921e06bccce0870c99e9b313822cdcbe46bb7873405564a72c995f617f6c0784", + "0x1da9770339fc4de44deac8d7492ad7383bbe87c612c4c67adc34fb8e3d238fc9", + "0xf5ffb92e35319e9f774a92eb6952afbdde67ba6e0745e1a3c71c70bccb896311", + "0x4512e8da5501ab1e8ae6119cd85b809499471d399d173219987590b2a6abe3fc", + "0x4a8eae1525054a43d815d908ce3f44bca11b752343f348e4bf192595b78c0186", + "0x34c1295a71ad67edb4dcff47f2786fe1e7f1107a9d689cb5d79cbdf032bebc99", + "0xa955a997dccef16949f7b807d0ad5a26787f571f2ed51dab1b817152f2213ce3", + "0x51208bbacaab475949a40695f8fc85431da990f2e6abe8713e84d64352fec767", + "0x02de3f50d3fdfeedea18a3fbcffd159a59e772fa215c8ecd98a9280896d5bf76", + "0x3a7727f834c9b51daf31e7cde69eb70d394e3313120d337e0d4936f3bd1d2587", + "0xc7167e96cd777fb3f4cdc1e74a50ad9645f534bdb53854e44f892113840c947d", + "0x2104cd84595fbaca878091f6ceed854e34c61764f6d26da7291b1bd2c51dd8ab", + "0x94096a1658702b258de971c89a6a14a7afa550f2b8310b4b634f5959b47718e8", + "0xc88650d9970774cf2a3c1094edebe4f1f93b62575f638cb5724124bc68bd348b", + "0xcd9a16db5ecff581141573448176b79c95b7207536e8fac3a1642aec86f4ae8a", + "0x626b35b26c3f6a4f1d3f80a5defcfa82d633fb2717929dab9e492bf044f90cca", + "0xb4076f0f8af319fb43bc841b95657ba02cef2c29765c9af0429a098ded9ba389", + "0x88352ede2dd72026a43191de1e1ad28e5210739c8d477cb81465072aedcdec00", + "0x123e9337299d4b971bbc204fef14a88015ae0e32dc0eb80c1ba21af980df89ec", + "0x7b6630a6a21e532e53c572b78e52cc1dc15f6fbdcd407f6ef58ff801f78a8ac9", + "0x9aeadb8d64a59d41a5724712b91e8f49b3e9adb9d33589b920ad8b6541f77964", + "0xb8d11ebe793fc36771bf1210cbc7ea7edd0e2c82060191c2233c3d04761ac2be", + "0x52e251e9cdb61dae1b651ed1a8b86f7765a1ee6123032d3b688968fb0d224cec", + "0xdf5dd12de7e17e010dc1006fb6b7400a20a0566d65fca01724d710b7594a4953", + "0xe05b721b47dd25e2141c1ec5eb940e3599a26f33c49af92d1c3fd52dec3dd8f3", + "0x4a3cf6a85864b62acbc62cc83638267d6aee4a4a99a91515739d043aafcf46d9", + "0x5176af57c5f9663660b54448c279c9880f13f43909d8b111b11f401bb82513c5", + "0x4dbad47f9c1bd8f1317d833399b8c7b7a6895836476f9d381f33188735c1ae03", + "0xcc9629b02eeb06b36c79e2e51e1071dbae4c961b555888ee0cbbca19583bcdb3", + "0x732123de2957d8c1b51cf014ac158b6fe31c578239e33c474717770553616ee9", + "0xb697af04e8b5a9691ed18d4d2256777a55f0c9c49da1a1d6b5b1481ccc2e7f27", + "0xd010ffe481dc2b72bb3991c1ec211242d43601b47a5229d4bba2c565578cc9b8", + "0xfc115f5b7e282cc74f6177a3ba673de0f1277d56600652d3ca98ae9f8c5673c9", + "0x5cb2ffd0f3747d80b5646120fc8b4d4b0288b4422979d7f05e542d61533a36e5", + "0xf5aa20182e975f19aa9aa24b998a9b0272efe2dadef75769f9c2e00ede98e03d", + "0x2d8772e82969e4f31c34ebc41a97c26131eb151f8e92293c40024bbbc5f40f02", + "0x0a66aa491fb84951065c20df157e42b616eb7de4e1594fbaacfaaf88d484e340", + "0xf3be1f03a8936109beb035108e644234e0e9577d32bf43a62289178b519a40de", + "0xf13818775f58b83022d033b22133c4f9d6e7f2a561e9b4806b3f5bcdea09150a", + "0x2ebe63708b86eddce21afc5010ceb663b51877b09add967caeab5d7287797e82", + "0x7d338f6bdd491550b43484f3896b8b6c78f2dfc3c7acd30303a12e959617dccd", + "0x0670215ec15948d8fe63c38f9f7bdc5ec7dbf666db9d33f84608f5c7be2e0d53", + "0x97052d33c3562d09691d2dc7e327ee36df37a75696216e6c96d310bede8d74a7", + "0x3963a3deff8800d7c3187560a3f9ad387a369c104cd5aea317e9d49cc80ca219", + "0xe6f08a7f32d9c92a3ecee303ed655fc98ca8498e5b99359f144d5f07cf7557ea", + "0xb78c0f03dd4873b1b8bde0154a874bf592253631dc4ec383193a887624695ab8", + "0x04c842977743513655252b0e0a8f5a9386b61c01487686e83eb9e73038f700c4", + "0xb68f5af7b23273d4afb68672487e24016549b330688f571c6286be644bc79a58", + "0x71aba5abaa54d60a426f0b1a7bda1e4f9302b88bdeada48a7d8a084794ab635c", + "0x9eb487c3a12816d9584230289774b1d6a8f26b148b9ffd4ba45bb54b650b1c9e", + "0xa80e32c447a925c3af45de669bda3686ddcc346afa01da65ad8b5d4569b44416", + "0xc84a9cf0915c6557213cceeca2b43890e3e28820a299311d7adc873d64251c90", + "0x09390d5a6422daa5ff36a90bbd596001751ab80f2417f76feba24daacd96de77", + "0xcd4efde930b05c321c28d5a92c36b789e4a55ce592656bf02290bf67c4d84a75", + "0x24d1ab940a047669463b42ead4ddfdb7f7cbf213c6789c41384d0395f22e9d69", + "0x5cbd19666675adb58deaf551384f226d71cf2a7fc69eea886ff547ccdec2d1ba", + "0x47c45be8cfe462be575b0a6f0aed4071e279956d2af82ae29300d852ef435eae", + "0x7f4306167fbcae82c714df2d7b1860d50c42ebb1f670ee23275aa0ff7fe6d8f5", + "0x7072fd0478180ff0365ecc2bef562aeb3e92a06072c27f2de1a72e796fba6ff0", + "0x536fd0b0c0de762f11869aa6231da6ef03d48f61909e4e773c3c5b002228ea25", + "0xd593737814a84d5498bfd2638483c44ac9a4592d2614c69dc61757bb88ca6486", + "0x24605a449fb48bdc9ef687eeac7e9be716f2135e449da1da6332e696a39ed3e4", + "0xcc001626a2d04bbee1e02c22c36c9f892ab0422871ae793853e537cdf5703679", + "0x2e8f9e380ec0d6791f05eb55e54d9d0c36697bb99addc5d0e882f1ecca6c0e01", + "0x9e7a42d613e4e508d5db867b1b7b972457d2206c382c414e53f42bf3cb74e609", + "0x0dae5efd7aa458deef8226687f7fca43073a5cf8895c4d8febf0da1750fe0082", + "0x8bd498293cb07ed7f737c68d97b3240e7ccfdd535b97e23189a98008600d1e8d", + "0xfa82a48d5097f8997aca547892463218c8d6507c41e9d1bb16064149454af177", + "0x91866551dd22af187a7fdf29ec6336c8c0f6b2c33422146e5e283226f34e56ff", + "0x6cbdc4a4f84c58c29035209eab64d2e3512edcdb43e2a2213a1aaabbbe0b2757", + "0x47d8b973c6ad5f92d240fb9ec0b7e049bf5c8a1f21816a035835976e3232db8a", + "0x85397869af6ea2947e2ec8adb39292d2a9b156311154d17acd202c0efc60d9d9", + "0x526382e6bdd3b09ff3b55901a1e61095344f86ba5bd06fc60dcc5696408500b9", + "0xfa8b3f2c8c806b5b4d868b9b7d4d66e0b29b9727e321ce85ef04a251be816908", + "0x65686c3db20332db56d48b9b076421900cef00d7a24141f0230897649575fd74", + "0xb4b9ddc4d684bf8f3c58ea814cd849857cdfdda03173e3b1bf68b8a4e5e6f76c", + "0x27992a14b8e9b9a225c82d38b041d6a1e4de926424a44742eda15d35e0fb766d", + "0xf41e7395611a354d7b2b8f3714aaa861cc015f6850ad30e2de39303bb2fcb4ef", + "0x8df03de78a10ebe885fc3f526f4ab008487badbdc977fb43ed5483b408c65dbd", + "0x60a0e960f9bcd388851a4039160baa9f002060a5d67f810b2fb63aca5095f29e", + "0xc08e66dcd79a4fa0e4c2a4487dcf24d17598f14c1064249e0c4c3ab69ebdf785", + "0xda1d433bdfa949ea61668a80b11fb6c8966a1baa9cf6830ade99b5bead19c42e", + "0x5b82a6dd155c0c5fe053e0dca27dc447da859a351d89089d9d77ddd12903cfd1", + "0xb598d763bdce7437ebfbff690f8c96a9670a90b3105e421d4b0c8cdde6da83c5", + "0x540d95df8b92588f5f8401bdf4e0063f3af358fc6da1e1d0d3d35e713d2d2343", + "0x6edeb25b2c17785351d1205dbe98a528d6a8d75592bae5ec9ddeb98f0b437e60", + "0x7e0213c33560a58cc2c2b7875bdf0e35d5b2e49bffe98c5a171890436533d276", + "0x1172015fa7287f909966093dd502ba31481c621f4016534f395210ee538519ce", + "0xbdcb7a7d3bf8c0fa4ad25f7bfb0e90c748e675425fdeb7c56fe111cc390161ef", + "0x3300dafae3fe836cfbf88ca60c88282bdf541402c07bb7440db5908bb67490c7", + "0x3fd890f36002c8ec29fcf865e3bcb177cbf4ccaf7e99811c27bd67e7d174d6cb", + "0xc23c19cb215a889518fd740528eb93551c1cf60e1fb4315acd04771b3277d71d", + "0x67d34a8e1d00ab6067e8033708a602e7f8cc2c3ac346c220a1f5eb84318bfb0a", + "0xc0641a4d9616f6431d6fe9643249980811b91db258a06feec5a2b821d18c9783", + "0x2e5f7e9728607319a69ce93787ba0041cfc6ce11f2e15b7ac1680d2b86d7aadb", + "0xa07f5986ec44d2380e44dafb3812c739d921c483f3d5c49d866b38628db23351", + "0x65c6a828a2f6998395be4ddc405995efd967df2b75355917666bbab80f5c6545", + "0xf6aa63880104ebd9e36a0d706c2027595ec6726d2ce1a143e8fd575d6ed1375e", + "0x16211e64982466fb5c499ae5a3519917d6587c237b9e583dd4af6680c2802a6e", + "0x8d45f193dafe07ee7b34e93e4617c81eaa6a273bd26519819fed4a6de11f2612", + "0x758bbb7f2c1e25c64616e458da9d7ecc3b2992057f6f33554aba19c2b0bc26ba", + "0x64ae5e5c2363ef5dd2a5aa88a240ce22f42e74fea6a8eb46d9da243011083c51", + "0xc46198e13dc20ace034596d2d28d299f1165882cade3e0bd47b47375872fb33b", + "0xe397759eac9b5f74854364e42d100d8f77175ccdb22155e1fec67b426e74f915", + "0xef8d6e640744309d1a008feb8a1b8276f27de22a579e7297569e4507ce1adb49", + "0xf8128f3d83c0f9a4bc5bf3f8c6d82412d91b2b5882f921993386c0b91361e490", + "0x220d91150274210f412b22c15e5d15038a20bd443eb7e2bbbde7eb9b935aa787", + "0x844ae86a06e11e64250b23dd4b57651098c4d0f694a99856904b377729cf0138", + "0xf1027823d0b457f1ab9616e3057ae5c14b64136de4800230d470d5c6f4d395b1", + "0x29af6638b99762a1f7a9046b8915d2644ada764b92ff883b17d33ac3157a283f", + "0x0336b2e538fb24781850dcb3fe0d6a096f8930d0b427affd7552235fa4896bdc", + "0x08dec5ed94b5d40af3ea72147d7124bc133faf5b071334af108bf095c73b3496", + "0xc9e432f5ae8b4cfa34214f217537bfdf32797a54935fd9cdb14990beca0af198", + "0x62105750018cd440b5f70cc61d0697e5d6c1c98e69b1697af8224fd123afdb6f", + "0x47dc6570a59b20d97b863ebc5cab8ca37b51f23cad31b579e462aea85e6f2459", + "0xbf7a7eb046ab7f5b89ef36a7ddc356c398edece8423af2e9000b7b9a188a7248", + "0x3c2d6658ee0fd3d8d8e9e4de8e2d03405ea16132e204064bd62daa761eed75be", + "0x1c8c6d3e9b2154b80e29fe7d8e6ef493c5512c434c54a7ac89b7b1da6fcda2bb", + "0x1179a1f0f4e5b1c1051bbcd7fcd2b3bf6e51515d30790df14f4901a65da4a7d9", + "0x04618e10f41b105cf384cdd6ce3700d76ffa1ebb916c13547513c22c7be9a47c", + "0x258a700705071c26b397f57fea6c97acde969542fd5375a73e1eb88b80d5230c", + "0x34e8c798aa96ed6b6b063bb2afb1f9caa17cad93332aff2605688c740c427f0b", + "0x7112dadc61da25fe3fe1397df49570a37f45c13b5950993159ab9a63848bdc6b", + "0x3f02b19221f00bd079d12d97058289685c05b74d8b9a79bb7402ecca68a91c57", + "0xfeeb823d417bc0c0a87f52a5551ff8285250c4c7dc83543b4529426cc0418061", + "0x87f364bb1f7122a036b01e25232a23f06a50c7e2f9f5460c6e62b6554d30838b", + "0x4273afdb9c95040ec156ce8f2eec1d6268e06dbd412391529108096ca88e8c66", + "0xef1f71cd1f26aeea755cb6cf8e9d4be23f2fe17860c397a0108892dcdd932bb1", + "0x00016ebfb5ee827421f7e023ee8ea31f6883ad6758b6e990910e96c1fcb717e0", + "0x09cfe7bd150b6686039f372a62217bb70757a5d3e640db148e8d4d618b2fdd9a", + "0x71c7adbad830a52a46fe02758183f93ebb357dbc79ba6a44e55fa9b36a63baa1", + "0x53ab3dcc5a44ee625b4e0f18ce9a2d45a0bbe7ba6f531a296eedbfee75aedaeb", + "0x5bdc013fdb8463bf130a458abd590a4d9895360665956907744755c606417baf", + "0x29e97b2fd37f9d8d43715d6fdd9394dcf70fb4df582cadf7666ee65b28aed6b8", + "0x97da81b03d81bd4022b879c5d12b9810a6d9733fbe4fa3c97f61c1d9fca7baf6", + "0x6ed93694e307f761313f68a1c27024f78e9a291c519a1a917b12fbbd0e96bd81", + "0xaf977f559820fdc641dda9dcceb1d286839291b5ed3cd9d00efbf90bb6ddc45d", + "0x21c7f491bc0e67c7ac68ea28ade9911ef122cd8cc6652ebd81c0badeb315a98e", + "0xbeac5a017472bc5ee29ba23166a955159e87636f3ac1fa8ef70fe065fc1a7e50", + "0xaab44f1386420f485afbe237aed8acb48770f160c0a80d5df5e2c0c16da34642", + "0x3df96006fcb31d26909febe8318dad5dce2b755469859b6f8e45302ed50bc406", + "0x9e28b30715dfb9d930de7d82e0a6b11c051862bc822a5ac4f07e72638fab4a9d", + "0x2a3f902818fb92d0150dca04fc139a6df928f9226c178ba2c26046b4efcb8dd4", + "0x3f5da263565ff0aacde3538ec046812288f75565e614a7a8aff93002dd622d1a", + "0x67139dff539330504ca23c8dc0e7064187c4b3e5019e7145f47a9dc1a4e28296", + "0xa494d2e3d31852e8bece3b26513732fcca16d91cf9716499e5c4952b6848f3ac", + "0xdad0222a4e26219517bbb9161d466f24e5f9cf616e67a721d2715e552aa6977f", + "0xba6d302fc654dffe9089289475829b879b95057408f3aba0d8b0214df831a869", + "0x2000d1a74f684e0860399304593f779b9aa3bc71797d288a9e48a5720640f185", + "0x29848ada3902774b144542ff415ae0ea74cbcd7855e737d81b4d09c7d1a80ea1", + "0x89060c96a862cccc229d07abd3b23d1d4f9683358a42abdfe8c58a85f528841b", + "0xc49d0c6570e819f57c74ba7394d1637bd406e8c400c04d5596ffbaa3cd4ef247", + "0xfed945a4a82d21bb8556d03ab38336b55cdbc80dad5763944f25db2d3ff9c04d", + "0x31315c9a753aecbb817fe97b6b006c0ad6522514bad0a80943fb8f6207b56a60", + "0x49d4780b6a8e474686efbefb692abf40ba8ba01ab02d91395cee11220443cb93", + "0x4e0dcbdae8f60efc2a3843a373c625b27277b11498f9f500494f2d883bb58da8", + "0x751e5c4e3cc99b022701456ce15bce880dcbf86005b62f671ce52cfd1cbbc68d", + "0x28471422e64ba3afaf54907fb80491ce9e1619eda800b751f1221a2e58d43f69", + "0xc3cb15c154df2dc87be052a0d1dcc86cc8f5bb939e69d24af99ceab2dd3276b9", + "0xa2cb6a6bf18cd03d7733e7b30ce3d49dc9a07c74bd58b18af020911cac04753d", + "0xc52c21f49881d6745a20541515db873f6fd538e5da4749a636895b31eab169a1", + "0x34b48f160000cbb0f3a45126ae2eb684a99482389454612bd8f961ec51385b25", + "0x009769498e13604f5b57390fcee5f4d70f18231020834a825d14acc0d0b7aa1c", + "0xb37f71dbdb43dfdcb75902d4370a599cf9e6d1500cb5018c679a01552dab5f77", + "0x8e9fd210c5c9be2d179249751165a624f37468b7d47fafbd1673b533d2f9196d", + "0xf0506322e525c2174358910a22856e7b72c9b82d4909f1baed64df40ac6c2b70", + "0xc6190152a4cebb80614a9289e731ef4fbbacc85deb1605fa8e1a96f88986d747", + "0x6ec5ec64ddea2b015a34d5b9c25ac314e7f605e5b7148c3c1c9e98d864c11f64", + "0x2dba35649b8c7163810b3116ba6d8fb191c365f983ddc65c64e75ab77c2b76dd", + "0x25c41fb3fdff38283fd5a997f5abe9902ef74bbe12b7ddc30edfaea0a2a43278", + "0x2489057253ef82299107604b241ab4bade8adf7d42223357c86a83ee2ff0afff", + "0x154a10601a4ffa6b0e9db1c784716dc8990998fe0c42a1a47874325329d48148", + "0xfd8429a5084d725a86ae081e7df7e1ffb135d676bd9dd197a922e6236edbb134", + "0x8c8be933cda1eacd559e57bd1b920e5d3b838928a8a7a76448afd6ba5c18b9a9", + "0x345b684fa70f5413d914621c242de5d3423cfc25969734c992a2d871e711470f", + "0x333c8d3593906ab1b5b4404ff391920e21044eba4143ac265eca0eb9b2b9dcad", + "0x734158c71ed4c1bf4aedfdcdb2520639e5d1fdaa966f265c629ac642784af44f", + "0xfe17e4260261703cd14b6880c42869f14f8e80236d799bd03765161dbb5151a0", + "0xedcc85ffdacb9dbf2f303decbc16de783cf867be3125a1a13db012f7122ad09b", + "0x1f0255b4623cbbb93be7c7cee162d98ef93edd12441b11d063fa3431df3a2e41", + "0xf0f3d84e1bdfeb2095a00605243a14b1ab9bdcff5be94b3001a587597641f549", + "0x73f928733385fe048919e756a8031e0fed4dda5ef23a9b9acb092763aeb5a7a2", + "0xc6a3c3711e68fda04f62e1b806c06fff62e5ce5a26e3c927706e8848625d1d00", + "0x00372b48a03aa251c87df396048ae93fa8f94a55f04ab3dc5fd10fe4b0134a49", + "0xcbbedc6fd43bc4cca9ad041583570cb78d144c7e41971c96d4d327b2920796be", + "0x3355e5a38b9ffea0395c8973640779c42411d9cf1abb1a049193d5101c2ca763", + "0xb736692792c77b27de87ec5ac00d1bb1aa3fc770305eb1abd6f420585cc02884", + "0x0a66d2bb15a011cec27e8020e7b832fd5b3415be0e5738f431c2b2981d0ba14c", + "0x661eeaee66fe5641d7269e9ae23c49d0f2c69d146cff13964ddacaf46d33744f", + "0x4e1a63b652d6d2961b86a0b0264df48a183f48141d9ddf46b2044db1a4005bba", + "0x4cc9b1f33cdd811854d25e8b9a18b6f488a3b024f4714a28a8a8db95f5db01f0", + "0xf2cc0d90ed2fa89292d424459f6c24a3cb819aff27f15b0bec718cf00c6de192", + "0x4a3a37e58d7b9e0702611e9936ff56978a2fec039d5c40bc8c8272895eab79ba", + "0xa3349fad292088381ac823f556a109f1e1f36643e544b5df02ccdb33ae069a98", + "0x6786993a0895ec949d79eb520d9620538a70781309cfcc1c49aeaa46f4904233", + "0x4135b89261ba1ff5143ca1cb77194a8e8f7a836522a25c0da439121192e3c5b2", + "0xccc5f4378b9537bdc57caaa9291d4239e97159c77b9b9a4ba2e62e3e391c6675", + "0x041267d7323e6963560309e18d8b26e9ae6f9ca53d12f605a58a19e9190dd987", + "0x78c4973f4cf0ae6a417f80653155034851df70b649cbfcb2824a66b18655fc25", + "0xfb2b43db2587c6d06313ea86a4f0f807df422a25b764a159b011cba71ea1835d", + "0xad4343baadfac31454f1efffec4b60eaebfc3dee068c086d1031293fd3b902bf", + "0xd42868e731e596eaacaaa37da288a29dfe31730225a3da548ba074b182278d0d", + "0x14dbd3dc9ef1e99d0e764b1899f1751e220d4794697f957f42317c44419381e0", + "0x0d71b6ae7d7fad6daf9a6a07b23c7617a95ebb36cf14970458b9486b80784e02", + "0x3777881b3d9a223897f25d53eb0c67c84e2e7855083e1714cce8bed5e81e5c90", + "0xd7af50f96bad80adeab94232a6440a210a5568a2e0dbf9bcfc44beaa1d156bde", + "0x61cda2c0ef9999f17cfe0aa031a880c64dcec29235f8d7376fbd6bca2fa41208", + "0x28daf998c7746bdea73352d11fcf6e8b4737dc08d82fe7b187a93e810bc33189", + "0xce990dc83bcc9fb3a2c5c0b0382fcfee1d7e47dae71d72fc707715190dfd0888", + "0x8464c4813715e71a53c21ef2487f75cfa33813c18be1b28aa455e9a417dc9465", + "0xd77ed9f8ffb16859de7b02d5cfd136af77edbfa2a54c09068f0e010a882077d2", + "0x0c12bb72589daa8e3c7ff7d27310e665e2200b9b6fe3e1ffbc77a64d2a523448", + "0xcc79be5d7c94d9eb5742335c39ac844888caab15cdc4f508358b06ef82c3e495", + "0x07d33f589dcb8be9b0eec240cf2a216c4c616a5d7c7f253246ae52570cceb269", + "0xedf87384f065df94ebf05dcb66528c83d2846732fb86d64e87ec051fbfa25f5a", + "0x69aa4c231fc5e995d66f9ce132504dae82e9a53b332b3d5d718dca94d2f48e74", + "0xf2398f87d089296166f88230ca93f50de5d5d5c7a1893b7430e9b0c26887fd82", + "0xbe6a789deaa4ceaa7996dce3e2471defbae2db1302abeb92f1a9e3f673c77512", + "0x5ba6134e8acc893504e1beab0a1b3ba485d7073fcdaf41dca8a996da6ab487cd", + "0x7b049de6a04b37f98249c64121e13a48ccdd3fcc5d62091f2c5a4c9254e8b9b7", + "0x69bcee3fbeb737de0c5b7d5259c2fb28e2465b5975b0e2b981a68eed98ca66cd", + "0xd75ff7154312458e0dfc2a9573f92741513f4b44d6d22f4b71c3bfb4a7833799", + "0x0d84c48a619f4ff02dc416aec95bf4288d8639dfd4a354073e2abccfe2e1b2f0", + "0x1946248c80ebf73f4914332fbd6de0d44487ca3d036a82436274f2b5f23550e1", + "0xd28aa874d34f0a64d88f3f609dc1def33c293ca27ac95718e0026adef771c93f", + "0xdead06b11c5b98e3dc717707210a41fc5eee40bf5919563fd8cce2e0bd19b142", + "0x9b1dd76e20d5377bfa2f5c8f95b1a591d8ba13ce0ea438188e0c160374f10f85", + "0x2af9bfa7dc9c5207dc07bfecd9038b47b67eb2dc0852aef126e39fff468b51bd", + "0xd33d4d7174ca6a5ec31fde7aa5f86b6247fb75320d38052b2d24d142174d9487", + "0x9cd867490bdc36061094114c59ad8f6c57a07a78332d0d87696bb0eb23f53764", + "0xae625ae737e17a9ddc51fb3a017a1c6da9190959c94230792bb7b8382db61054", + "0x0c62c279ebac02c91083f497f007997cd34b84defbc3f42da258d4e5bf0b69b3", + "0xa10bdcc5f0ba742d69590efb18c3e72090aa4bee223f52b417f4f4a19485901e", + "0xb5b6f6481914d7df60ff17fa7efbee31d900ecfd06e74494afaee45fae8e4a84", + "0x25a1e9b43303740f093d6112377afa4020f63eaf0d2b49d4126402d5738563ef", + "0xb1867ed4fadf3b48a9d71d6f03ae81df69b98465b1d846914c89a12d909c53de", + "0xa0488bc9dee2c3f16bbc5c95069e41a618aa467c46956a4cf696e84f8af24063", + "0x479c22795ccd1d6ce68b492c47a24125f13cf0af69b99118c3d4cdc84563ef77", + "0x23ef56679522cb10ad69832a98d0e035652ae4fda5b6e6e4261c4651145c29c7", + "0x3b8773d508aded32dd73783bcad7e09f9eec0b5b0206dba418d9215ae3954ffc", + "0x0c853be2b817f22405924028d4d604be3e8c10e1d219543f5c92c6eb9f8c0814", + "0x07e1df4d3c045cacbe8778564605b1bf4a4a7c2476d5368e70a3733e3e4c40e4", + "0x0e5e5a37b336357b3600689e4a1bc837309697c8e169746a077d294b2166cc6e", + "0x57674878f2c220b3ad89203a5b74c311f607f5452a6cc65ec538d3c26800e69a", + "0x2e4b6f9020b3237b6cc9a24722c67aec1e834e81c47b239d2bceef06d0055b1f", + "0xf26ab99a50bb9976d6c6f8d4f064142874df122aa91e9bbdc77a3e6397db1938", + "0x55455b0cbf450bee569352b03b03dc478b3fcf71f9a088496a1834a65f5fb7bd", + "0x731d1a8fb6604d47fdc04663637ffd774d181ebc6c83696b5585741eec935ba0", + "0xd1f207917bde54ff7c5be9f83e02111d69093e7482692bda8f826125503ec266", + "0x7491b6f91d3ff5435a7822ff322b2149eb86722b6bed471181485766d339a794", + "0xd8a9747ed7058aa344e0c15c81a3ad97d233eca2102a2baecb30b429edb4335f", + "0x33a61867a18f9d0f5f45bd516ab6b117ea00ee53288702c7effca583705bf074", + "0xf1038e28900936712cb9e9fb283af3152b401af9a762bf50f7dbfa43ba9bfb89", + "0x91cf3ad90e59118ccecb1ccedd70971bbabc3aeebe1ad51c29773b72f7bcadbb", + "0xb1f962228b76d5dbe9e61b443d6b5cb6026a19cffc213daeefdc063f68abe906", + "0x04e838861f5bf348e7c0ea288a1e0a4455720ce4a2fe77ff4d9bac7c1ef1e8af", + "0xb60a06cc30fc180d6ab713aa291c826e91e856c3b27dfcb2d2b816a25ed93f66", + "0x60050734df0812580d1f98f2740733731867b4a832191934abdc8e65c0573428", + "0xf81951da4de14d41005e5c447d41f2e6eb65286d99275791d40d9f8f414b72cc", + "0x79f74f5adffb0e98576859f042e9c1c9fbbcbead3bef8b123e0e6f5a992cd7a7", + "0x559f6706d0381ab9e29ff4079f0da1ffccf9ef4cb3bdf77233f0f9bfe18e84e8", + "0xc0a1f50861a94d20bc3bf241211b989c5a486860ca3a6f29d6fe13f3b8eb95d7", + "0x4aee72ce182b902ac89be1ed0a6c3281e9062147d469ae649f5aae2cb4632a4a", + "0x96dd53923613723f9440cf147a54df964a216e0b42dfed5b6fc0a091dcbc025b", + "0xd44b92a33ae614951418725e733f81eaa92fe28ccdb887ee897c79630e01b6db", + "0x7ba1238d4d1466247e266ea66adad05632282db941963d650a66d93acc83fae9", + "0x1096dbc03e74f4712b095dbcce8bd29523c934883b2e590f7db60e5b78b2bc41", + "0x78611c9144c45b6c446da5985753b9c05dbddfd17c6319e45a42250495cfba78", + "0x8d78262ba4b6ea805f83c60d05c1c21f5be72be1e863220380d7db12bf4104d9", + "0xe03af0695bd64d79f5130574f9172d32536b9f33a7669dbd51278553274149cb", + "0x11cbf3d29f46e2574cc61d119a5c0f437c36fc5a089babf787a08c27fc25a351", + "0x4a70079315739a85b3e49900199e6036c6d4f0622960e57844f00cb46f951f23", + "0xc8f1c706688a101b6d219ccea99d35972b66207b28e8f9117d3e7f6823a4079e", + "0xd18134f063011158d61d5b01c7109b190fce8aa4a3dd3fa63b0528a495b48dac", + "0xb3eb12b4a3be0b13d53ae37ca182cd2ff6b04a39f8363c523a7b3da1d5cdc653", + "0x7f3a71ae063f6228e090949368b830c8caf830f14ed60bfbf0ebe845d28dfa8d", + "0xf76f3fb77db48b2ff9fc005cc6d256e2c4b16200bee309f72f572cd751786231", + "0x27f341e9e1a8349a808328f9dc9b882450ea53fa5e179077b059a4c20cddbce6", + "0x6effc629bfc8c798db5d0ea739130394746163008cc2b5bb0a3326883becd891", + "0x3dddf38a3538ef73b4a861ceac19e2de28d662a60ac96276d9cdc439801dc8f8", + "0xd1d389d28e2b20f4a3ecb5fa3e064f9ece8dad32904a9ebf92bdc50e28d041fe", + "0x6fbe3024fced9cb3e2b5887488d5592907b2ba050d1e59d36bd90d9ff8d80010", + "0xe9eb429b43bf7cfe756750de7732a2eb4d4829a19296725283408f5fcfeea37b", + "0x3230c64b0f66cca3651f94f80ad8815513b6379184638b4f35381a2d3851b579", + "0xcb5a92ca4dd2d5deb40bae8983bcc12981a44afa424de15db54645ebe5002ab7", + "0x2bfb2cc3e36665331794eb63a327c9c979cab5925e3dec8c3510c46b22478a83", + "0x54aa43ba7efee03af538eaaea57aaa6b8c2abdc6397b975a7da367127b6da425", + "0xbc51d1de250dd71e5d64030a23703af79a5413b7c1014ed8055f660bd28718b9", + "0x01eb1a4e876ba333d83c8dc98de411ec92214d8d235459a3a3e0c11fa58d8492", + "0x12c8ee75fa23140dd7e336fde2f454194c6978bf6c6cea73b0224a3c30fc132b", + "0x03a61ef18d8c3672c558d010e5ba3dc32cb5f3cec5c285379cf31bc2dad1fa80", + "0x560a6c28ad35e875d1788e4e21237d84cb3029394e8bd7a85c46171ee5af47bd", + "0xdc11ba2b27ef360f119b67ed781af9354853d38b75a3b19fe9b6f4a69a9cd278", + "0xfca34e1364bce99e17b441d4a86462c5a668dae73f44bf1ef21f030c01cc0889", + "0x0e3d58caa460933f1a6085ba2c585fecab01d55f5f4acc87bf02b4f2a8a6a519", + "0xbc5bdffdc75f2a1a219a4f1612b8a3943b9c4e7383324e4ff07900d1f95b5e9b", + "0xa9f62cbe0ed112a37c76aed8ac9d2a8d7c9fea9aa867bb3ff93a6121e71ea0ce", + "0x81efececfb5f96e16d32623b8cf4c71fcab54573492a49994d192e17a0760383", + "0xe6b7e51195828bab56660aa693120293215c85ad7d9ac8591be8a7a09d0dc8c0", + "0xfc1fea5f8d211069bbbb9d33737a755ddfe5fb1a1f3373f429aa0313368fa5c5", + "0xffcaa22d52d44a94e72750857c6f9c2d8cb4bd0afaf5cbc3631ab2b06dcb8243", + "0xd3b213ade1c5e757dd542680e1a3fb31c83bc9bec6e339c01d80db8435fa0c84", + "0xc060e777cda70ff9913b2d3acbe802205b7a541fdf7d8c293b2a98ac1b7f1cfc", + "0x6e4aba0e6987bcd01455fae8344b3c3734bf4260e4377714bb38c82fce574e2b", + "0x94bb15f5793953b8758d80fe008cb835ee0e2c3ba3a26f8425dedcb51ebdb9e7", + "0x0c05ee0f57bc02316179a5b3a2dcf4c455c0b5fc4f167341f7385a8986f2d327", + "0x029125ff5b866844cbbde415f0a4a02353fa1392d69b899f8aa4499167b825d0", + "0x34e31493fe4badc1073e4897f3c7a1ca18e45ca469fffa5ffe3db808b05d8aa7", + "0x0dead093df0b962460233cb09e90f439bdb93fbecb3badd9eefb2b3255f02cc4", + "0x08ba467a54704937aa8cc74cc7de5a74e6a7c31f50be36fd614b130c81d2e81c", + "0xd2c3e890b948ad2a567d22225934e84a99e24c8c854bc3527e0f2b52d41f25c2", + "0xa889dcc653609cbd06b6f09eba45d5696237d80f50824b26a39552f5e4704e54", + "0xc5e226db0aa0b875e945e4d3a9f6366a0ae9f0ab7589e044c01938c7454a5c1f", + "0x8e2c2ed335383cf133b929ce3169ad842d238c4f59b3210a51bcdcfe2ea63100", + "0x70f1cbb3302082cde4bf53d4f28a61729a5c2d2f62291ff5a920852008887d9f", + "0x201af285b1e0e02a82128eaa0cf9b3b1e0837a0bc5f4316bd8f5214e87c704b9", + "0xf8a8e45184236a2a9b1455369318eb529f8b98a3ea72ae74769a92ebf4120344", + "0x454ca0aeaa9dabaf326c539c8330eef275f0fb0154f47f1546c4ee6af78bd452", + "0xb2d1a11cdd1fc4d90ff4a469d89f0e29ae764cc66a6281a4f8c79a2dc6fb713d", + "0xdaff64cdf4e63c28e486104b220c9592c5910ab50b6b1c59181a7526c49b7fc7", + "0xc378a3d9daff69f952e6e598315f0498c9196181e02f86fda7b9f67b5cbe0c5a", + "0xdfdefd7456e4ffb96c031b6d359509f582a96602336e9a389a82d716c030a67d", + "0x693d390bd36580754ba66ca47d0c027325cc41a1ab103bcd4ce0ca9e536f03fc", + "0x1fa3ec6af9b009f82790d9a354672e861a3174e3e0eaf0bf05a15bcf1c978ea7", + "0xd227d6f9282b0632f1fcddd06b9a4b41c2c6ada185e41979887c7dab57f9a678", + "0xe2f6c7e900b3ba7f75103d858b778912ae91ba9cd6c087b1e8d48d6d280f0520", + "0x11657e101b882c67b57ebab0a079a10ef469abdbfe7ab0591bc61b91c85ec976", + "0x3dcd2028dbd201ef04ed1a7f493e935277c1bd74b21b486d075257cf3c773b02", + "0xdf8ba5e14f869a936e52f355fd0c039819eacbc67549f4b0c1e9d2843ec2ba9c", + "0xb535db1803fb9e214f24eedeaa53bac9ab681d8d1ea7a70c2a3cd168fed978e8", + "0xa805712ce07d2dd1fbf8d3ee1517f8471d65ea22b1e6b71306db7b5e3cfdd252", + "0x68637c188c42c7ed0982bcaeeca94b625b879e3e8620a1fe73694119786aafe4", + "0x1e12c820db0bf7312ad10efea858dff1fa541b4c6c80e605fb298bb96426f221", + "0xb4ba56fd6f55c157622c6e374facff68f2f4f3dc2ea51742204c8406bf323b4c", + "0xdf7e19427b46fdd09e555c0da1565d3bb8aab6f03ac5ff97a56d77cf8012d096", + "0x383e36c5d6e047d92343ddb3b829515c343aecf377e50d3dc538a4618df6600f", + "0x1d4c0425704a7a44e2bfe6603ff891aac789b30558cf85d12abbcb65c4c8c89d", + "0x6ae761213445cf8406bca6ae4e7831568f854b3e38b46e1410a587dea5290a74", + "0xead2b8163ab79045a2884e169962757d210fc02248d656e6a3be053a17b9147f", + "0x9c228545a1e37b8cd1da521d90966b408a43badc18fd0d79deada1fe9077cf4a", + "0x20706b0f252d8202e0ebcd4fa8e3e8f16b6cfcd5bb6cd84c910c16b4a0da7206", + "0x8b6c890ce7da153219975ca9b7780c3507b05d6df0f728860510b53f8e6db4bd", + "0xe11026281067c9d81be30d117d821bbfd023ede877b0189e1a1ef8a4e77d2a34", + "0x00d3facd3235494affdb637c45f1cb96bdabe8518ec5d5189852d98034008a06", + "0x746ed675d6de29ad63397ed5eef6c30c6cb1d08590d2e16cf9b25a5f817270dd", + "0x8ed4f5267d2a738f1d52379576b1d4d9a08eb983f546a84c33517870b3f74960", + "0x792634bb27fb3516d5404eac6137206c13bdd6ad6a12e97058aa7c0eb38c1c11", + "0xc11f27fd974dc5c54fc24221781280345aaebfaf509807bfecb2229b564894b7", + "0x6a98a6799e2b011f41d0f7497ece9b93d9df0486c3549c10c61414d12baa496b", + "0xba328dc1adeb55ff46a667ea400271bba28f06d6a2443924421817761fbe12f5", + "0x01e3377c7d9310bf64811a8f1339a6a267ea8280e56cc2763f959efe518452f8", + "0x2dece657929bca60a44ad8c5803cc8f67b5ecde2ac402ecf264d06b54cf7e300", + "0x4ecfca801a5389d4fd2ed5010a243ba26fc6d5deffde0acd08f3c6ac17a9571a", + "0x656e9fb62d880fe34dde4f4af13326a599bbb45b77b5df39eceacac4e993576d", + "0x6a518524aae1fe47de3e9261a97ccd2d3d717a8c2446201b776c0e9a1009e839", + "0x72601d0e277ba9e4e063a1c0c22bed99dea18bf09175d57d3d290ffc57af9d0e", + "0xe0f635c032f405e09baff45d5daef5cb852a99ffa288670cdee8ffbece827772", + "0x34d2eba7dfb062a912277c2ec3f546b95f31106ae0f792af2f882a4ed838f377", + "0x4e50dc607021ee1d59f63f2ce248b17d41e67e936f3edaa370d7ba6a6473363c", + "0x6ce91468a17441d2bd2bef94c8f270d7eb17f2d1e29c7b3589de8ef7a542767d", + "0xeaf89d7855238165240f181f36370229273ee539e091120a1d7d4082cfec83f0", + "0x028bbcaa33e5514f7bdd361ca3f007342a05ddf426dc63e2fd04f366c8671a29", + "0x98bd82d817e706eb4ba6a95b9bae34114f44f632f9f83268e3daac5d8df7b306", + "0x3b010db4b3c08e1a03b02e91520a1366e246a3ca396457bf1862ca5f45b67ae5", + "0x227ca2b777ed1038975a9cfb52e05489c15813a970338344fa70c1eb52b8a272", + "0x1e9bee19b0ce6a446c28fc7d227516d2f69f2ae7af4c87c815cb3b638dd82a35", + "0x56cdc94150e242a8421e35bd8a533e0f29deb3ccdf3766537c9c6d83ebc57913", + "0xebbeb2bdfebe43c06c15d7f05e300cb4f46993d2d1a8402df60069b9b5617d55", + "0x2f5da4c00b8d093c28b950f87c5eae40f05e8b13077367acbde57acfc751a9af", + "0x6aaba4353383b73025b9c5be0bdeec068ddfa763dea49ca41ee3093e9ee9d514", + "0x89889f6840e8fe6ea230e824c95f2ef4fbc24ca2465edd151c965f505c0bb500", + "0x9d5b4e8f854d252859d41c3b5d150b78a1c9d2538a371704f8353a088cce169b", + "0x73b7aff0701299542682ae71a8fb68d6aa5de20947006b151daf9f9b4536ce65", + "0xcc8c8f2e6c5562e918b6cef8f0676efddfd20e3d519831246b2437c580783998", + "0x681ed9e0d01e871f82bfa24fe13276ca821190ae7e4e1ad51913cdcd5541390d", + "0x500707cdb84191c520a5cf783b2de472f11ac5cca41910a22e8b926dc4ff40c2", + "0x5c386efca71659e6a47e5d5db65b8bd8530117a3512d3c6d791db8c9f2fb4679", + "0x3de69846745df006c40ac99d226c2c6bf6280da8e97a7de183276ee9b57cb40a", + "0xce424b5da7b532095ffa0c4eb41436c7ca07283b7f159bd67fa6bded34ed07cd", + "0x1e763f6624d9a7ed7fcfb088ff39ebb1905497a48b7c05b582138430ad8bd387", + "0xa2121a0e55b68fe1eb4ec7c8bb1f44aad03f7bd65595af5c41cfe95d5b184299", + "0xe167412690e8e8f14c469dc32654538b4bb7a5236a727361aacd02f2ee9935fa", + "0x7819ad8742d7ef054dceea14102a1ab3e2a208c10af69aaf265734980ae41617", + "0xa4d3c00e16c1ded1cdfb4d13e9931ec727ab7e8908f08a468b6aa5ce498fe5fc", + "0x365c6cd6af814b17a393f0d2a6cc807200cb3a8c1649a53a5e7c8a1ca8e3b4fa", + "0x23549c1953b2ed7e44db292dc91d8d22784386ec352a8b2b9d2cb488c52c4b0b", + "0xa7e3a05021dba96ba343c63f97fbed38b2c8acc94ba2a5748a9701ad8a7771d8", + "0xbfa4e045208d262fc15a40d2e1132f35d0d72ef0eefc5f61462f586f5c58a5ff", + "0x661f9cf1f27d048f3a9efb8536ded4742fbd9799e19eba7b59d0ee55b6b605f6", + "0x7bcc241bc840aa7048e70691fd49ab1c2680eec79b433f2e22c10309a1f6d165", + "0xa96e5ff9f709f479f146265d976c71533d49beaab9d9399eb91034c8f6be9441", + "0x89ed6378a5aa21a82710a52c3a81977bf9a3a6c5c5a2873d3495bc6031ab5143", + "0x2b8362bda84aee82695eb01d1822290f72dafe8abf31154dafbdce2e920e9192", + "0xdf923f00d6c0cf5e6213e496ef8da3f3ed6773aa629938312b8535b821257b00", + "0xea0d36c0c0eb351c6a5e632998f01bbe887055af63e1bc95af75f475554f9ce2", + "0xa7a7d1d62e65fc4f35666ae1b3d1a896c4caf0072701eb82306f86d0ab428e37", + "0x4dccd7ffb582189c6babe62293650922c2fbf50663db1c9f57762fb72f6bc3ea", + "0x7869bda3924b0f7547a45aabbbdab6b8ac0294d51f5a38053d52aa1f50afef53", + "0xd719abb162fd0b902177adc730c2326dedf971efb0302c80d3d4985868459b3e", + "0x402163c075a5cd8494ad3508e2929e95192b580497a74ccb1144fce679e7b232", + "0xfa429d251cef7a59c167907563577adc81f67a2e617095ae061151d01364d4ca", + "0xd9774589709ec5fe4803fa3d424fe4d5652d84f66d9f83a18a2e1b106eae0e79", + "0x8d11488750e4c98cd9f4e0025fb0f073bd59f736cdf9c68c9cb213390e9207ee", + "0x17b04bfd8b5267fa1d6686e1bf9c2415046054e80cea3a16c2c695b31b1caaf0", + "0xa24413135fc5b26f2e8b8221890d27700cecfaa54dfcf0f0e739f79f784d7a1f", + "0x94bc9ca2e4103bcf945b73c96f9deeb7097e355ac275fdc737dfb6293b81b750", + "0xa12d855eeb14ed8b36425ae78363b433f5ddad756fdbd1ae8fe7c2b3a22a9908", + "0xc6e2e71b952749fd93a26b09c5a65046f92a7a57f4fc5c9580e5c241dadda3d7", + "0x2a7f017d7ad24e5cf1a10a86ebcbf5c555f1e141959500c8217d1d20f692f7d6", + "0x23869329c00abaf9f8f800eaa533e36705379b66e7371dc1b527ce2461f3f281", + "0x094ab8de411e8ca5fdf01ef48fa5200ebb4e96483fbdc678bd8c6a216ed9f417", + "0x0e9737f3c3211eeb1bb5efba1b2f8b384b0504a31692fd851043428f02d67634", + "0xe88e4e403940183d7469492ad9684d9b4fe36ddad27ef43a82304978ae410a63", + "0xf9c9fde7017f0705bc44d96ed22788f95ca2dd0dbe2085d3573fa8d0fa024996", + "0xd813ae9b9afa840df59a991ec4182188e2e8b1757ea62952cb3911c118988acb", + "0xde7416c652c51f8cdf5b3e832c4751a98a13cf19aad5d3fd4a231cbb893472f4", + "0x42db2ff0e73af802f21cbb0e334f03bc9de32734804c7e3719cec2d1db58e8b5", + "0x97ada1d8ce886929eb084930502f72df80a65c66eff9d95952976dd527ac645e", + "0xe1b3eb1123d0c78458740353e4365b95e6ed5f335369a2c440eeb9219cf857fd", + "0x4df7a3136c9aa187a96e4d3daac47bc06b1a8ded85e6a364dff88fd6fbfe88a5", + "0x374c1143f8bd14fac109a79f176d64a093394f0a715223f5cad5f177e8c861a2", + "0x326bbb54b40cc41062b8cd3b37997ad6b82221986fb735fa8599678a04f30ccb", + "0x5f45449b690499a6f461ae93bf98affa4515f4086e740f507da8f57f4775d315", + "0xfa9fba8c93b64f5822e93bf96a21f0e6ded14e952992b692a8b97691a3ad2fa7", + "0x765942fad567b385d9ee74605c2dd0ad13ea4fafac46a4db90cd012769f8dc31", + "0x1b12113aed97b8a23baa71bc47b234fca46194ac925956786a940449e7fe7e29", + "0x6d56f7abb13bbda90e2eac3f2b7ccb9b6df53a05618587ce32b788f24d11fbe2", + "0xe537a7a9a5dd4eb781ca93c2af311ae8219d45bc0a292c6d5ac0f7bea19c57a0", + "0xf69789540672388bbcd38199d9d299b9c43e9ba3b0ce2e1ab0dfe992306b2178", + "0x1ceb78c90fc3d2b0b377201b49e82573f64a82a20ecdf4f1a85f1b1291a93e5d", + "0x011c92f38712dddae87313dcdcbda0ac73de7ae227490a63f399fd31afb3e1f6", + "0x169169c84293d31e8477a86995277342b9f39fdc0847b1465b0251db02ff826c", + "0x731b150fc5ceaae6561e7d9aaa57d889ffc92b9de499d815d25905991d6e4244", + "0xc9faa11ec743f633af0e16e94ccbb8692c8bf678223f071ba39781cbc79bed25", + "0x5eda1a5021c7fac5cabff85fc3109d151c95f47907b335cadc3d1b9b57a01d23", + "0x999ba6406eff6496b1de895328d2c52a201c0e24a1bc3ae2607eb71253092ee4", + "0x6a1109ab718608ce1159a147b1bd1e850eb4dd8327dbc6ef61b8f2cdbd5b53eb", + "0xbf6e2558782068bbff17af6ff5a91a79d04a4ed6cfc1b6c52bd5bae675deb679", + "0x10f5c3259353b633fd7fd0bb904ebd85efe3164e5bc9c70bb3ed33d43d7416a8", + "0xf1f377e5bfd7537f1ef24d5470fde90277d80e3ad96a52aaee68fb6747c3521b", + "0x4cb5af6ea29303d8066017c2b5da968fff270e0ed36ca892919b2a11c4c1e6bd", + "0x8d703fd4135397580efc3772b878caf48a0dc8d7dc07c5213f2bf50ba6e960cb", + "0x0ba54bb56781b5b8674c7a86913a59fb44c3b2e487da05f631f60c3546605745", + "0xd46d335fd604199d4918bd72ea28942726f2c14b9dcd3c41901a9b50c8e34bf5", + "0x8a345adb0a0797edb8e953a76afcbaaa686623c3d39f803a581b63460148003c", + "0x9811f93a10451f74d702f5a53041361ff49193fa8c50d811f19bb8e02f7a63a3", + "0x853bc9757c2b636b5456663a74b5a97658bc277647d02fb0ab832288cf114770", + "0x4188bebfe1b22100f578a2a3a6d3dff799a6dc072382ec96b5601b77e16a8748", + "0xd99c86d53fdc8a35b44bb2d3bd2e4db2e9d1562d92cef76feff822ad6d6756ef", + "0x0c147c440169c0ce349c0b3914e335e337de5eeceb49bfca194461205b41c303", + "0x901dba3aeb7dc069ac69f25acc64fdf79fb4c222de8f894785bd513f607a3832", + "0x94ae80f484de0aeaef5629c6fd1a2e34efa2d849acd60fb2cd46e5fc42a4be2f", + "0xa6db07e4b4d0c2a13d5848df3bc00eaaaad87a678b6c9c1cee7fccda88811f22", + "0x4acf2afdcacbf96113f59177292efe4bd5c64d48f2d560c9d389931d2f83db29", + "0x166f4ee32ba1dc19c61c11bb8430720ca6b5a54dc90a953e530a64b6bb9475d5", + "0xa24ee85acf639f3a805d6e252a1841232bbd7df6de5221571579d898690b8fc4", + "0xcb1d6fae753593d659710b77f58d8149c4403a05955838f0138f8245ca58b915", + "0x8852567d2767cc27141594904d188f56d4091084971dd63753c272ec821872b1", + "0x1fd8e2996e060dd22cb720c34b5a816a1d1d0367492c53b08c5b758b23144f16", + "0x50a7ea57ab696a3d795974bd57c2c7d0c10920ac66eec62f0b3557c911b01089", + "0x43c8285f06fca8d9d594f052b87d318bc2c9cece2639a5617d9e341bdfcd472a", + "0xbfcec4ac620efc4de2aceaee3bf8805f9f614a61b2bd1d6e6a32cbbfd4d7d227", + "0xcdae982eb88ff661ae4809ed4b0202b83465d25f004241e63c8c162309179e2e", + "0x3e513abebeb6e902ed7a9538e285d0851f22c5fb55c59c0ea1a399992ee0fd1e", + "0x96a8c1f5cdc073dd27e343cefeeb1e184d41b038733bf4b2bdb54abf9420a526", + "0xcd26b08511f0ebdc745799c817182ea25aa02a5d01dc903eb4149fdbb5123493", + "0x4c0c988c38f92e771641f148d40abc01486b8cb96c8156b722c1ea877fb918fe", + "0x5b5867896a986021d0d3286c70204f2766f05f97ee8aed7c38434493e0db9ebe", + "0x10a903f51632654f6bb491a1703cd903220c699f443f52edfc25c74721391c4a", + "0xc6c5604451c8f509dc8f39d8aa6f55fe5a58262ae3ec011d35c8ecfde30dbdb9", + "0x0529c0a042faf661a41f2ae90d09dd9f89f872c4622f4d4097e689a9d8fbd533", + "0x88d3f3ddc9066fda1b3bf6c71c2f91bf93d3c7b1df34860e8e5b4fb2dbff0b50", + "0xa020aaee24fc439bf18a603d986887b250c5e3e17ea0d0362b1d7708f13bf6ee", + "0xa88bec0023dd9cab7eeb0cf740266d0793ab53476f2d27b43b4e65db2d21ab58", + "0xdeff2c24b7312b60db909b1b3e2d63fef61315a40c31f54abbfb5ec7633212cd", + "0x54bbe72ace4292fdee18c6c60dc2f6ccebb1aebb17d23c61d02216801962986b", + "0xbc5a3aab34e915f1070678a61e9c889d075e363130af7caf68aeaabadeffcf86", + "0x366edd969b505f0230ab81a1b9986c588cbabddb6048b39381a1de21ed13cfb2", + "0x1ed664e90b29335504ced17c3bf215f27324a1f134ade38ca9e192e85465812f", + "0x4f76647721a11c69515422f64983d7d5ef04a8cf3418b9ffae741a35dd24e618", + "0xf5bc8399a1be4838d62a5defd46a09e76d70344393e4286d78917fd4af283573", + "0xa6ecb6f7c1cde9961f560eb19fcf18e8feb0fefa22889d57c4d1a7c44ff6ee46", + "0x7b3d447392957c238acc6e7d916f3e8c1b8f0f0a9007e703d57c4d50fb5867ec", + "0xa68a2a49ac6b6884480f86ab2f1bdbe227ea24e9b544351f4fb58e5ba841d101", + "0x94d18ced32542ee6e61be66a43f74ba995262965e1b9b20ccdc3eb4fea1215d1", + "0x0f58e65b813c84bd23d9214e84e812c474f65133f66ab05ff35a56c9558c55a6", + "0xe8de6f926e0ff18c60d22c4000474b703ecce9ee2992d5c5be744b5f7e80132a", + "0x4c18a1ef80c27f8f5bfade787e1453da4143706390b331597d6b3d209d0c15c5", + "0xb5be49774eabb120c8833ba4679d32fedd105037387f7e63b3a95ada09252289", + "0xe2046de656b9b3def4cb31b8090bb54aa94c7e785057b2d7bf559559a90ff837", + "0x25a80f019e88c22c812d647ba537ece623c86ff5430fc9bee12b6c5365a2b21b", + "0xe3f165650ba209673c9e8e65e15ac961b19b76a6ab9c17014625abed0f7eb8bb", + "0xfa19c5723d5f3b46aba71f98f2b4b25f40aac5ec13b73fbffb9830ff5b437a66", + "0x97222a91449cdedb7a5a16ed15ed11064fb920869616a0acfd737e56544d09c9", + "0x0c7dfe392da6288a62a81c14c7eefcaf4bae4e78177ecc917a51afc9e3c1f090", + "0xf0ed43a1c56f2a451c2d2b5fea3f4fe11e11703a48a940cc44114ff5550072a0", + "0x99cb29ad8e3c175dc5c0fe3b572a1d491f7afefada44451431c73aba240b7ff0", + "0x016ac82c6bfd8e6c5182d8ee9bc2448fb8fe1478784f75f0eebc995bcfedb07f", + "0x465329d11761c037c2be08bcea5a1aae47fefa767aa27d41fb4ba4e3e054e014", + "0xe61bb22826d5841c1d76ffe82f5ea09c9d2d035163d1046d11c55c23ad1cfc4b", + "0xb143f8de3581da9c9005f9908046b48b12c763fac95a96e8256b192136791414", + "0xb0f80c1e9f35796ed83639a439bee52737d8622716f3b32415b5036c4ef9d0f6", + "0xd1024838459ea88d08481e10c5197e636c9d793b8bfebe191cbcae0d22b63866", + "0x0349c1fb4b9ad5402e05b56707aed0c626f17a5d206695f799b64bc587a01162", + "0x0477a92defab8a9c7613eff313229b67da955b9859a5f8be227d605e11b0bfdb", + "0xf369a0f0360bea0eeef7ad4198f869664f0ff02ceee9ea47de9fcc6f24d0972a", + "0xe2c3c535f578ae094149e4627015fc9bfd43864a37a3f70a1991208ee9a9007e", + "0x53d2c8b81d3115fb15cd042ae2766ba4b6d87c69549a3d718319d9edd756f3e4", + "0x687de6037ee6ec7d835e5def4e78dd478f8604aba93fbfc7a2c05e1f0efef169", + "0x8513c0ec9c4cab7d5595b5934a12b02e33c3a598250a380ab3b2a66430dd66d0", + "0x23907b68380f173a0a7344a906eae92b54414d5ac6163b40497a2224c5f86f2e", + "0xab785257ac7d343bbb206b3a8020dd93856c841de5b82db9e2b2069d86a76109", + "0xce9f093c1eecdd1bb5c142ee7e4fd7f0378f324d07d08d783825c3844d092643", + "0x022f99d0e0ae87478f141818c7b60284ae9eb8c1bdc358871949ab7f677bdc17", + "0x18c283d9c2e47314867fb051d300d2fa48e301d656c6a0e5cb632f3512305785", + "0xb801b0b50e031f3863d1a6a99b241a9842f718882073eb034b617aac88926acf", + "0x6018e85e2d586dd17e9d6d2fba23dbcccbd1a805afb253de3e4a851f38a53677", + "0x183bbf19ca3e8e4d87d4d01ff6da46c9f6899f724c54a4b0230097f9a7ff2480", + "0x32f0ced4d3af730e3306e0d700f9bcf4ee2ba4eab26045c8346dab5789cb2f79", + "0x8f5dbc570056d2928c03dde38ac03594825158c1224fb8f2cdb84c662e5a95c2", + "0x0d3db861efa10b48032ec576ad7ef38e29fd12bc071724488884f52bf9e96257", + "0x780076eceff50e7520dda6857d926ce0a2d37fa26c0b8dc2ed6dcb91eaf19bec", + "0x4a5b653ab03958c3aa388ae4e74f043f61e0dc843ffa58692b6bf3ae73a3ef9d", + "0xe54b526ae445ceef5f922d869bcf6057310579ef78379915895cc4c979e5e510", + "0xba818524e8db0f2ba80dd9d59e59d28da99ab3db83e4fc8163b45cfaf073eec6", + "0x39ca45ebc1df7ee4a03378f4f7f7a0a46ca86f25d77b3114e84c0eb6ce59518a", + "0xa4a54a5ca114d355367208f98fbdfb6084ad6e360920636b7aed0c2cce239c09", + "0x26b9658d649677fcfaeaee3600f4fa7822dd3a88930b9074f72d8db8f32db505", + "0x699b66deaf2c2d9961857f4a42f162a0f63feede48994a5ce0760461bd4a3ceb", + "0x4605a2a88a411299df53f4644030c570dc8ce2e990139be9d39438d0867949d5", + "0x8df02b96605119d437431eeba3f1bcd357ccec78f1238a262c0e4f9c970bf47e", + "0x1de2d22cb373f6c7b1b7320e6ba43dbea578ef4dbc57a11aa02c6dcf6a490600", + "0x34f9bb1e9fce2ab1b5cf35edd2a1e0b9bb922b4d9562fa3fb06f403c531e9ef3", + "0x35ef47d3c9d9f7cfc8c4dbc15e2e63543c7a04370d4223b21ac0c2d096e59663", + "0x0221c02a2504d34f1414e488fe3e6b7112ce1b23e07e7f2f4c93a85328ed6756", + "0x27a084e64e4e0dd545561f2f0f93b176cffc782c234c82ed008469ba7ec0f0bc", + "0xf4051973a168b95804b9e6f46c9d0e610aee7175126e0ee1945af4efde60c744", + "0x93d26ca59ffb4f9146f243f418eb43ce637c98ac697baa5b94711da9a3e58abc", + "0xcb335f0d74769194b9fb7761ceda58097a4393fc17fc5fd58ea01bae0724a741", + "0x934e940c38d990724edae5549fe33df409a36f35f4b3bfb2f8238b78d79d3a7b", + "0xc3e1350b4438ec39d0a58b7a1415e948a4aac33fde59bf40a025f371fe9a42e3", + "0xf55f957114ed31c6dc5b9f50f7361e37dfef2c35b84d601564052019e3edfa7d", + "0xcb2caa37ef00502617f70b9b4290e7d9c76a6b9087100801b308fefc362e2b88", + "0xe64dcc93d767b2b8a276aa44b3f358095447a02355842abb8391515cb82d6695", + "0x848e93d8d9660eca6f41e6b22797633665ce3626dcd3ff578a62bff8f3ca1e7d", + "0xfe53c3c7700074d82b16fc12fd80f31f9fd7e853d8782ca928d8dc65f2ca4d3d", + "0x4e1ca007549217d4d9600e0463fd6fc20a538732179e762a32d5b8fcf6896650", + "0x6abb550225380dce42f108703de3033e9f8dbfec04fe78f855975a949105cd99", + "0x703875ab252a87d75b5fef8a22056510acee299f70b7b2192a36680d2a627df9", + "0xea56866f8a2e98b835fdbe1410ef94f08de87d90e0792b65b6e0ab421fb2679e", + "0xdcd0fb700019d912f376235ef867da1c86739c01037dad4f135708c4945165b5", + "0xa9ff632c6ad3083be0706cc78c5ec06885b6a95380963e3d15573364745e0def", + "0x41bdaa2c35fa559935fd4984014172adcd6bdc573aa50a4e75b85c490763f48f", + "0x7095299ade37962fc1b2ffd0516e02cbce1238df03ba16ab1d8da08146283a0b", + "0x5c3c56038480c5fd7e6d14b8d21a6d3c0e88f0d5dee1d34e571227d97d925613", + "0xd284dc2844db0bcae87bc8c99cd762f12195e379e794246601a66934c01fc51f", + "0x2faef91d06d497819f5a00eb5ffe1ee562443cab9a64476df9050aad7215673b", + "0x8abe153679e0bb828c0a232d0019f7ed5406d0b72cc2fec0985182e31b608fde", + "0x78a23e481103b49577f92365bee1a5c2257fc9d61f87a9524a91a4d217a8c25b", + "0xcad840162f5ed83ec31d4b40873ca207d019f5297e046a022b55522a72b49d6f", + "0x0bef641d03e8f1b0d2c6220b470e02eb74a01e0ad0a63607573df37bd6816b9d", + "0x58f66b275902f1a9c3e4e0ff6f1b7bd1cbe477a25c8d5c1d9b39dc98b6c59f55", + "0x3848830c167d1b8d0a7e7b970c67413b0af9e4c0e778874fd3eaa52f69985eed", + "0x0a9d15831a4d8e4986555d6b37bd3f7a390b12f84e59153b00cb53644a776a1d", + "0x8094030e919ba87ef1b52c936aa579a37748d9d1e31f77becb8b34920f8c0596", + "0x4ea4842074673aa8b150b801b12f03f52e36f5a7a7ef2ce7301209166853a47f", + "0x578fa19f8782aa5a4477441fd462465586c1aeff720d06b9a9abc9d70e437e53", + "0x2902a1414b8a8a512f2062c6ea7b92953ac0f380268d9740a932d5ad3f5e162b", + "0x402ae1670d24b6063eb70b8b5df3f1b7c8af5ea4ecff9f27e04a50fa8d70ed34", + "0xd225dc1c1521b4f8dcb86383826f559a8af86aae9a0bc70ef6a702b3deedc0f9", + "0x622d02b2bdecc5941b1ea7fcc17eec36525e0e11937d51267eef233d1041811a", + "0xb129a096e1c82a5ca5601bac51fe43cefbca5c1cc1cc2a87ae05071940180769", + "0x8593558c89db5f09a107a2c9574f6f637b3b45b7d6002730f3b20283f275d758", + "0xb717b9c1eac7eb75fafa9271be4fe61c1461174c3a03b1237d860a83b71161d4", + "0x90bc0507e4ec1fb3b3235e32aedd7339d7e7c3377d2ce21e86a4512fbc920b46", + "0x36d14cbd1b24c04ef4ced1779e9df034826943bee83441e2bba40575ed718a02", + "0x9944dd773a558d7717f3564b814979a9b8c0818776d705e2a4a9a3b4a462f54b", + "0xf5eca41a1038a969cf385a6828d9b83a658c6a9eb3bc8a31b85377f1d770c8fd", + "0x687d337dd738a5dd5a06442453b49169c724c108f4a2c2a3931ccd9c8ea5980d", + "0x885bfca2cc86a4d72c5879d902ccee27ce2f75b356fbffa7f979fede232f5849", + "0xd377b82e4d76800446630cb4de29c653d103942171dc659583a6c7edf4b18cde", + "0x61495ad7ee1401b36206def961e1ed73e00f099095648d7d98ee8b88cd8a8571", + "0x9fb34384ca1574f1b4e6bf2add3412d7acfb2d2ecffd248715245c2055d4672b", + "0xc8b8ce269a16ecc197766c622fddecb32b74b5b3b5b87a61f43ffc4412c9fa67", + "0x667747dbeb13ed36ef3b30bb5668bcac7061e96572814fc902563efccb981086", + "0x65d85abc92861fcd50694f2d47ea3b29ec43092784037d5044502983524c6d5c", + "0xe9badcf7f9bbf8b83452e046226ae31b0e60cd870115bd3fafe14f35e2094b6b", + "0x61d70e33816d694caa1b469c0d1b7f77946f496035d664eb05203a7cc96d5dfb", + "0xb5d38c373721964cbf931ae708b92f8c92b6a86e33320ae0fe4250776a137758", + "0x3bbaff7c7b5cbac485e80c474c8f24fd6e07510118ab3306aa597a079013d925", + "0xc8ba1022273cf1fd913c072bd2c25346204d37320dd2c4f236977e52779c1ca5", + "0x12d64ff36caf64e8eb2086e7b4ad07552acfa937cd8b78b1e018609bf5afe6d4", + "0x5124da904820838830758343f0098a09e38f0c795c647f629c2a155a015fe61f", + "0x0bdd28b37141db950f79671934321ebdc8ca08af92fd5c1abcdcac678ed87fd5", + "0x2c476a1da91c3f75ad2c80e12f73cb57fed088960d82171e393d18ec224ce359", + "0xac7b5fdaae21f25f99f3eb7d9c94e4683cf9dc3faa8a53cc988216ca7f36e390", + "0xdf7fd6e61fbbccd0c9c1525c08d7160555d188d8c9b1f9ad80733a56b3ee21c3", + "0x22681ff5cd1b1c88d2d6f7fe843b02d98d77f9366fdf7410266b01d48f4ac70d", + "0x3472c86b5a6cd984c06140b624d9b5db3f4138803a6e269be58d255beb6070e0", + "0x1f010aeb0f77eb52a5ad7d326a1fcde138e5b0456c83b86e8f740e56dfdb26ea", + "0x21280cd3ce7783210b9512009e91e27f16dc4e0d26a3c00f56194aef43968d2a", + "0x0d62df10cbca3aef39fa6072ceb8f05f9b97ec0dfe7f98d22eefad2291df73dd", + "0xd7296d1364b1a7df7dbbd09dab625f8e799658861ce2b37e92024046f8b8763d", + "0x4e856dc0ae5bc0f88f9b9a47057c08cfdef42b3ca15b777201b6458900ecf93a", + "0x830309a84e0793a48ba692324073ec7e54fc4c685424f3f35697a8db5660f937", + "0x75ed3d9acf32b8b4a5a4696be1d32f16b70a31bf0f1e4e081a3153b5fe2944bf", + "0xd7c4fdd7b778a7fc8f6052f2a4c66943587cf5d54a6e0d820dbbe2f0908f77c8", + "0xd0c3d9eeb53fe43c336fe42c29335cc953e953ee3d9a7bcc24386bcc6e0b4fdb", + "0x09383f642801e76f488355a3d772f8e50515888596024ed5e66e910eefcbfc86", + "0x963b08e1c43b4a266b69c4820d55b61d17837810676919b888672f8947f84125", + "0xe2f86b16694899b47e695dc7fbf8f7ba8c85fdbaa6c9d48eaaa8cecce93e631a", + "0xeed175309efb30f643e4530d446fa6b72eb2c112c1a7deafbaa4fd5cd024f3da", + "0xebcc142b4bd94f4db0b2aa50772a933e76dc7e0fbd7dddd744c761acdf3215ae", + "0x3b9efb952ff4efbf9b24bbb93496a962b320ee5ede0cf24eea3c8d1373464d6d", + "0x75532e4e453f6d8d5e847d5301cd9438e17944f80afd001c29678017a0e9e0c6", + "0xff4a99ef45afbced83d82a4fe4b1f55186d06d9706512f130b1e4f720ae65d02", + "0xf719e623ac4c45610b778fc77ed261789d4f34e3551791c30af8c42f5673008f", + "0x8e9b669d6d6bbbdcc5f5d32300efeb424fd348978456c91312eaf0bbea837df8", + "0x708bdb80249e34580eea63702b24ca81363c75fc509d8bec8bb3f497109da119", + "0x8d2e175eb1acff131ed3c0cf178c4ef8e4356680ac271d8e4fa874da0af5d1d2", + "0x88d7fdbaf844e79c8d671d375d7619f89793bce8dc6be6be0cefced47974f7fc", + "0x33280fedfbe1705e3865811116f8a5765282c3858e0bb7c8173bfa112676d79a", + "0x9a6df76426288501972b45dbe7671d2d2def881922a12df863ef90a3fd598dc4", + "0x55b0e215c8fabd1c0ffc8d07832befce4eb038a82558d758808e259e9882763f", + "0x7659e23b17f21990e9e061ab5bedb0f3716ca0ba87b51d526a4e6ab850e7b922", + "0x198d94c8f3c1fa2eb477ffe3edb69c3fb052e6e89a0f9ed419c2f5da19444a6c", + "0xeb1a3277f89d30388e9a9b9e575f35d8024ede01c065b0ba32193fa049d05d9e", + "0x25e9f26a579ffe69bc796328bdb459680d52cc409aa6e712c80d41dd312d019e", + "0x02d19dd49073b0c02cbef9ef9530c53473d8db41e7086e8cff3365005b79f084", + "0x7178ab64b47e2f66dd5ec05c031ad30c8234640fb5616d0dbd89299318869e93", + "0x200a9f7d035d3009641633c3ab0b94411e0d80e29c0f134c9eb3aa33f226e67d", + "0xbb5743adf982f60670bfcd8b36a391cdeb739fc0dc8339dd1f7f7cfdfcde2cf4", + "0x8161620dbe9e16f0cb4a6ab95eddd98517c558491f69562f9edbfa00954e7c7c", + "0x3cc15a86e292d4c32c67ed5617f9c1115d65f322314bfbc5e8eda22e746730bd", + "0x89967b76450058d22c86b8f09250fc3052971ba4897c7d592d01cca299b63187", + "0x5253a72cdd454a1c3603fd0b74aadea97cb0a0bd3ebb7a292cd227d94055e02e", + "0x96654372d567a283ff641a0deac507e14a554c6acee483081b3b9923ab15f034", + "0x1049554b1e36368c9469fb9827394f211eedb68f738f219728242d84346a2139", + "0xaee6b13cc0dde2fbb0db1bafdbdfdb66aec05c3510080f44188ff7b8d5e42e6a", + "0xcf3148a19c4dca5944a9223060f644ac54f1b69fb1b9be880956a0634b917985", + "0x0cc7c91515ac3a5dbc7948d57753dd8be04700a3016a74325c5f37b42c418594", + "0x77f5fb3deed80551d5d8ffcd87c87e3465decd2f22b66c348435cd09c290f404", + "0x514f893961313eaa4a3c74584e6df22ca4aa2282218fb06b15b7a4e24363224f", + "0xec0f38302fa4120e1331ebf0a2a0d21a27af516f3e9598ea85214b292e6b39c7", + "0x0a9ce94a4c7812db7d1c36fc36eaf5570eb61a72cd2435c486b45366f56aefd9", + "0x82936a2dd7f132848d1acbad390afc8176bf453c1ea1e223f294e02c6665d3d2", + "0xbf7c773ebf633fbef2bc4d9821fd6201e8341eb6054712c5512a2aa64f77c525", + "0xb9214b95ddf3fde585d808c54d416ec7b3ed4a0b52c1ab6e90347dc3a0b988aa", + "0x44527004392fe3137205152ba33115ded3efc79c863ca4a3ffb29f89ead3207c", + "0xc9b5d32f27b5871c570c4ae775a42b97a93285b4e1f007ecb339fa373948da45", + "0x2516c387dea4b80c70c94727819a171a8ce564e88786fbeaf0c8609cb721dbd5", + "0xd8c10f1866d9e18960f495ff82e4238e55197b1d67ef3a2a50874ee1331ab4d9", + "0x557c519896d6a8121c66e84e693da2cf78857cc7707ac72c59ebafc69d6d3100", + "0xf7e026ae52e5c396a79cdc30c8daa1e39a6e37a2420b9f232d8e9d0d2c956376", + "0xaa6aeb8c685e69608ac4edbab54311a80d2731c81efdd5d4a3f9be676742ceea", + "0xc0faa9faab93b84ed5cdbaee84cac03ecb72f01bc95c1e1cbf38bfae6166bbe0", + "0x8c0462c46fc740b25c914b803a689668b464977706f708f76c4a1f888908a5fc", + "0xa08379436d68ebc8e79c0c4789c3cad5791c4ea3c98245b9965785a306e5fc71", + "0xa41c7c32afc384f9a95cf586b3f1b68f8bd52c48aa3dc196b8b06dd0f0521b0d", + "0x6e6c735b47718044affe1654eda3a97ab04d14d74fa9b000e67233d5526b3b31", + "0x3a69f33c4aa7a892e651ea4fa14ab9c193fa5ea30c309c654c04287003ace528", + "0x82172f7b8b4408e3d9c786fce5eedfa73804bdd1d5a90329c29df4bcb28c1170", + "0xb3c004e22f4a222f3c034f11eccbaa5d20264798e37daa14a10472f7bdcd85ca", + "0xd09fd9ae749749b5405599c1baf894fa519a461d614124ffdf8f8f4a0261bc69", + "0x656f9b89bf7d83c4782770e6c2843681ffde153f344a5cae6804a7ff563dfa02", + "0x42e6bd0d7446ff2661096668e8e4d276b1370b7282c01ac717f3583831bf9888", + "0x8d5c860cf2e4ecb53a2d570c05c89ec9a6f7d583f46fe74800176503f03107fc", + "0x77c7e311eb8e0dc55227d4b5a9cda6f60c500703f252d74af00a715d5d2e17ad", + "0xef558bd7a5566e85608c850951ee8a18e176f791bb0505d8e2ae1b98c15aa0fb", + "0xe48337a47218d3e6eef34246338ec3e35874fe8d3f5e2f709a71f9eaf7b87cbb", + "0xddaf6a40610b00099a126bb5991df25b8b8e6201eaa4c8e8aa354a9bf6de2335", + "0x17e105f36df5239c75a802fcb67c48c3f492d6a498a21b88cda084824c4d39a6", + "0x091095b39b75893a44f35277536cd7b27b7bdeda7bb6d08a3c28527ca66cef32", + "0xdee2e201537044e19854f863cae5cb2a1f032b2c269d74a0078c71480c85b7a2", + "0x53aaa37f25f907e0db1ccbdf7fc461b65922aed4d40486b0bbe72bd0ce115359", + "0xc7ac5bb95f9451d9082092266ee01698034b2b238e3c4b9cc4e82040ef6447b1", + "0x12510adec1e9d3de570f33dd558f9b2e1db3cf70e34849ef80215d83cfc69eed", + "0xe98426d42f93a07fdf1e3a5da515ee5c09c277be26a516af044c366408cea906", + "0x7da1953191160c4a4900ee7be25679d3c5e912fcf737262cb01185d1c4641daa", + "0x9b3c21b47acb34343a9e8c1f8cdb90e216460f23eea3b28b64af96dd4b45deef", + "0x76da76d20b9fdd255d21853f9e54036e56c19d5f120dd74b4e4298df1b0f2f66", + "0xd4cc50d934106b2eb9bde5ba24b6eefb151eb13b2a8ccc953f8980e961c28c9a", + "0x9f957468116472bf4ef0155993a12a926d3a740c4972b1168ee40e44f2dd7fdb", + "0x57b9df5547ffd31afa554978843d1429efe3005d02eb081339c80f0c917d0ec8", + "0xbab1665d1f8388de25351164af62dc0503a11a711a15c61175d806d874bf981b", + "0x4bb4a975fdce139d4c7b595f20636ebb996370267631d813badd9afb7a550557", + "0x89da3a0c95cc5c1ff8a33f586c256b32642c9935eda6b788392ac9944d72e279", + "0x4430eaf15f9c32aa0d1943f024f7c41d58db97c94b9b9707635a33ec9fa7a859", + "0x9f13395b2679d887a60c213d481d57030d008d8d27e52803259ab099d4ce0909", + "0x39aee73081bde63dc28b66e938c1d1acd5ce4a7ecc07853e7e1ba71e27987c7b", + "0x3490498a08fdbefe36d1d17abf23a65a00ec4531a95af1fb76fe9d7918729a1b", + "0x4cdfd482c90422877269a080f03d300ab6363f31718b641443db763c2f2d185a", + "0x01ce385205b93113276028e133b88e67a6b4c05d76f0543ea1011a9ac5b937b4", + "0xdd9b3e345fb1223907a34de8e73fab9f9953bab879298bdd5197003362dd5fd0", + "0xb784b9ddcb31b546504f2092e19be557329b29d27e2aa4f27f1bf1cf0ec5f2c8", + "0x329d8bfe3473f1fb7f98fefecd9e2cc02743e48af624f97b5d7b33e9ba4307ea", + "0xc183e23664cd656734e06930722d37b92ee7a8272b5e120dc35a626a05fb5913", + "0xaf8486c97a34a1d7bb6afe59772a828e5d8aa2a071394ba0cce5695d046cc70e", + "0xa491463c2e574810b1e55638ead3be3a8a270f0a3b241209521e9b12298c1d4c", + "0xacaddf8b8a9af16495d92624179e285d553a351a8028962c94d26b9e147c86c5", + "0x795aef4f227250ec7b793a0499e1f2ed9f4578970472c4fcc0783b3e694aa9d6", + "0xd1cac28e545220f253fd4a412354bd25cf21989f8cb5639b51fbf529bab1c268", + "0x745ad77a2f17366caa78f82eaed32b9c742834168d3bbf5d5759767827a6cf83", + "0x6860da374fdc0ad7a6af8dfa7231f3e9272b3f6937461cf618cb880a516da23c", + "0xb3ee735e3ae07b14fd3182aca7638f9b24ab645d3cc2274a86778e5741e8e6f8", + "0x48519da6b51c0054f001d8cdcb84f2cc9300f3dbbecdf6224e1f9deeaff14345", + "0x04e42046714b9355b40ab3e5f2ccf6522fa91b03994f4d5bbdf6c7143ccf5882", + "0x5565c7bc9685f6d39fc7455eb7c2cb4ab489efa17339b751b670caf934d215c4", + "0x5fd490516f6dd87864345e103f669a67bef47b4815bf125c5b757ed3683db772", + "0xc7bae9c76430e3ad10d5dd85c475115249d4368afe2d86a471c670c2729746bc", + "0xce79c9182a6ab4848439660e95b251b5f76fb0029afc3f33c7b61807571c93e8", + "0x1402a3a2ae67aba0b646bada530063c84324350f377c4e09cb4dc39871214fc6", + "0x55e6565d948e537f871026d70f3d4511d7815ca2d9e0a89d29dd8faf21f7ddef", + "0x8e30938340bb68cebd756f70922d53522abd5d9cbb1c05923e73ffd7358c4d47", + "0x279bb2bff819c8eb688b13822dcd8b87492475f0aa40db04909a64a27293028e", + "0xdce4b253ab7ff47d87566657a635363d58dcdefeb702a76b2a6fb7f55a7dec18", + "0xede6ce512737aae33f3e78c96e8ebcbcae12e4fa14783bd8ca6a8f8d8977536c", + "0xdb6fe5d20ce8cc0ce9d50bda8b156f726c1f752c6803bc63bb4da45acb4ee822", + "0xb69a6a8e49518d28a68f740f92a2251ba49109842fd632fff062c5e2cc319a78", + "0x1a1ad1935d4f55e7351586c8aec53af7c23a3739dc59e25b36e2c4f3c778c34e", + "0xb3ebb84ff092ed823ade9cd13a005ba0af65096ce3b0fb59c91288acdcdce93d", + "0x651c9fab077808dadcf7e586c242299616717947cf0d2d85e3f38e5d1764fcb2", + "0x62566d0fcf9f9156a9e7473061d307ddb9527a82d9bdecbd5f9786bb4f5f55c3", + "0x036549dc4e8c4001a4885fa062017a0f0ed8fea5dd3dfa89ee1c273e2d2c1b2d", + "0x18055c467447282b9d650931d3e692a39e1088cd780ca669a57b39cd4fafba5a", + "0xe0f61ecd65efeded186b5be8c5292b48c33f10b24ef6c5b84ce138b1cdb47306", + "0x2096487c35f7580ebdd240a2db04904a0ceed09c0ff210de3e67faaa4e504477", + "0xd0121ac6db322e738dcccfcd2148f29196c4d26ee0e2bdeef2aa6ff30ff66553", + "0x5317569782e73806119582cdf623f65cfea66ead49e24a37fd50caf54d619ca2", + "0x136b5c94aee8c97e7a1b330a5370e4a141c2e1c17b39ecd0cbd137f5b2cd5588", + "0x62ca71a93ed474b3555d9871dbfecb760c10cde63055e4b8bfd35a74ff766ca3", + "0x17376309694bba9a2202055b4aa344c5132cdb534e1c7c2c48cc0fe379177d3d", + "0x5704f46b996ed0a03d4fa80bedbe7430817502aa9b015ee892be2357c3a7b836", + "0xcae02279824d475b99bd6f3b08c5554fd136a1a536f8db123cfdbea26b44d0b0", + "0x1b48382a0aad35793a69fa4ac814e497c5df8770b6993b717f3b2244aaef6728", + "0x3b19b9ac3a36b68342b9b9895276e4ed8e32d8dc3dd67dbd80ec64d8db2ca049", + "0x715eb18df03618e9081ac87dda10b5ad1d2e41e32f42d2fc63e8b8311ce8ec2e", + "0xdfb558e7ffa2657bcc7599cfaa2ba63414bcea243c075e40081094e4bd01fe10", + "0xe546ed282e34eb51950e4c221d87d7fab138a18b2138844b792d9feb3404c69a", + "0x51e805c694fefb90322c63d682edbbfbb41dd7d28a40cc13d3a6f42635b336b9", + "0x0811bc979a151602d248430a772693a4efb80d13330e2e5660caf722088c5fb5", + "0xb54e0161ad3a2efd41228fa10b634eec5dd5864a1087929157893157ca443594", + "0xb79fc7567c7e38d39240b46235f0b753e4a33ddfc79bf4a0cb88a7799e721374", + "0x826d23e82db9954a576b022bdceca90f9109d09ace8bb08fbdc300a73b81ebdb", + "0xe3c4441876957109a7d3a3154107083fa2a794579e93680e8efab98c8f4e1dd5", + "0xdbb431992e8c04818d2245e0f28f3ecf809d60bb95165fe604fb1a05cc041717", + "0xf9bfc658139ca39a72f88322850909326f7f9d05698b2e241b8d0b70588d8d99", + "0xeed822394498037228c562f3b4d962c62ad90ecc99d31c13d5aa123de9ca1520", + "0xcac84d43e6ffe1b549faac8e557fcf8034f4e34b226de6c8743109e7e7fc5d82", + "0xfab45b329c5084a92aa21bd45222eb03e29b6080b4830389136fa2462a8a5a0a", + "0x68a4560256afd84becf0b9c03796f42a6a13f6e8d049e76992684409e17e0895", + "0x09f00807080ffe0cc563b0a13b9199c76a3022790301c5c7816a0a6410b35ae4", + "0x63a411fd67a351c478894c243176bb9d95c633657bb48311de9cb768d07d5c3c", + "0x85b9e219437f9dbbe1f929059d8db440956c4aba68e54e7a8e2b9d17560fd494", + "0x23a6789ea96a96b6a25932b92c52991ba18007726698a874d22fa8af8d669aa5", + "0xb072f72ff09a05c9bebdb9696524503bc489519a18d766084e9fe6c27fd433fa", + "0x2a14bc2f197f2652217d646fb58760584700317b5d75d5f8f0603a435087feb7", + "0x2557eb267d7df7cd4723a555ff80ada0ebf65d329a51c8c9843d38e5893b14fa", + "0x263b957b6fdc804dd86084b4e7d2c62dc7995928c0f8f17701325210754cece6", + "0xcc48a280a5fe4bcc9d9803cc277d540971cbb2d44783ae7b5bdae44b12a1f392", + "0x00b87fcdf3b9847e21702bfe14af0461292305fc98cd00763cbed94a538f2354", + "0xac8fc4d3843d4d945b6a7ef173444e56fa0f9e2ffeec9dd3b82da2186fb337d2", + "0xa12cbd97e5fb7d9c279039da78b9c10d29232062b7e78ba5784f5e08f6433587", + "0x9883d7ae745df49cc80160d777015999229de81d02a4c1967c4d7f7c75cbc712", + "0x4e157753042af0ff7d49583ee1b9c3c7d75b247dbe88ab645c255895e01f4240", + "0xad0a8e473191c2ea4c2568a3151a7e3dc7c01d1e4783c0821b9c1a8ec20a3435", + "0xc5ac88e4e9f52577131c7077fc3638d28d74b5d9b08ca9222dc3aa2d5e958406", + "0x4be05e85823cd97d791a43752defe21bb8ffae38f1633589d67a40b2eff3da11", + "0x93b5da20973e4bf40f2acb12c6fa13aee889edf21f7a2548502d45d69029d3c7", + "0x42250570c4cf13acb3280003d939194a89290cd74d78f30f75d92c8a3e6b0ea3", + "0xd39f66de36411360cdcf979d5ed91b413a31c2d0e9681d8cfbfdc28923e01164", + "0x52df9a65de87f7ed9c6ae29abe968e0f6df09c6a5d9e9eb00d38cb4eb06e39f3", + "0x0dd4bed6fd3928fe7d079e322a9c5cb9b30cf6523ee34ad54f1f90bb42d03382", + "0xee3c23c6c5c49436aac9c068f18356db7187f76897043629e6c3ba280a587def", + "0x630ddfa7fe5d2a8c023f610efee56395de9215b3a84e810bce7147b92563ac1c", + "0x03a5dcc4b757eec68f36b350e3dbcbe9584bade7f9eed5588b9bb628c125fb6f", + "0x72f012466cfddd4b2e133b3f7785c52738ab8aec67f7baba90ab017dd06c3a30", + "0x3b1e1537d1cbc876dae68cb2d0ae59f1288e40dfb9d8bc9a76bfeb599c2782f0", + "0x5a3523e7f7fbcb8651bc5936bfd62205add061d8d7278540be16121ccd6c3b8a", + "0x1f4de846bce2f5031123f39f557fcaabc8f9380483f2aed01dce1d158b8e7463", + "0xa06a4d8392b9d87078e5f271ce87a74c3b851ad1a468107b2b59e4e5fab2b593", + "0xf8922e8f8ef3885aa6889f84059df0b4ad314e2a3d450249a607dce48a96da9e", + "0xfb0693c0e0e0f7d6016015a29f6901a39b075633fbe010a7d2225ee21e2256da", + "0xd79e87624e270ae3d3b3d6e265e034d9df4cb8f9ca983f9ddfd22fe4a441f674", + "0x4c35e3081805137258494f2e9c2003baafd1e7deb6e8ecdefea52893daf0b328", + "0x0c6aba36bf7b10c450f01ec6aa84e4dbf44ced3f8d9ed6c39b986aeab05bb9b6", + "0x234a2e9e0209ff8322412a05cbebb0e3c3ca5170e56ebc0e8977d38962d0e87a", + "0x69f59f49c18fd65862a3386f88bce89e6867a4395e2115b5a751bb27243b46f5", + "0x66971092d3311f93125265c175170920eacd7616d57f35ec0dbef5f8233439e1", + "0xbb84c87248bd57e9a7daa7a3a75c537d99d8d51e0ccea37864c1bfc0fa3c3db8", + "0xd4d97a409ba76e515d6b9d68fac3fc2de21e5a9caf0c0b7341334494f85e0c11", + "0xd5dea7ed20505de5b79f00f70f0bc1be062b07a6fc80b6385564a5b68a63e18d", + "0x5888ccea6c5342500b28908aa63cb31b2bde19462b3880cb2cbb10d2bc84cf3f", + "0x7d2ebc9521b6f9a4e12d2e9365b8ad2fc9b043f336d1d0b64467dca873700730", + "0x5e94efeea26463dd4a607a52ecd093a597c8a3ad4dd32d64f2d59d6c9ab8d6d0", + "0x5db13c02efed7cbaea538a3e5923c6b9864faf8b5309ddc92257b520aec20731", + "0x5ef41e40120ead9144b615b86e8e50fe73ee452dd936c2e808caa4879534fea9", + "0x33befdeedac33a1425cc7b65288939776f6e5c6905f366de45ac2b0d18bfe993", + "0x61fc26ebadf4899d3f97f7726204a3fd61883dbc2ac72b5a09ce9697d8fc345e", + "0xc8f65678dd9798372c377c17e716633287491735a07f787ef981f09578776e68", + "0x479fa5a5fcc3653b21d777c8185348ee940b0382bf145d1100656a6257ef4c19", + "0x6d27bd8084ddf9a21142306158e0e07eec93e63fb4438210f68513c9e5850ca7", + "0x02f29309829b53bfa955bb1a7430e5b660844d4ec18aca96bdaa33bd7afeb9d1", + "0xb4f2845c3cdcfca420316caf3cce4e6bb22390d7a5df8c00fc34833bede06e51", + "0x095d452b2eef84f7d0235ee6e091db3147a29c0c3c25b1049072babf2ced42e5", + "0xba7e07b664fce1d5dbdb53c7cbd67dfb28933365d2dcb80b64ee40288abd5553", + "0xb80089d966bc764fbb8bd3f494f4b7b242a3a846a020eaa81451a856d9b67b67", + "0x78e48ac7aa983c75aa7516c6f664a05fdf0d61a0e20c42018d0addae917a510b", + "0xcdb61db5a693d5387f9a212fe1a79678dae34682d202d97c24ad2b3cb84b9d44", + "0x9df8e92abbc3329df665d609f0c2009d212edfae5310f8a1e1a5d620886c6305", + "0xde323ef8a91f545d89a0d89e651bda651ac959ac2d640e36162567597b423688", + "0x07b7a511f8e4bd5f4107c1a9a494e63f2b2a3decebdaacebc71531bbadf7f90b", + "0x466353debb1b437ddea3ee76491c95a750ed9229f3ffaceb685b202619bc4874", + "0x1ceb98b191ba46e387ab22081169318b84c62310a1c3f4d5b1bc28e7393cfd1b", + "0x8e317d9b65368251f093b239a7d78590f1558e915de84b2708379137f8308922", + "0x6dc0b19799d058decab01a70292028afa88662fe36920541f3469df5bfe4710d", + "0x52e1e3f557a0727b206372c37a40c47a190c5abbfdc6dc0f1e5da053fddc1a4d", + "0x627412c0dc76c946d677a3bc8655026dbd4702a20e3e91988f5582c82eead049", + "0x163d01170b447455487e4826a690deb1a1346eaaf42d4139ea90357131b1f347", + "0xa0b362e9e2d18397e8104a3c0ad937a160de32e21d57e444cdd68e99ec5b859d", + "0x7c62690703d1738a7b9d58eea3922285df30a25f5540d66a84d4527f70268847", + "0xbb56b8c4195ab21b194a6a86cf21617cc7740c64668eb63ab2a0127df2a9da57", + "0x75cec20f80767abe434a577e1f8c62b05717cd86029e9818bec9bf2b6c7f91dc", + "0xd649da65e8dde2befab328ead2acbb301fb571dd93dd871c7ac13bfa90619592", + "0xf1a37067506e02a1e25be1bf3b7e7fc294b938c31f8c377a51d92592731127d3", + "0x262f15b1992c9ab1223f0044a043704c9ea2e556f1ff09ae869033850ced2f3c", + "0x2745f6484f2b90aa4e614373966058514ea24d3ad60ec14a65577121d5dbbc8b", + "0xb42722a9aada692b3798d7da9efba5213ffa3f8476c5d61eb82a9c622c757d71", + "0xd27e2962b32a41403a201d08aa388d84109fa499a1c72a73cb8c2e369fac603a", + "0x5d52e976a2f41060a7a31d727b6bb6ab2288f999e750158d46be8ecb4ee85c8b", + "0x2eed0fba144881e4372bcc0ef7677a4a0e12d9a9850a2e0728fbb4756e5696c3", + "0xc7b0b7dd911d14b58619eb2c1ae2b11c6f8b650b9b40ef408526f79eff8023c8", + "0xfb3681d7dd9f8d398030693b57fd469f2f937db903a5e4857674ab219357b03f", + "0x65ddcd3e1adce77cc2ff0fcf602c3eb18a5ca34ea001d3c36ec79cdc0c85c16e", + "0x140391ec088c5d7d8f9819c8012910dba271aa15d4d5f9f2f9acdd8ecee6b16f", + "0x2c8dd73cef1812d68dad3d28f8e6da8c516caf74ec0d524cb08799570bfb9d72", + "0xd2e1f6f293b6f0d54b71466e8aa809fd8b645ba7a8e070e08b4e74c9deca38f4", + "0xc24b4780fcf6fc852d963e1175d0e6b6ebcfb02593fb1a9109234cc1558a0a39", + "0xaffea114c80c71342760a3637d845babdac345a177a1d54aa12c99ba4618c163", + "0xd828e7902b3b711bfb27b2293cd262952a5834a4bbb262e6c13214065bbeb178", + "0x200aa8871886a885f50e752d1d7f0140dc7d0da1d4fecfccdcf12656e6284992", + "0xb68149354201e4eb3876cf512f3f0eccf8a7089f1f51ccc88b17e2d0d2009466", + "0x7df3c0b3080479d0b68424e4a93aa185db2fafa5bee885f45b90bcd3cf55c8c1", + "0x47aa087921fd3a782b08763b2a956dcbab37f1b44c6a7f4119c614644fd6ca36", + "0xc31debd3ef3f71e52d74ecc2691f646534d362507857bc5b764118dde8a7ba20", + "0x868ff3fae9bd237e973a4847b27401f96ece1ee41bfe9398c602a28ecacb5e92", + "0x335df38439dc591991c6369ce91a13acc2741e9314b8e814fe66bf08d00ef2b1", + "0xf95106e5201829851f0c15dbb6d43a1108257f3d5b50ff20f8b48e3133ef3679", + "0x5422d1579a87fa28d4a614272f033e50debb0b8bf1ec3ca1dc9a6a7539f54892", + "0x3f3240bb6b9b3d8a1754c828819b691bed2ff2092a81dab8f9a323b81eb2dcb6", + "0xe610ab489c132bed66a1bd90b40145d43d2de9292ea9d88ecc5cabf1937b51f5", + "0xf98ea9d2c258269972c7516fec229f05e19093a6894ee1a321c577134ae3b3ec", + "0x4609decb7e472bd5e8aba4c09104bfa81ae00afed119dd34b5a35795435c4a12", + "0x5096c18f036fd8bf133a39030c971d0cf1c2d4d9b09bce26b18fbd4253201dbe", + "0x0eb066c3aa1893ed15740c85510d1429613e409f8be8849c57119e8a93c3af19", + "0xf053d2a57c9e49b39338a3057d18a83604b4a9789b0d870cced6564545653182", + "0x8c98a34b93f1ffe8190ab18ea3c76734a469456bd880243e991276c953eff200", + "0xa36a3e392fa42872048e8309e66a74ce1164f01a231a2e869bd1f07d7bcbc350", + "0xad2b146b4000b8f8cac27ba746ba4481fd107884b885472ed01acea293f052dc", + "0xec76b4f13feddc2a07e6d4be467b1d68b5ad7edfbba552ea308dbed117a6d499", + "0x8f3ad56ea0565df3de6d83e7456f330ba3a136aa59b830b90d5489ebad1f85da", + "0xdae53d7760c519d540af330d706440449cecf05d026a81fae6a3b4c676f5e704", + "0x6147c9630a5bbfa19e524aa9081620c2bbafcbd908776647f2ec06d0c2a1b948", + "0xa5aa03e2ead8f1310a229dd55b13997a6f95f06048a7117d16b3d5ec9b3b397f", + "0x39ea9308e45563d3e8e95b325541ee78c3c9d4d6585695cb4ae3291316e8d7e4", + "0x2819a2ad209305abce89be035efe0d920567244afa3f0724efd545828591a60f", + "0x646fc61d66cb736dd25cfdc4eed1b1835aa8f409886e8a346da965e8c850665c", + "0x3876b915c832067f41e8582c72b5934031f3bfdffc428222e4346a6fc7fff943", + "0xd2085acdd607d3b75ef44453f6d78042f66221d398f450344f665ccd01472e89", + "0x0655d92087eb23d339b3f508bb233cfc34e829d6da9e9ee20f99a5c6b835f330", + "0x04f9fa2dc51965d670c4dee4672d47a27525fac0e174649fda0a4337f633c957", + "0x05315f6cfb02defd8949868fa2bb17b54bd84bb9839095e3df5abcb077e20af9", + "0x807b99d7b2f583933b5bc5539f19706cc6ef66b9c8574ac78b450e35e4402c75", + "0x97a9aca543b12a92213e08e6f9311beae91e19b225d2a2152da04b3c3e9c6b47", + "0x0432f012cc94d97ddd4a41eb66f777d800435e0112cb013cbf2fb3d5d777b9d5", + "0x5f4047b17bd35fe80cf3def41fb819dcd1d2fa4b08fc5628f23b387aeb8486dd", + "0xd6033c6af8206a21d89804e57b0600d5b81a4bc78d0817e41c5bb99c13eb513a", + "0x186251c1f43de531f3b9665046e6d621ad5ce0522d6c1903de62ee953fb60e87", + "0xc066bac519e1c9800818e81baad64ff6cd7b76096a29e0898a6136043afc9338", + "0x34808012719bb5a02cc12b7bec2cf9aa101c171397e137a9fe4567aec143a011", + "0xae8ceea894f28d34249c00ad465a552ce7d5e427c595efedf79856a610e1036e", + "0x49e646e25efb04275169d68da657ef3be5a5f2e10ba74be8ef323b50824eb5ee", + "0xbeba201c0e9650a7b486fbe748eb6e5633acc74aadafd87f005b91b9f5aae156", + "0xa41aa05ccb33fac637015a90f61ab13e30c89606a3dbd5ed190c5ac2075efa18", + "0x270a8ba67f32aec0a24057743bb115f1ff55b716c128d1de3408a8ddc0bdd34f", + "0x393ecb51dac3b370d486969ad87c096ce54e92c253b7114d9d56357389660e13", + "0xfef38ecf25703e131dafa1afb836d6e2ea80dc4702f05947bd8447bb34f1cc5d", + "0xa1778e57f3201e42d1c0e8131f065f9f07d676f50626b6d9efb1df362fb97362", + "0x3030b89dc80a3ebe11583693c321d69e9d18b3dc3f42e00668cbb20551a0e7d0", + "0xce6b341e5c55cf04b2b2c8ef4a0d1de58028790aa98b52e8ff9cd5f9547f94e6", + "0x4e068d98605489478cbbba72fe23046c7e0b861da3cd0f75c0e6620ea4bc1c3b", + "0x82fe1b200892b1352ccfc9d603c65423e50548ca17bb5cd577882f9a26f2b256", + "0xf88a58d0aac8514f61e5da9018d95abde34e8894e652ec6d88a2f47405abdf26", + "0xebd66ec41c38154ca01cb574c33c76fb45c606bab719b00102d04a419904712c", + "0x47df36c41811269daf8f7d1bd26f9dde6a99db7b23318d695cf4acce8c107868", + "0x36477d75565fdce709db2495c1a26abc1d8812db927e4660288408faeff03843", + "0x38302fb02691ad6d6407a36869440de6a050e48f98cec05005738be5f75363e7", + "0x491e43bd7be35c0dd9f652d6b49f2f64593c5035d692233c59a334a284a046a2", + "0xd9988c00312927deb008fb9e321195533d288141e3725f607dbf7994dfa1988f", + "0xac9cdaed3c337893e2efb8ee2c519be49a59dd90898516c9f5c68be0542b14c4", + "0x162dae23d1ac41a295a4bd859b9f6d795f1cea18de60204022d47c893f5546b8", + "0xaa6cc5ee511a610eefe7c2a2e9abcd87080e7e6261f76db5d4eaf6d38121a09e", + "0xa38ece67bff8157d670b9e9b7395b0caf526302b191046eae0315142aa17b0a6", + "0x0cfc0620199c69a8728759d5bf490e6eed0329bf0d87e0dbc292bd6816fadad9", + "0x36a9071e5aebea1be0be3ea414e1e030d780bca24178427b8bd1e4e2d31ae81a", + "0x805446c75a004a22cde1c5c56064e1e8d75db2ae3436cca5806daa885119e65c", + "0x8849e215ae24034adae756824813a0fcc7388a1ff1c1093db1cab8452c79abb6", + "0x5241103101ec017d6f80688f0cb503f939de7243355d88e45f90256ae456c4bd", + "0x340c3557e96d3f4f8f15a12c78f04fd67d9e21726b93cfa7257ab6885895080d", + "0x17807bd91224a10dcd99b007fb3be5b060d15ed704b409dab88847ece38e3c91", + "0x1d8e89fd738443493b660025fb5315745991627cd235f505598f3370d146d937", + "0x843fae30513fec4d46dcfe066a8b57aeef994b3be804e11613b83b928b03293f", + "0x32460957383f6f950628dd676340482746793c3ef36221d10359c17956524812", + "0x932100411906b26b7f26121596df22c00120b47315f0ec1538a8fed28e9904e5", + "0xf34e0b743aa8b062d7f89611de2709c71a3adc1fc83c849d32e1cc4062ef4dd9", + "0x5d55ebc3ab5ed937f8e809ac83bd88c44a489d76ab4fc0a82ec4636fd4ec51cd", + "0x576ec8e986dd77a9023c1e440702ab76be509bd6b08ef9db915d683b2708d1b1", + "0x9cb5edaa4e3d3a719c22a82a72d88351002cac815e37d145247eeb1bde310d83", + "0xfbef3e483d7e42538971db95ed40159e6a246063138c5b3686dea6b959fa5872", + "0x9795f6bf8f953928a0a82ab82661afa69d81cd95ad7280fddb40a4c6a5cda0d4", + "0x3e41a9743a0dd8dc6888365d9365a795a15f5df91b9a7ef3957b2edb30480cd0", + "0x727cba7d233abeb3f62bee35c5d950bd8a84c490df7cba5451cf6b037375867a", + "0x8f71371fbe33f8622077ee7cd8ac2015142722f26e8a225626903f8f243b0050", + "0x28b1fa2cc2d7cd92af0e0802622b97369615c3ea9e80dc7a7cf809a2cc10e45d", + "0x48d4faa026f574d5c146b04624c810efccd20b907ee5edfb8314945c5e16ed3e", + "0x1bd4d62c5f5376b828f12654d9328d960576a46ff33247ea08a2ef401059ec85", + "0x067ace435012147164e4a22efa86c63e1b128ca660bd86579498dbb10b85bdfd", + "0x122c4b00960b2f511b904da96ae8e7f2656cde7f99384949e7261a714255368a", + "0x94bc36eac2a041c98752e2cc14f1e890a21330954aad8eb1d1678e34cbf241a5", + "0x6d11cdc0ed4c9472ad29b7cd967ba96d3849bd29e9bb0d1130c90b5e2069b979", + "0x8c089c27343fbd5b040bcff86da5864bfec003d647be68d1f708f9a1ce31b933", + "0x04f75f7a4ef5d97c5cef1a3b53866f55179add586d9f5f4ff8a897306d4abdc0", + "0x3fde7a4193d0716c4bea92f6eac115a5ea5891b908cc7422a883b99a5f0382af", + "0x81cfac3e67474e58e6d560f5b81a6800f384cb2c3609f68a24a7ecfb7961ec6e", + "0x7efe18177a9ab5a39adf2c315834dc133cd66321538aec118e7e7d8484e23f65", + "0xdbeb7a57d17e25c31637803a071593ad1fca909c7949d36f7c14e44d501f200e", + "0xd255c3bb873d63ada01510a35692a3c07ced6f0805cbe85c65c963e53925cd1f", + "0x992d79474e97a7cd5391b11caf6ea2c5406526e479b5676c188836af85296667", + "0x4c39b3e1a053b15ea85c376604cdfc513cb0a72dee18d30a431c52661a4acdde", + "0xa076a0796d2a453be30a116faa2bfd13c6d3efb60d1d26ff915e13a32947deb3", + "0xcc249c29b29a244f646518913ec23ac02517ffea9d878dfcb700d693feb30470", + "0xfba1916a4c25237259c2e82c596c800ad4ad95d810e564adf7d21edac08628ac", + "0x3724d816243deddd80fb769a172a4cc28974e31492f6ee56bb2c22401fb88c9f", + "0x60edebde9f449298c0d5344b07f0f1fd8d49c8cbbcfdc4eb89c48efd27d3e947", + "0x4e3cc3c5f9b719391a9b1daf2333797fe6ab56ecf5cce1c9292e7a1430de39b7", + "0xe47e010d6ddc4d283d439dddee40bddff10579022acc83e09422b5802204b5de", + "0xf32956abdb6222f8fe245db920134659e3f82114f7b354e418bc8a7663c5d9f1", + "0xf6267ae62f46935994ec14c854e87265ad3e0373107a3031e9fd35a7b1be0dcb", + "0xbf0bf2133601aead9992a7f45e275fbdaf3d2f7324bc120c8dbf9d8f7d248a72", + "0xfb5127bd0fc17b0c095a867635c425f46651eca3dcf3a4597d36d311321e9c53", + "0xfd20b1786923da63ea886c4e3bab917d9a15afce5b44838b23676618bcbf4dc5", + "0xba536950139d6558eceacede7648617c5beb05e2d56e3c0d2b4a22711d6cc3de", + "0xff705366626ef106191e2d43d341b29a56e02322015d72ce8b48cea5fb522231", + "0xe4a0f52725843e70cca4c2e3fd0962c8f41b807407850c1d95fa37aa042a945e", + "0x1ea3854a20a299483a1cd48d9c2b20154f9da1e39ff680014c75f8523dc1d542", + "0xb7d9dfa52c49bcdda48e1ef29bebbf4aa86630ab9fc14124b735fbf187fe58e7", + "0x541682435c3e06b1e3ac56c23ad05c322c81ef5426431fb7935c930792806d94", + "0x9ea3ccb9f5472339412b1665be7636d73bffbf68361cf6d061f10bdcca93ede2", + "0x208d16ef5a868d0f45e9c37bfbfe3f24640d262865d59beee297e3cfd5da4415", + "0x343a18f719303183ab4d8a254d1ab6051d48f1b8fc9626801c8ff4da4c3cc680", + "0x719c7ed4aa45133a2bd8ae7511f143907a327695f9aeae0944f26e98f2dcc444", + "0x04ff3f4d0af7f6899b8cadfd02021ecc3caa496be62f2ca7ee5fcdd853fd74ab", + "0xa25f4659d0ee5a7b2575350d20caab0a15291fd1d2016713512dd0abb46d7a34", + "0x004358e4fc95fca1f907120a7ae5999a3fefe2fc67b2d7d12dfc1874f571420b", + "0xbc7295e36365bbfa10ee06a8ea926b3ecfc5982e66440c2ed76d4ab1ac5e371d", + "0x0f9d5b1d2dec454f608e03c152309c4ffae7c1ba0819e9dba4ed806c3771ce3e", + "0xc5e5da581eb7d20843909e2ba3d08bada628022ea5d5189cabbf4bb65733b991", + "0x3132f98b9932f810a4ae288fa903d6420913823540998d40c75ca77f28f2e79a", + "0xe22e489471ed31bdc5f225fc53a2f0ac5dbe07b2aa6fd3bc1be316fd1496426f", + "0x885fbfe39ac03b8654204b99aa309ea29dfc8a6a4ea63b4c56f134a2a26c8a8e", + "0x431323b5d23292a948a4df63d7e6a2a9ae4b6f5b52ccd4f9c6ff64ca35ce0172", + "0x64841990ddc1119dc88e002b26aa9bcb3e7ae98bd8ac1c579b028773f485537c", + "0xa9046755a39f31c5b02dc271975ba399c57ac0e6214eb86bff991224a012f270", + "0x37f37411617415775cd2379bf07f9dfc4efe1020baacf97a7896038b6e14faba", + "0x34c97ad8ffda8ef0b182ff05735865942a5ffbc6e720f5427bb42d7609cd1e69", + "0xeabec8cfcfba73ee6410021278fec53beb34fc56ee96d8bb513affb6a637c4d1", + "0xb176f80d6392533bd932052d6a2595e8a385bbf2e2e6ce4b3fffbb4ee9c04a8b", + "0x45d7f42c626ceafd7496dd95cbdf037d5f8b473ec30405698114ccb70760cb7c", + "0x42cfdb97d40afdcfa3aae882c4039dea9bb6478c1909e5c5d16b37bcc594474f", + "0x1ec4e0de62742be4dddb06b45ea186e82f16d2a593553e03a46b95cd3730e5d2", + "0x16d985b60abb5460c843d43b8e3bbb6373325a18ab691a84a473db0185e3182d", + "0xcddd1a0b96875e94b672353df059013f7d4addd354f4183474da566961949f9a", + "0xe2f4af0ddd4fb4e64c2e42d560a95e293ecef547dfa391c48da160edf6c3ad08", + "0xc0aa060dd0bb408bf99334241878cf292c2fef61d38fb71e98cea3d756499f66", + "0xe3ac8ba2c382e3d7fc149a3196cfa06fb091b729c825e0cef468ab4241227e93", + "0xa2a424378329ef0a962a65a3ab80c9498bf2d53c7e66b86f55f795537e20a0d9", + "0x83d477802f3aff1769eadf37fdfe9a11cc1cd66c9d224882e5ca33d1fd530f68", + "0xd5e23276d02b00e3107bbdf722552b356118224367b893e07eb07177edad4723", + "0xeada8339f59747c7ede7ff8a59f207c1e1f8558c9942148368ee400c420a4966", + "0x1246785c75e9b2cb00cb8d4f6fc156b0869ee5bb204e9f73a2cda914c6804e49", + "0x0733e8af9b80ebb2a7db0692fe5a530d3a801c4e654d624b26d3dc606e26dc83", + "0xaa902cf0f23908dafbbf8f0ec4ae5a4eec6f95b84b9a63a1e4b43f52afda1e72", + "0xfe5203a7f48624005fb84d1b85ab25b24398a3f66ff5b7d49d2e65473e68d55f", + "0x4e5bd4c6b318fc56b3042de860bdb93c66b48f01d1e6f662cceaede142730a8e", + "0x9004a26cadaa41cda2ab97076d471364b470f8569c0abd35ce0914f0136f5cfa", + "0x4742e325681edb393dfdb3b991ca1de8fd448f5184c833412537804eb71f7e31", + "0xd203cb87e22e21860a20a6761168bee45829c30d0b1abdb7564e4af7ab20d6b9", + "0x4982c6390090d63412e966e8c03f3b78fdabdb067c274caecdcd70a4d9a7c38e", + "0x5d893824487c7f7fd2e17b11de9055da45ce7f889cf88cb0a3d65e25bba43abb", + "0xc1ca37617495a4aecd38c12275845959cb88bcc1852e10f77969896784f7d5cd", + "0x749f4f89338524f2246f442fa81d7f75f2b9d450ea84f0463daf7717edf4e817", + "0xb944ceaf5371c434a45ddad771c483d935ea9f2f842d8d45e4abe8e06cc92276", + "0xa6ce135ec1b6c27f5ad8506be8c158b420f6a3675a8d591b3cd6ac1b9cd9e3b9", + "0xc36967998ea54f00ea324fea7ec1d7277e360276012234b6216e552a480a6082", + "0x55b71545bccce2467169abbf28a62c987277d47655ddc07fe4c2314e96e68b34", + "0x6c7f57c4b445ff8c5111968e40134ffc489ca794181ff3564e4ce85a5e6efe53", + "0xfee558865bc0112ef9a822d06f635a199e081d8ea2bff4f0e375022f6292c57d", + "0x93f5e84b0732afb28c0006cd5a1888dd846a4dbf8e604da2674afc69c615ee48", + "0x0009eaca59f11043e14444b9690cd8a9ebf71d8cdbef2f9d63a4cdbdc1657217", + "0xac28d6711ed9fe2aa61ecb0e5aa89689684803a7cbcbcafeaf8f7cd90c25dd5f", + "0x0ad2589b8dfc0e80d8b323b01c8cac5765982efab78d4644cc1b37fbc2406f7b", + "0xb4a8e7680deaba82a9f44fe2d0c7d546ebb45f08fd41fe237ec113ba3ce40e61", + "0x9eab3b7cbce345b635ed61ce8d4065bfac64da56215c1d06894cf3034af0c27c", + "0x8212b32249ab15dc3964c1af5e4489eb55af99ecbbcbe4976a2856fc096f5267", + "0xf7dd4fb6967e681f17d624e51417f465ab469efbc1f4022b4f06fcb21be818d9", + "0x75ed3c16ec877621d03945922d71bab14061eef258923f4f3262ea1709cfa374", + "0x596067b42775a8ef797722706d9ada152472775495cdfd26c829f8a6c4469e88", + "0x552d5ac034dab715d03c4ec37180f4d503e36a4b1f74cee87e375e88805db4d4", + "0x2d5b0f10fbb397492e02afefdb424c8c3ee5a9ef24e24a1a64b2db8db774643f", + "0xe339f2a3fad35aea5bec2531fc6ec178fabb7e631e17fd548602efc22909793a", + "0xaf385e43aee72f3d4d893a808bc78c812956f9a71a1c31af8f6ecd2d9dce9ad6", + "0x1ec0d4ce3a6917ca4cb1e0c0b7cb1ff0cc66a5fcd95022dd4e3e76daeae5544d", + "0x354bdc907890a4e22f810fbca4fd1364bfcdda1e35b4689160986e9596f93669", + "0xf5a34afae11c81c1a31f4222395f984a340582fb2ff571420ffb6d04d5176791", + "0x35a202def764addfd7221a5afffdbae3087ab7fe1d577712da3493518ee27636", + "0x3f40a39af6fa3cdebf442dbe4f4b0b67d4fdc69cf0a12b8b1d0b48c16bfd00b9", + "0xfc2aebf3ccf254466562fe9b52f5f79f6099c2d37356ca353d1ca3cfb39e4c14", + "0xb895da7a72084cb5e0c64e83968a100e86538b89e82c17f3d64f3ec0536804b5", + "0x1a69b1fe503cc4be2bc67583e8449ca5b353f2c74e2ca3599eb4cf6eec18467c", + "0xfc183e30fe8ee457a6a9ce93c13eaa78ae66c070686f82c581198c3dcf25beb5", + "0x72b964a916f5b34370f73a7b7ee67b6739c9a2f7df3473d0498cc4761805310c", + "0x3db14169e05f1050564764d888c7c44026aaf9d1d3eaaef86d1c8d96740339ea", + "0x808b3437b74d85242d5f9474f0af1cb89e36337547608ca1ddac00a2672e952b", + "0xd80494e2eab607af700a2d559a5986487b09348f013c9e580dc01590f253bb8f", + "0x3eaf2dd547ae4d9288512780ede3c8700502de3019a8f24f40fc036a0261257b", + "0xdb8574f57f00336d95106305d6c58953a8d3dd496444f991f84a222b3fe9ab93", + "0x667277994aed7f2b12fae7d3763a0f5aeb1fc11b70293dabdd2f1a4097cd1aa9", + "0x4891d6560fb3b8932cb3bd6e6efab04f28c852ea8f5e52a80ca7a889a2e6cca2", + "0x5b1a403ad765c7bdfaf08f3808f978eb99e57da58f817c8c136ae3428c822c36", + "0xe143e11676f7437b2d44d3f6d56b931e04c08a2893e255d099385064fc7304c4", + "0x0d6506eefccd7326e33f305afdf20f5b1875bdaa6a9f7292295fb18d6d0571f7", + "0x7c286cc146ca343ca07a162614401f87de8a98e66836833083bbaf4fe8e5ba82", + "0x4f4b217b0ea7b3887cdfbc32647ca60521adee87571824603529cf77701ada06", + "0x41a9ed879b5d1a0a4e15644461296b8a39faf69aeafef48ab263e0625d780a54", + "0x79d256c7902f64c2de3a09621c5e8f15691bfcf12c298196c89909f0be251403", + "0x8c36c3bcf5b8272b727564722d22cf2446184a1721e86b69059de1a576dc585e", + "0x8616b848491b0f3e1322481662b5edb256b4d6af97c31d9035e15922b3245a04", + "0x402dbd82fe36aa031639b35c3c6a89c3166e9d24c733eab71cfa2770931781a3", + "0x7872a6c239f8d78da1b9065129364c2b20833aff46a3ddcd85dfd0507770e27d", + "0x2eb1731d823d9378493463e6e73d8ff88cfbf8edd7196a137cc54a8a88123a64", + "0x597aee20dffa4cf46611080c6777c6c81943fa1cd219b8c620e52bb8499a4789", + "0x7ac1ff4064f6401faea3987fde6deb2ba0d027bba658c7fd85301d7d7ba0f393", + "0x2ebea0d600ab74154fa83be0b09a7ca24441f16ddb5678792a93bd28483f30e4", + "0x7dffd941cd87df5f90450218509409e725a9a3c908447186f2211ae7fd3604f8", + "0x2f3a22bbac0f3364d770ac34110b470a5a51af2e141134ce5ff1a6b23b0cd80c", + "0x513a29c632da176f310e89b72ba6098258a55115f0480aa1290b9fad81eb9d56", + "0x2c2f6b82202d2c2a0695ea6431df8fc548352d6a4da049a1a1d3ebb6dcee671c", + "0x3239e8a96df66ca0ce3f883865841da4cf3f266f6dcd14ae7b9b237102a6c30f", + "0x799b02e7bbb11f847224c8bd8bf4c3834fdf4ff725adb27ed95e4d79c487328c", + "0x0a0ca0f242f716ac47987f6b8d3a722c14ec393f45fca3d0907ac9d3b2a5376e", + "0xbef884071d488ee68a1e47d6ec3355577f0f5f5c33abc6073a622a9db0742e3a", + "0x2c85023ff12fafa969fb67d8b71b02b818ab4857025985745f72e157c6bfd293", + "0x7e988bb2dd08e712255aaad41e045647f19a235992c066f30c912b721bc4b69c", + "0x3c47e9b9ebfbd7e08b463562ee026bfdc1eb0e588d0b562c1d5088ccb6b75951", + "0x5df164b36e54c5e6548cd717b7fc049e39ed71f4d64180a859b59765f7abb13e", + "0x3cc4cb204714a94601a480c3a12065de943984a22e29903e5e5d41b5e0255922", + "0xce0390d2560421c7327823b5b6289b001d4cced44a76a53c190dea622948dba8", + "0x59550f21eb2cfae1a0a0202a76e4c6ba68b4fe5675fe27b73a946b1747a52f9b", + "0xa02d984f3841e3a3e7da170ebadcad129a5b7b1709240afd4c9f58525f2c0b04", + "0x66d8f8cbc36376059fc85e6c40b3946615d1dcaa0abab7d2eb3cd2f939a9bb1d", + "0x8111d7b37b4622b3c015304cf6da86be7214e52d6895101f2a78b425e78e3668", + "0x6133a0f6a41cc11221a0fed4ad381db81adb94271b2b416355178b5b3c76e7c0", + "0xc34f9eadba0783c6e0d3649a814f40bd20b583e5800e47b10a7708486784baf7", + "0x2ac322720c8401b01338c21e4e7dafcbb57bafae710c9f656036a9b34c615156", + "0x25185c274e96f49544ce3d8f6b1c39979c4683cf62d3ef37c3fae2191baac351", + "0xaf0c179ee617d8f2c4e9fb91c86ed9e82ea96ea7f358b8c5a77afbb7ab0c10b4", + "0xa4e5da767d78d98c326392bccf68dba9dc4eb9a5aa3c22595c748b30d6979c14", + "0x99777defb876b1f6d87f30ee470c6c3e10fd9e978356b1f6a657b694075f24fc", + "0xe3dfba1877bc858362c3322e73eab68bfe90dc21c6cfb35e2b50db43feb583a5", + "0xe92a7df2c68129b56ee1ef7272b529c4df7f3a1057c89839cf1f70676d385769", + "0xa5b4c83e2ad6d83252bafab8bcc8d7b917638874a790a290099ae82bf329180e", + "0x4ae6f4fa44750c4fb89658e795104f743afd5a1df200b3aa194e9a1221da9e1b", + "0x525d9fc2faa130d63de409ccd6e145f068a45f33d95994e0238f674ffc6a0f6c", + "0x1e7c80275d766effa886cabb4341db2957c7a8d5a0dc204e81c4536469ed4734", + "0x21bffa2be6ed7731938e179eead9dcaadcf3dec9a8c8e66a907090ea1a8fb977", + "0xe9533ae7f64e82a079fc9bc41c501f803cb3fe6d12fbb26aa765033e7a9e1b91", + "0x77465e92b840421972625945383777e687bb675836c5f778d773dc284acf8bca", + "0xdb1c17a2ca23f70777973d18b902ec603b405083de671b7ac6642a332f7e41b3", + "0xfa9ea7cce80aabf9dd864ede0bd2eb1b5b3b6e96517d95ca51b0ceb204375927", + "0x3abdfec0e662f71895352a38bcbbbc4db951a96b71da12c4edf87588820211d5", + "0xb3aefa17e76bcf8bf411736eddf9fd2fb7da50b2a0e2ef120c8251f4d1a76ff4", + "0xef5cf39253371d43d0beba7eb9805b3a0649a85d6d458282c68a10d719e50763", + "0x0eec0ec9c836d1822240258c34507a60d29623a118b9ec5e0f97e2b2ffd640a6", + "0xc061de0ff53a3155211919929ed4150366cd0a56bd602b0f5d714249ae4b5a2b", + "0x99bf13db9ac871330ee344baa0da62050469d46b5c46c971f0e663ed039508ee", + "0xe8c88e491752d0c749a25f1cb89a7e50ca59ee66aa5eddea6703c53ced22c613", + "0x9ae9ebc7b185b055f2cb22b8d589140957b50de9cd16f6662231fd43ea32362d", + "0xa50ce511ef36b792716f688b19a1c7caf2165d2d4dd8ee38d1d65f83da474c18", + "0xf3114fd4ad146c127d3eab51df9abe150abcb1bf9fb82255695c4f00a2f2e5a0", + "0x3d74cd7a73380e7eb9fcf3aafe19eef24e915a951c3a6aa4622c29c83e03b0fd", + "0xcb953f59ea438aa55d78ed03258580dbe9837016277c1fbdc0033658af78afeb", + "0x3fea53f0e70414c631ca46806092de4fe1f145ea252be4840cc9651473c7ea24", + "0x73c32405a6dace78cf0d7a92b96dc91710633378e67bd1c4e70c5198016f2c08", + "0xf9f3e6c01dc2c7540122b1ca02c296758c82d921d6db21026c0d0a9124514d7f", + "0x307aa50d9f32328d9d9715dad2a49901714a0668855cc3ef83a31b7847308055", + "0xefd62e8093e5a892bb11970f75b0b6cb38738e642e87bd11a3959e2f1d2a8c99", + "0x812bc2f9c5bf04010e280922e7b7b0163f8e5f17e945330b1387742cbe48b09a", + "0x44d749b234f1c72eeba97d4ad2df6da2d05c67ecdd49b7e8d79cd0eef5fcac5f", + "0x1416c13c3a3a5899e823d25853197d6e1517d667d38fe6bac6a27e02ec7300e1", + "0xaca3010ad825f03e174aacf1af7f55b865dbf8e8d4d14efe315947d88a2ec9ed", + "0xb176d7a4aec24aaf536eec96bda82e3006420e265353c47920c6fb6465ef6df2", + "0x7d88ba7b29744bc87f5deccb5dc0246a9899e33126bc8874615b42d7a2f5eb20", + "0x9d70e1b3c28eccef62c100c0cf1a4e74815aaed7abd0366648d7877299263bd8", + "0x8521932ae66c9c1c78f92dbad32423c811606e902d4f36a4e6a38248d2ebc14e", + "0xc4584c3c668adff79b5b7e3b45e21cc5cb49dfd4432aa07ac9c4f2f95526828b", + "0x52a01f768c7d8daf2f4fd47d597520889db97f72dd01feae583412ffd30644b4", + "0xd5235c052adafd800957e222db53e7f4abae8e69277dd7edabc466436288e424", + "0x1c6567c03cf1063aa8d9eec758573f19be0003521513b08a2a2144b0c7112c6f", + "0x5a7270d2ba4b0526ef40e06b123cbc8e8c351ad58760c2fd4977f49ec9553529", + "0xd3e1e7a1479020aebb87efdf0226c56d41c36137c96e8f66eeca505a0c4588d6", + "0x6d0e19af25b0c14326375fab3812748bff83516e100410fff8e2f13e8d852762", + "0xfbc3049dec0d07839393f412e67cb950d1978250e9683a284e17ecc836117a35", + "0xed7ba3c9da05bd9fdb3334b37bbec237905034e85f6e5a327911b14a6b9963af", + "0x077473f80fabde987d148b20a25927117d2eebf75ae677c85537b8d15612d967", + "0x237a2f6ef42abf9fe1e270c95fbca1952e54b522abcf158d17aec191c924d5c9", + "0x92ae62dc5e9893254a4f122b44dbed9510a51e10f13fd5625d642a7db125f9cd", + "0x3e4b99758657181c90858979462a72fdfb894e58637e711320613839f48aa225", + "0xbfa57db7b876706f2bd1683678f88ddfb52a1d875e6f981271ad21f70447596f", + "0x20dd0ae6e4f0470350be62d27bc6f16dcf2c3dc00cfda669bd8ac756d64063ee", + "0x9b1f95b143948959c2df30c6a1dda327e1a4e5d374c2d9371cc84dac512ef1e0", + "0x085196c010111a5db0092bffcabb45ebee5da01ca4043dd0f6df786ee808316b", + "0xd3a075a85d715ccdb0b78664e97a63ec79015eaf023c72cc8ab8d74afbe99465", + "0x1931e0f938e25e8499aaf9317d7bdbf9327b5350488a3a8120159dac8bbf3d07", + "0xc8efe1cb9280e18694b28745108f229bdc291c523d61f2b80e57813234dd87db", + "0x5cb4a5f2885e9a330c10f874c9a370d8d80f2883e0aa78fa2937393058910626", + "0x050a13795aad6cdb9397a15013481fc6372ef186d8cd2269eefd61554cb07734", + "0xd364d3105797975dd4756946e29d23bb28d649b6520217108d03b615a794aa86", + "0xcf1154231524279eddb6fc243355cb9efb0ba0904ecfc3c933bb7ca832501df0", + "0x58856c6d9ad53b96d84d2dab61f566cfcdd1ff056591ddb46a4c8cd8cfc75a5b", + "0xad5c007a7a1187a988ada86d9235f0e25984409b7a78d8f52a78aae7ad5aec2d", + "0xa2fc766208749deb4f00a07618eb6567934a890214e02634c37c9922a63b2dc6", + "0xbd0dafc72d4afd883123cf42db0d540c19789bc92b923893a92cd0d36ab107e3", + "0x871ad474afbedb816c10646e6472167441329dfcf1ccd8493fe149cc3f4821b5", + "0xd44267945165446f26811fcb2cdc9dac4036733f2b849a29edc7d0e869bd5a99", + "0x1d64c2d1410e1a9fd07003d3710fb51a393f0a7e6fa2f7a632591abc984cebf7", + "0x2986f1018a6e9a976a7bd82131ee6ec47491d01816121e32cbe0781c51760079", + "0xb3e998c7900c64daf529c7b4482934fb3b236d292d71aba53e1c165ff6a8d340", + "0x3eab99930b92adc191eb566c2230f8e1fb479578a449a2e51ceeadb2dc4d10d8", + "0x02cdf34eb79ba66bde87307e6f343d3477c6422c999592c61bb95eb75b747490", + "0xbc5979f292f53962948041e26af6aa12d54a877549b9723277b6b40c2f5a90d9", + "0xfb6cdc28ed9c41ea483ef14c669c503185b9c10c400fa8cbe1d0117447d700da", + "0xcfac9aeef93c61492b316845b644322d8767413026c3cbab8de13d82fe6b0583", + "0x43d2d0d05229b660618b4d1101f7ee8d0eca004c2f2150e2b4acb95a0143f88d", + "0xe2389cd2fd6138a6c9c1c46cd2a60c649c1a716a7b08c69e9efe03954fa68a9e", + "0xfbaa9c5970c60e82e78897096d14368c51ff2081fbefe787e884513bdbb196e9", + "0x339c77cb99b64a458a681c111c42a4ed303d38129db31bf10fd8bd9d3ff4891f", + "0x1df6330d3ba99e659b2a0653d0446aeed747142cc0ebcfd264c29d0c7290209b", + "0x1fbc29e34f1db4d7afa4515b77f8477e30e95ae5b271d7bf9e1017c1690ac0ea", + "0x4f0ea4f2659050cb8234bf6b07722789ef7c22daab6a66c4cc06a14b29254234", + "0x102d644b67a363fe8743f24fea554aefb2e294d2b9354ebc3418d7ff7ff2b47c", + "0x488f3377d1e8d76114e3feddf3610c11fbb357827b3a57f86b019e315b906543", + "0x97cecd02c3aca76dabd0255f19a86598cb4c51db4a509778484f98959b24d13d", + "0x6aa368888aaebe80ab690a1adb13fee388bc290bd6232939bce08786baa6b28a", + "0xe714e54a50832e93623f552efed153bb38a5a9f1f47353b41e900b052a0e2da8", + "0xbcf0aec68a543e0f4bbcedb67e7215927cac908210818a86d868d64295c3e730", + "0x4dfcfca3b434d1732f128c16bc23f8363290d0829617bca383d9eb8d3b3d91d4", + "0x23d33346e920c71accb55b113de08b2c8414ec080585ad5f05a32e76f0ba1b8b", + "0x429b37b8ec514bf4ecdd003135994cb2a9952cad3ff4ac9074544c08bdcc7af7", + "0x970a0861d601b4b9b2a91b3cd46c5345e41029718c9237ef8f02adc1e04dfb3c", + "0x605c04629416f0270d17c23421af1e9bfde58f3a69826b8c7f24fea6c4cd317e", + "0x11a16c3c0e3dd9dc615849078549910070faa9ef65dcf2180943f77608506ec6", + "0xafe59cc260c6f1136b44d74c7600227e0e348319b2eb87d8f1e81c8cc989fa9a", + "0x5b8403ced61858f34c6063f94d95d3ab017b8ca5462166b4353f65c6e9995913", + "0xea26f865560ff2b46fa22094d939a3db06b88ed5d6e88e60e30071827fabe86a", + "0x87decf08176827a05afcbad0704961e4adda23a4e24f3746021a412c22a0f96d", + "0x95eedc11a2c05e65e7368b209ee22438f9c2769769620c477246c0148cb5be7c", + "0x12dcf291c13d502305d6db79a9b0acf04f0886eba1db8b6c60b71a64ef5957f2", + "0x84afd56f92dfad694b85fb2842d9afd46f72d3d5bf03e9c376d81f71a1242195", + "0x82cf4cf13393a2a8b6d21dbbe4a65b1be37122d6865bd9d7e177d39ce849e8d4", + "0x93bb8e8cf27d50e2c0df04516ace9d39ebeb4d2f628902ee8c609c6e67afc706", + "0xe5df70e96bf053a73436d32ac95714921d8a8ef3c8db5573c54900425facb29d", + "0xce9030dc6ed003f3ff2f7bd5cb4d9cf2885ab80abcc9eabcea49faf71e979270", + "0x18637369a2c553c089e745472b8b1e46619b48061912d8106d03ef8a6e5aa550", + "0x3d80029b73a2d6114a1262d1733de8f2f027046c09b1fffa8424aa249213192f", + "0x77e816698d3b33abfd6ae203667ed62764f7dd61d423d1b84bbd712953bf3e23", + "0xf745592e8f863316abfb8c5f61e2b9c5b2d71368da5f531716a29518cb2ab73e", + "0xc84af46591929ff044b18b96d78e0c4e2c304f0b3bb6878abbc48c310f16c3f0", + "0x0c65d516bc7fef8a2251847f9d95dac4b2ac89aa10861f059f205fcd67703fa6", + "0x5b81bfad1f1533d140552817d0731535cd2995dd9ac1815415e1537ebf764bff", + "0x914303584f63c14bb165134c4522327d200c26c1195c69ca2c08504ac260582c", + "0xa95372d0fe36522b0846ebea509135ecf7fd40414bf79bba140ae29fda6c888e", + "0xd6d3398b833b4069cc376c25d85cac38af3418785e991d69880b6ab09a8b3790", + "0x55b20d7211edd039ec0868edff5e145a72f3b84e3b6faacb2730d417bb59dd43", + "0x5f105f629c4911d8954b8b6d96f960074cde7bdd8b9d95a544047784952137f0", + "0x28e338ed8f762e97a0edeca66ff5dd26d105e1fa19ddaed05a70690bb9036c51", + "0x7fed322453a25eb78511708c642dde4c24e05461cff98e91200506591e124ce7", + "0x6ae8e04875471ee3ec7bc562cbfdd5ebd3c423bb0628fa8203642d0cb75525ca", + "0x2e0ffe6415f0677dadf7bb9ce419f4c394d2bc5ee42d2d164d9d5545c26e9314", + "0xf8127aad73d3d2391b10194575cdc6d41b3ea5be9ace544ecf1a2aacb073786d", + "0x5e3f9197c67c148929bf033e7bbbb90a9969f84b9bfe5d7e64d834ffc15e5aab", + "0xd0dfa44fb224aaea430d16e66551c77612f7ab4887740467ad6e433e809c00c9", + "0x127dee7fd30c312eee9431c5ec2f827bd6ef31e81443bbaefc274e91f4ef0d04", + "0x6946b100ee19cce230ade42e2f98f9f9bbcc5511c47a1fbc1c34c43532eadf31", + "0xd885399636f3e5f7ec5d706d7dd51bbdaf9f28fac85926f51bf6f87e073b942f", + "0xb9cb4e92d746370ce2b86420dbd8d4ca4649d3118edabd6ca0970b9475429f9e", + "0xfb03b55efccb1d80e599156240d14385f34b1c2c9db9b03e2f98f0a6898d4b66", + "0xb93e07a2fc9ee21e0ded3d0ea94b9a7b7784ad1c4206be8f05105a16d3dd0fb1", + "0xd0f25758f00378f820cc24a7ece22f8c7ea6f953425298fa53360b0b66a34591", + "0xed442f8b98354c28f2a415488480da53aaf322a504eed9f87faed091166f9088", + "0xb8c3d6965b3481526fc81ad55353711dc2750efae46fb3366eb2a8062aa82380", + "0xb5b86c5a20955d39ea93dbe1d8eeb793babcfffc62e49c791207c79e68a9ed1b", + "0xa1ca820b74f6f05ff3a2ff5afeac45bf0cc7bbc064289d005f71eccccc7657fb", + "0x70acc0624a2b8bb2e85ac4ca956c627f74f7463bdd901e8678458795bc426245", + "0x4aef27c470307e3848619f6cbd97bb652dc54e12b34f59a039cd632e09f7552d", + "0x6054932f762e5165f01851e906779cf7a9b258efe2aedf5e5ca32106ae781e8d", + "0xa4708caa8fed3613dcc96d0e32aeae215c7e642d95594533b615034f52c2833d", + "0xdd0402c748cb4ffc89231ed156d2c2008db78fa48dee50bc04ff4abf70117f23", + "0xbc4ba54b402e34e4dbb8a4a0e1b029b15f76ae2936e2a5f9c691563f24656aca", + "0xe23c1346d6c23068090dfe197f2b7b0652f5282f9ceead24f8a15e7e57a4b59e", + "0x2886f87be4712a38a908ddfa267a961490d8a631f47d3946dcecda8837bfee7c", + "0xf05d3086e1a856f06b2d46a66c907f0d8ed7de96f3538290c025e64e0e44fc8e", + "0x3010b9924acd1110d63ac5a757e27da24d3faaad08110587b0d5bc6b9c53298b", + "0x757cdfce5547b0dee89bdd9536e56ed4a8582c606d92a08fc5e32a6413f167b6", + "0x05e4673cba20eb72a6a80343b796a92ab021f059c1c4581c6a401e8c4be5f9f2", + "0x89fcaa6fc91fbe0019927cd1e8ef299002ad07f310fad5d20ca990b9a5d74a63", + "0xaff26008e0645e4fadfbce51cf68ae1c64405ee25b96e83f2b59dfb010e102c6", + "0xadff572a2610a09b68d8cd7208a11dccc3e783493f7e5ca38cd145dbd9715f04", + "0xc059f40868a54633a3c2b12f8897827dc0ef790535ab847cc70e6199a9bb0e00", + "0xff7792a35914a8d60305d9030bfce56b27361099c73c7a029fd0ec0fa5ef9122", + "0x7cf60c819aa23361820c4f78d16c377b1fd2dd13451f98667b778102ba7b6a7e", + "0x0b8c2436b4bb833198267f848a483fe88d1b5cd54efc5f4b747d88e9c44a1418", + "0x7bfea30a474b710fa0620fa85123f79a1f478f0245801119cb3eb27764d98d93", + "0x068e8ebcfaee07174c438c8c4647d87c79da52de66760d3114f8f087cfac17b9", + "0x2c22f460a9138b0125915daac35644ee985f15e4fbd6929a44885b45f06ffda4", + "0x30c6037ff78c3800e403169f077f2e3143c0d5432d39424d6a7c9593b72c6f81", + "0x10da8e2503a06fb5757e80641169b1b80e58b690e84d7632692dd7efcada0163", + "0x166d425a5f819f4d372ca074fb014d8adc5acf5f11ec17525679cd6dd9d9fc92", + "0x4b9a90798f59d928bd0da76fe633c3dca8f7c31da8f78b072b82ed6975900204", + "0x43583c302e09a0b4fd637ad0d26e7f79284c41df8ac67302a5e858db413aae2e", + "0xf9b4e0381673ea24a7359cb4fb285a1058a8cb45f2c04e9b3f1ef8766ca98e40", + "0x12542028c736e7c3ca546416916414136191832951fa1c3a99b0095c7116824a", + "0x538752ec157083229f505a8dddb1b79e3433631c4e2b5288b619ee4e9215b5f4", + "0x3b579ccc81d7681a5d50de060c696f2cd7c85c11f02f08e5553abaaaefbb0139", + "0x5320e1784ea731ea487466620a9ab14774fdd1b6dbeb621ab6848c83571044f5", + "0x8e40a6e5fbfffba2524f1e420905c9f7fdd854061925ccda76516e9f12fc9f2a", + "0x865f20b859748f4662f7a850a7eff55cee76e56f17ed89ffd4459c24f4d2455e", + "0xadf16e037ecb2000c1853b51d53f8a9f4739be7ea6c7a9b0292520672ad7a40e", + "0x1b8a0ab1e540cb0d88822b18f3b4303ecaf8f84e8d4f4246712d941c749a7ed2", + "0xdf011d2370ea05bb58cf8b8033da79475cdd181e9dcb5eb662ff1e8883930c10", + "0xc4d533e48e12e5523b128d9ab03280104b77173bc4f0ab583b32e54006b28e8e", + "0xff9ed57dbe37678690acafc4cb09fb3e56bbd7050097048c7f5392098fe6ee92", + "0x4b9a12ad32b7fb6569f54f90596fe99c2d38b7b71579921e10ddb94e6fc1dcbc", + "0x3826d56ba475a19df8e6f5ec5f645af25da76c04b6766d668ce0a6a022abeefd", + "0x31f24d1a9c442a7699a4db788ab9414a0e5835e765ea7d00ed0f7b80013501c7", + "0xfdd310fcbf2330c06236cf4dd991de44cb11d9ae94f9253967147f1948470cb9", + "0x13ec3a96a1d2206214903ac755aa89270b667039e8ce679476a7f111e13fe043", + "0xc74b1e9a05a25254b3907500f6955e69ff7b394461c4d0f54a922720cbc1b8a8", + "0xfc8df36d87cd31a45afa805b763400f5fbad188e75c8e66cea20400e693d7906", + "0x69c6cd8d1dd4d8e5f654d2f4771ecedba31fd45c3d53299d4ef6fac4e5b95815", + "0x9bd8286a55dc52234a23373e645f3c255cd6ec41f27bbc9caed8d399fa9c620f", + "0x006f7c7922cf2c0bdc2b2ee91fb156157f329b07ecbf6a319f95e0d0111fa850", + "0x0c5e968f523e445f11b0a03320edd6241ea85b753b888bff331ea30385b7f3d2", + "0xa6147f8035627461943a68993803aa82603b1d21b4ca925f022fae8841adc858", + "0x50dde6b371044e7aade60c5a7ce6a062fa0423f6821927912193033e15d80137", + "0xa7721663c835ac289d5f60af361330f0e23defc7a0f1b2d1d5bc00d80eb2debe", + "0x853488bfb096ea6ca5c173c023195803e1224389f7f7aea5542e9a0a393d7709", + "0x857fdb7ff3500aba3f538f0b5ff9ac5abb11f70edbafb718d2e06a6f72e59fa1", + "0x4aced770a5b3a0aa03dacfd63ef5195f4ad577159d63843df0e92a07f39e0630", + "0x6a8df2b9d41d9772e9fcbf757c7d7109de9c305b9dc8026771610877660a81cb", + "0x87a1a1ebe30d205ffe48478be51c4c7565d4206e9c6629ec7ccfaec58a1e4ddc", + "0x8900ef94888d7691e91dd9fc12906dafad5956acfc1385778785d6a96af33d0d", + "0xd2d39ee59f28378b462351191a4919fbc8c002efee5db6cd42db97b4003fedf0", + "0x6327163df3d47d0582bfdc153948a14add8ee77c7cab07273f128596a87c829c", + "0xa010ff90c6f09009fb4861c3a1f49eed8b7a9bd6d0b28c21ba57bc73ae8effcc", + "0x0bcaaefeeb5f1e4adaf8b8777c6f130840f8f6ebd8ecdcbb0cc30613a1ec00cb", + "0xe0f6b140a5a5199136be7c667f49a8a03eb0069e8d610b821ae9a81a46538078", + "0xbcc5678a2c2a4e0897f62e2ee72963a95c320cb4dcdc0d91b4904d59008303f6", + "0x9054869832bd800b8acbaabe3fcd5a6d3acc57a795d5942a8c8f1e6120014ebc", + "0x681c45990d091e6369f1285176e6256a553c0c6a0fc00180eadf44b4e0f4435e", + "0x8669d1cdee1b6901bba2dd7afebdf39d64e38c62e0ba61b2e2fce400247b0b64", + "0xb22a40dc7e3bd0a737a7c889c3ef146272837871e27f31f52bba78d9628591f9", + "0xe84254c0da8d37a96d1684352dd6f5431425cd13d306bfa97b01824566fe0582", + "0x7b9f3a3c6ac5fe17f15b371552c009f2d37f5f717762348dda21924767a208de", + "0x3b073f9726dd368a90c8fb0c982dc9a395121475b1d79f5d17f90e45f1c422e2", + "0x6b7f1952b4f96be629e792d427e2be244a7dd17646f4f6d3f6c18112193850a0", + "0x799fb1ea32513b8af5ac332c36d205b35e1fb151aa944c2de77300dce058b1cb", + "0xe0e61bc881ecce865fe1d424d89b7809888e221ce35b0c23427b6aa7302ca597", + "0x75a0274a697c000f6c9bb743de654f8f378b53ddb5cc9d3cdb047b9526a6ef63", + "0xe268f16add6533eb8995b1b42945d4f7228a0b3b1745a9e61f1aae2b1a54888f", + "0x2ec63c7830d5c7e6f9b4d820f9505816410535cfff94d6162261f5e7a4e858bd", + "0xe391827f636802b01f9f31087438116cefd6102c99d2f4aa347324bdfea94605", + "0x5e820aa790652e9fd75f65896802fef3e350501a943b621508df29b64af8d7f4", + "0xd47fa93d9738969d2c47a8995d681b70d0630c553a48eb85419753d5affd452a", + "0x2ac675f53379d6e949a42023f0d3f3785434d20f0a081f426929bc169a7bef85", + "0xbcbed98147100600600387cd85d1374c89a90f0aafac5034add2d6cd75d43f47", + "0x7c07af66affc9bd7f30c15e6fbf6ac5fbdb86ce222306b3e86075126fcee68a0", + "0x08f06f3aed49dd818a31aedc167670b31862d962eeffff35a5ab05123be48325", + "0x72bc5b93548df0b10c64d022e19758853b561fdd2935543104684dac98bb9e5f", + "0xe7d574ea20617832f5777d9ff37cf62159418d099d32e68d3bc282518cf46b78", + "0xc2072f0cbc4e5c4cf99f1abc3ce29fd0e8c7fc9d764f98475bb00a28d2033984", + "0xf6d68dd57ea3fa714abb481703d90f0629423021461cbf2a889b2f0a696bee97", + "0x78107f2c9e0e8ac2cd58fe50d2350d6dc22b8b56180c894ac5759983a2ffd91b", + "0xc9086a50f1515c162612991f8748cf98e75ced31fecb9c4b69acfc61c3294bad", + "0x3902f9fef44f9bc4338abd66183f655a3ed89e64cc0774b16a258c0e535561f8", + "0xebc1f75c812d89b581f2118768deb692aa51d0e8035c08f8c6a83411959493ff", + "0x2e68df57737e31e2d99e26986d93a33ea8457f1faf5200bb0a503fc10e83f181", + "0x97a8e7bf25cf36b5eca070939e0a193cf850456bec15ac2c962aae34c9dc5074", + "0x495b63b6cc1509e910bb978c8ace394e1194ad534a2b21f068368e835e2dbaaa", + "0x4c25defe595d05e79245ab9fb6c4c923c29d2c2fc8544819ca7046fcdd21cf98", + "0x11f032c68dd4586e3a680efa888c1e153901d5f11d0de0b4662a1fe7485a225b", + "0x72e6f419db15983ba909121ca9915cd18b73b82033deeb1faa9ce8a6129bd7a8", + "0xe83b4f9508257fe3e1dfb6543ffcc0b56f35c5a7939491410b28b8308c3372d6", + "0xfaeb02eb4c3a7bd349d9f88f43ea0a9ca90fc759f3277e68f09a15d43ab81e8f", + "0x0658126dc476f63b7f2344e1e7769878bf23c1f5cf2af084145382beb99242d0", + "0x89023be12c6667fa0f5c0ba847660eed209917aab7b02f8800f12a0b97c4826c", + "0x84eaf70f98d56ab2754fc4a2e44d851749187d652740e625c40aa98c22d1ec8d", + "0xa40e9127f56fd38f2d2d1bd2dadd7da134e2e1313a8b4f1e199cfb7cfef8efb0", + "0xc97c70f4418cfd47cd2bebae0828ec75ba069e513744f7362fa6f061f4f79e58", + "0x5efb29752284c1f064b4d2086163f01aa12ce526e786b2d5a1557d2355ff08af", + "0x983fda74c56d199eab1c7c1e179ab77688478c988ae7e170a37526518addd9a3", + "0x12aa8fed5488bcd07d2c378966726c46bf2895c2534acb4b5ebe803590582baf", + "0x5dec819a08285244b784009790bb4d550889bfc17f4198beb595e34252db15f6", + "0xc438c6222d02f0bfa651d836a164d054e7d40b6a5477142c9314dbd77534dddb", + "0x9c359a6b2ecacb09f2d8bc47bcde363a79d0ff71eee7f7ba992a27ca7440324c", + "0x326b8e5ba796505139959340f34fc0ba2cfd831b5b13347363932a06352be219", + "0xcb21867b538c4cf23f17eb74a4bd26546e9ce80bc3b2f529081d34d426881fda", + "0x62313fbf9dfe4002a6752e6c9605a4bc54fbc8e1c71f11454088a4b76221d594", + "0xd57c7e9aca618b26f1008a9e2a80a4c5f7e184aac0dd4285928ab54345d8e3ea", + "0x8f83eb880e07603c7168f920ad74d0af4b1b2c571425c43051c72f9619284cca", + "0x2e0a3edde6f77b8465019b81a9a4df9d2bbc2337ce83f4a86134caec05eb454e", + "0x7eb698848bca2d91cf4bad6a10d59153c593a5c21dcc17b4c025d78b1e40b1ea", + "0x50b2c2e0999daf16657d24c72b41b4c9598f36486c362a0aa402a1c59539d231", + "0x7fb67c50d82f2a4e42bd5567e22c0ae686ba269afa61d24d13a2c6302b87f4ef", + "0x8908b413f6d7b445ed27ceba1dbd903b0736030df9feb4f490884ffdcf5774e6", + "0xd030f54d2ccf6648c6a5ffdc2b77e9637d947da2fcb4193ec6e9e7d706308b58", + "0x23ff5791acd75375ec42ba44185eb461cd33c63989ef96aa4635c7f046f83e3e", + "0xda41f5eb7360048c50c1e9b69d76b62a37c8ac0cba0e871aed832549c82fd37e", + "0x8c67793e6753235000ff44c8c02cf414a901d2ef09fc30f9462f048d218a23c1", + "0x6f54024d44044001091aac3e366f40fcca5ca7bc44a531759161b26b92cc8dd6", + "0x69c644cbd9804016e67e2a24477888ec3b4430b9099ac99e0aec0f4dabddedbf", + "0x2b1576d1f628a9cfec651ade06bfd1435d945e58e8807b3b9d81bf22f51f8539", + "0x0e26c3ebdcac9105f816161f19c304dac5028faa8aedb853328783a4fc3a5372", + "0x3c40232ab27bef43b056b6f8b969759286e7e46bafa8354b5e2c01ee09505924", + "0xe77c4fa56669c06bdd6e8085594a34f0d709d2919deb309b905f2d35e3c8aaa8", + "0x4626fbee993cc7376c5fb827364ad93cb7aab22f1ba3993138a59332743b6f20", + "0x7afdfd4341cbba496f8ea728e7413ebacf1ae967c34b28859bb6bc5bcae99973", + "0x57b8468dd3337357cf65bd558c17d131586eb7a80cc158a48c02335d257e3c0e", + "0xf3131dd49cca9ee1a6c8b7c64a98dbd7aaf139e24ae78b3759dcc5350dcdd88a", + "0x3ea937838da7969a55e8d56bb845b3ba5975b349e4ee5451821788c2c057db2d", + "0xaf33ac9b4dc6772a6d8e309d80bd532b2d8c77b46e8d8b6641f3057b74fa36be", + "0x58049d12c2e6ab32f9e6d9c59dbfd67d50867adfbdd9ac69e18f622ac9e7f721", + "0x434de4497ed9b08959c68b4fd78403008360569cf92dd387931cf03e56e2102a", + "0xc23845b003cadac8831da20d28913cb19f5f79eafe0dd460763edcdec111c304", + "0xa6ecb71c33162a55c9505052c65da07dd3a3d1ca9c6544e277fcfa87fb3b7e1f", + "0x5befacc4e171547934bd9411bf2cd135f97cd098901d82dc3e3e532069884822", + "0xd6a151fb393445c07012da9fff60d0df41ba5e6735267dfd27dad25bef62d9a9", + "0xe8037ccf239222da1dc8450c895d3370c6c39934cc68493f91dd642aa2abbf32", + "0xf1e575c363d497bf6bdae824be17a021711d95df1682a9004e171461d00a0218", + "0x07e0ab707beac7b70a78e60888b993cc57b32ff2fa4cd7792acc486c5465e129", + "0x9e7064648f5691aa8ff6d197e64fc382ea3f7ae309f3b0d72d50f6e445b59413", + "0x37b73b70d8946e5c79bdcc20f8d2e30c9251fe8825f558a309bdcf104ffb955d", + "0x39c86aeff779c95eacd73dd23c0abc8550babac5fbb540d6fcb858e0f29cb969", + "0xae0145c03a856230e480a8f0316d1e50ade7ef2440e030860338ffaa1674e6f8", + "0xef49a626dbe03c2e29aa989a8772ef53973eb6f71444ced3ab20506d1a686cc4", + "0x78dc1bb34bad7b259c7c9968bfa702e2386343b3d61a778e63b7fd5850cd38b6", + "0x1a53d0d6a49869f205b3cf86f74b071977b0d9a7c107133f247d9b766ee02561", + "0x142657f7db52e85221262946ff2e0a193430eede0d811fc37fcd907985000df4", + "0xe00e36c62af848b8ffc0d3cf8e9b7eaed0a5eff81b6f38272348a611242d4514", + "0xb4556fbe016b7bb79bb88d5e27428ea08e839002318de8110212c461830a00b0", + "0x870a28d564fd07b364cbbb7a3d80e13da61b2b81762279acf26703f0578f6bc0", + "0xad09ee5f96cbd5eadfb8ac98ccb03bed1e71759c2d2e9928fa29ccfe72be610e", + "0x59c96ea26b642a32c0d9bc3750497164191871dee69f76f18275a88dd29d3ddc", + "0x3a947c53dd16563541142e4a5cedf2c0a96d8ed0a141c964d67ea3f75a0150cb", + "0x8ff3140b63d6f0a824f49c0d10697dd22165d682be00a8e655c4ea490dbe6110", + "0x18e8f192186c96e1b1366afce1fa96ed9106bf14abff3153dccd2a81071bc44d", + "0x0ac1f62debd4df7e4ce8d94966533f02bfb66905799c20458786c47df1ed7647", + "0x6d5307ecf5ece543151d75eac0127b0ec306499adda94f048569fc4836ff861b", + "0x4e1771f57a7225c2ec55cea407d409bbbff0a9747876e876eb84d8d6fb16984d", + "0x5cb41bd26fc11e239568e20dd9ce0a4c3f5dc92ecd6942c7114c0f0e4a323633", + "0x6c8350f184851e1dc6f185ce66269970d69664ffebf999348d156563e0755a19", + "0xf9a80a071763cf9b17e8730d6ea3aea3e01ad61377edd65b1bfdc991503ecdf8", + "0x5ffcc712f49794ed73972d34e1b09975cf6a90cc7e2f95def7d62dd9ae5f2fe5", + "0x884162cc8db04aaaf90102b8611b9e37e3b30d4e88811aa0ad2fc585f2ccc815", + "0x28205d9dd0abb60fb6f8b61eed68c6f3d4d253ec847627477a509451ba2c38ee", + "0xf2eb1bd7254cd325a24397c8fca471b19cbadda959466edfd5089088f403a050", + "0x31820187dbbfc229aabbcea0378ee82a8f73bb4dbf79f1002c39db13653028ad", + "0xc474839ca2b07b716181b9d7c53309b466c76ed9c8d58cc7213ecc24cb2084c5", + "0xbf078e1611d87cc6207dacf5609ff32a09e02f3e4a5b86c73e1633d4171bd1c0", + "0xbca29e83c17e2ace5ab34c500a83c677d5d6d5be6e7d384b2d3d1df5c41c5040", + "0x77b087b6fa29f459244767dada268297027b44e95f595df9a728822ef88ce5fa", + "0x94c21ac38a4d22efe067bb95a1646fd9a4b06546f00d5266cc38f9db56c9729b", + "0x0cfc43fc3c313c7ddf19f99f913247640f95486b54a7850d82b3b725474ad80c", + "0xc854887f728f8a6d58f2f819609afa70140b4e4ab23d275f120119d369953453", + "0x3b141d454bda366bd8bb43d9e4bc50003c7efe6b0848f22a3a6222b79229e8e8", + "0x51efb10d8c18da1b1a59d3e4b72f569b6d33805b2595a30f04c7d54898bf7ec6", + "0x0ce00d88eb9b45730bf3a9a905c7f2bf46646bd317a66d5ab051053900a77782", + "0x9602a30656dafc7dfe23048de652e2a35fc26e6ecbde37a0e368cae86fa8a490", + "0x25f00e150cf05189f3e8c20531e8b808816178dedc58da36ead70217faa155dc", + "0xb3e333cce4e1af63a1ac50b3372883b06dc7b61681ee074ed2ea3bcfd980cb29", + "0x23889207b0958a4b671bfaec53d3617017602d5927d3e34999d01ab005fd6870", + "0x860770e792f4e691646c026cc7e738428727038958dcbe3c999835bda6d2088e", + "0x716a4d8010b5ba32f9c68aa970360e7ee3863074bd47e5a43f78b7420e578582", + "0xe1893f80bd0a48fd10f536d5b0f9b8df5a0c6f8bf9996174cb6f7846b1874af0", + "0x258a5d0bbf49431e1e322f4b4b9c8128faa400956d00ea02f6bcf21380c9ce4f", + "0x991dcbb99ee05aafe83800c54e323274c986f778677ae63aa2910dfc073cd908", + "0x0b68e5697b42dbbfe5afd9a3c475f94e3f2ffd4841161635dbb43d599f886f2c", + "0x0b3a379ca0bc1e32fe8d0529249d315cbea74625f843b3ce5172f0c42932a699", + "0xd9db1d78a36b870f8610230fd937983e1a122dc49490f6df9ab0fea108f9bd58", + "0xeef71fc5c8cb976cb4947c47a6268c8311a395947389cc26a2f7cdbc868907fc", + "0x6e77580e324a211df66dd9f0409a2806b7e91864d4a0b579642c7cb842f6c017", + "0xd38b8dc56e1a62b64778c55716ba4814bd93863400b8d7d33b25deb9aee6ef8d", + "0x2855e3d0999b19eeeec06c524f08342e8e6562df3025398cec7bdab8065ab050", + "0x96da9b203d0d9d1471618b370b41ea105a14ffc7d6e519969c067ca1681dd434", + "0x6f277a5ee58d783ee50a3bb0089e7fda6f9133b6a30c66ebeed787465f31c483", + "0x29364f50f104eb29db23ba959936103e56b407f90ce78197c40a34e116ef9f0c", + "0xe0763fc5a537ca03ab69711804a14605077ccb1261e2faeed0074c3c468f0dd5", + "0x6db4f4d9b303e7c834ee7899dd5e2d55d6c8eacfa1bbcb847e8462d8bbabe560", + "0x36e23b1eae5789dbd331304ed39c997a9e61039672cbf7d884ad9c1b74ea7ac7", + "0xf8d4ebf12fff98a20e1ebc9b822df5cf72f9aaff56527be10c31981d283c4fa0", + "0x3949f18322c1335da2be434bd9a7f73aa730e33f93c15eee5859036115718981", + "0xee5edfaf9aaf871284d7e691bb9db677134bc6c43ee5b9857187c94351ba5267", + "0x71295d4572b4fcd75e32537a134e7bfd904e209f957856fd0aa9ebf48f6d9f54", + "0x6653ea7023ddda90894d8abeb1238a9e2e5d6052e01d59fff95dfc085301c1db", + "0x18bf6c3745d6b23546202e2173e2f35008768daf7d056a2123aa00a34656af1b", + "0xa65821cb2aa61819c68e81aee4ff4c423977959ec9f4d96b5b3af07566a6492f", + "0x98bb6c5e999d1559c542f114008d8be96c41d673303b34771c9985c698b1f901", + "0xfc29e0aff090007d966a50599d0e0aee253f0d1d1342cdbb29e627575f3b8b90", + "0x6b797586a6b0e58dcce1c4c2a7910be2cf7360e6eb2812e9d5482b51a4a70298", + "0xbaf96858e848e68e75fab070347188fdf631652926def4221c22389caabe8339", + "0x37e7d4965df41f99005cb6de19996c0fb45fb35085624d1cf6aa12945475e46f", + "0x40c9d537b19ac658368c1dd025049b43384b7d9862ed2d46a35596c41b9b6fbe", + "0x554d1b4b2d350aabea981fa8b6bb682ed2d68b5b2220632e2fa245b32782ce92", + "0xf923dcbc5ddcd739f34dd1e546e5b2808f771b1f588d102227fb3341884879cd", + "0xdb93eeefd691f1cab099ef2808ec7f605052920fe9919477159384f84de01552", + "0x09887122cd172f5b367dfab5444f5348bfd4c99020ca6f272612b258a83f2524", + "0x02ba8d2caa3286d9d83bb904bb7660272e7ad0f1ac88c0bfcd71c890c8f003fd", + "0x32188fae3df9872a94f8ac3b6335401e4c176270479810fbd8753330c4c6feea", + "0xcf3ac582190db0757d59da8e3498529cccaee71bf4327e22b1e71d65ba2663ff", + "0x67d8a4d229a7e6dfd56301c066216b38cb113929bbf2ab373c376c8a4eec1029", + "0xa84f66faa5c60c9d494c8cc59c5bfe938ec637ca2ef7a8767afc91bf71b87172", + "0x27ea2043c6b4a272e470eb833f3669d3ff2bb3152f998c09ca7265e699396871", + "0x94f9287fe589885cfd1401765a1f3716443d390e5529e5fe1795b1c8fedf3d98", + "0x31692404f3f5788b5c32f0c9f72e8cd73da6cf1c944c86eb46bf6a773867492c", + "0x4289691e12ef5edfe1df3eb7c07270f58e6f3a1492b5a20a95ab6d18145afed3", + "0xb71e059c3de0477641ed4c8fe26a2f09e82a59b2188b221e1437ac64fe175103", + "0x237e05be4c36d99b9c81de3551670457d2871bacb756f7fb77ecec2b87c026f3", + "0xfc0371515f466ed4a208270e8a0b5775efa8a512d3878017d5e7deb7a04fdede", + "0x9ca74129a5f455dec2f5e173d7a87c1e53665a57ad3762c8babf3d19567becfd", + "0x9d384bbaf114065151be42b4841b59be2cafdb9298a9b14de0df8c6c3f50f7d3", + "0x9e43897f66129fdf92ead6b589e5782f43b0142014ab3472d0cd922aca757bfa", + "0xdc5143a5beb6d5419043d8a3c1709f79ad750157e857ba0042709740aea4baaa", + "0xf123c368309dc64350a6b5fe1927b112cd0800c001f6a6449f8469806a0c8600", + "0xbc52ef1dbbea5e6f7777d1c29d7b72c34f9f2a51c26b589cb649c2c0d2f671bd", + "0x2b9ab7099111ed3f5e94c7405adb8c2f415f1cdb5ba9211906c32e3479c62300", + "0x32d034ab862cc5827ca278fbd1425b940f6cea126302a2e43670e8b2e1737e39", + "0x1b003adbd47ee097caf3d47f9bf0f48a3a573c5d837802e835ab62d9af869ffd", + "0x07e9348acfb6a54b19f28003d89e41df5d0a32077cdac303e37626a48b090b67", + "0xfc485ae3fd6892fe9d5273f8925e3041bd0bb4f8562d882c858c01b21534d7e0", + "0x286cd3687efdc7e254c60573586d3846af01ae22f0ac5fe0f138d5120c015d57", + "0x89191654d8b1380a4ab6bc26f559334304d0ac7bf9e2a23e44eea1b1016b2be2", + "0xbe3cf86ec5e1d734b325ec039f3e3465792be0c4d69f2d6e8c6f3256388e7195", + "0xee0aeedc8c2be55dd79b5b8e72e93eb1e3d364ce1c039798fff461b261d02ef5", + "0x9abf4d1ee86e0fd62c9e0bee17d7b714d44aca0c7d43556faf367177aa5972ff", + "0x0cb9d9a37230a3ee1246df76cb2ff877a3fe36937a25bbe49891a9ee82ca25f0", + "0xddd6c455f38effd2111d15bd9d27c599237e5b23f1c837fa8545b7f3bd024c07", + "0xfd41caaa260467d70e7e71d87bf78366f184e4697b035e13752c0636051984b4", + "0x171b50dc7d54dfbdb3f6fc247ddf1265f40aec7c57dcacf3d9587465e74c5259", + "0xfec6eb6f91e61764350a962fae73c580ff3e94c12a79dc6c47e1221ada4f3cce", + "0xb2f371c1c3fdb78190f712a422553334b843e4a3979f5df71f50daf6a0c27bdf", + "0xf031e3e86794e389aa1758877e98ea2dbfc42e460254df4387e2fdcbd5780088", + "0x80483790a1cf2f5036a956bc4222534f031d72927ebb16c2999f9dd43bdba5a1", + "0xf62c1312265462013278a8aeaba00c5e58ab52df55a8e7ce3ad6e07801769ac6", + "0xa8e8ac66d536daee80fbad70c6deb8dbeeb1ea92574f1025118374c10dbe3599", + "0xfd775ba6b4a9bb46ca0f9a209e44ab3906292afd7eb3a8bd134e64502c84eddb", + "0x3b5a3b89dc61e3fa4dbfd45514c4b7f6ce82388fb987069c27f1539f47d91f7b", + "0xc3fafa9d5591ca1f76f12f204f4f419818524c85b25fc6be87c11003fe764918", + "0xa33a74894dc56549fce4e3968aaa460772c27ae9ae7ca849f97abd72c00af601", + "0xd781aeb73512d6ac13baedfed1b2319b0f8218cb921c5c8227f3dca141ff2bf2", + "0x1950028b05d3394e85413c59a73466a529ac7e6bb327676e68d1ff5ad94d5112", + "0xae9645635f5f0039d19ab00e506f4092d6979742bf5f0544299c54ac08751ab1", + "0x7cf591c2ca861dded759898deb09d52622fafd5849ca96a948f0421d8474e691", + "0x5c4acf7b263dfe56f85f18237a8c686902296debb391d9149a9dd04842dd117c", + "0xa8f6f8f25ad0acf2499d4bf171f2d7f26c148af4fc4a56e2aef5338dd0974e1d", + "0x78b06160432af5c1310286a20096efdfc27e365eaaca6857b8e41715d067a3c5", + "0x668855ba6a06322e7f0e1145bf3a8bfb9d95caeb9548380b1419dd7c84f197e5", + "0xb070d4c9314654b17562e8f47198cfc69f1d58fa67d48d597398bee0b5a94a51", + "0x1539581875aa2a613e1beca53f9b6a237ee7d545b63ed38cc89ac0972ae470c7", + "0xad319b7b896acfc1341fb48a299cef03e3a9f2587006a28074a5b4f5d45bb46b", + "0x20e78f9325cea6e1d1ba9bbfaa67662e5bdbde368c09185cd4541d2dd73d3ac7", + "0xdea6b63b5c5ec838af94ee096f4fe91e0d5a335ee319dc7db69d296cfe1f93d9", + "0xa5cd305a0416ab6f8a28db2ace2b2c11caf10d12784ff5166a77bc23fc5e7b03", + "0x421bddfcf692adf5e6ef6bcdee7e52d96f5fc5e3f4f9f9e2cfed39a075608243", + "0x626194d904e3aac2f17bd2d226f9524a9eb3db4882ca9631a10fb3d1419826ea", + "0xb74ebed55dda76b0a3c4f23541ba87d896c0761d9009272088fd965f4ed12497", + "0x078f92a1ff145d106920cd464b6cc11530466493ad0fe249c32741a547238e8a", + "0x7c8bed6ca297d56e97d9d948269e2aa1f5a5eb2a83cc0e3f142beceaa5e20a48", + "0x794b4986f672c2afc53678badfc4130a6d5a42e58b0702108d76245f8609345a", + "0xe640847f4da24184a376971c4fa06325e26b0ade4a4dea80ed096278d9f40ebb", + "0x881b65419974bceed675f760bbbe21137ff818b8b2e0fb8eb66e9ae1d5b67276", + "0xfeedff6a622a4e193dc4ca96e435dd1ec9a54dcbb316b266fc05231093c2d7b2", + "0x2cb2649cee454bb8700533408d86f08cc5865e7753599c0fd049c5d16481026c", + "0xd457c3b197d5345dff53d6a954147200673dd69391aff87f6b966fac3c413771", + "0x904db5bad09c58d8f3f7a2b6138603ecfeaa0de2acea9d06bf925372a9c5f332", + "0xab7efb4ce27631176a042dcf8276f623294436931d41fe2c2b4ce635bde0b40c", + "0xc9aaa50e87b4a948a50c00803049b6e29db34ba0aed3fc05aa3d7648d0f297c2", + "0xa43e3afa107f499e9e39134fbafd3d103cc704cb2f55792fa626e69575ac6e25", + "0x24bcf8fd1d3c58abf4d2c05dc66445b87c40932e94e581de37fd2f05b87cc94c", + "0xf8bedecc5c76217c1578b98af68fd6cdb97eea8760244afaa0f73fe773aae46b", + "0x28e72f5d73f13192c7557f33edf989a94064bebe8206be7de6b9f4da890121da", + "0x2823f74f6e2ff659873b22adb77ddc504b4b38a45df5e1ae3b0c3c8286485277", + "0xd5db17a416dd35a22438be60896315a4ef12f0837e7ab17ba10630ff9c771b5b", + "0x41715358998d55e781e35c00b1c3905e9a9c5b18933b3f7dfa960932baf34a32", + "0xcb457f286a9924248e62971bf29d8a15111af6061864cf929c42801b973609af", + "0x5dba87fcb8be6c85f04671f88a5bf4a7922086f51145900b2635354674209d4c", + "0xbddfbd5cbd4a347e2d236804f5f73699bb1dae1c39b8ec183ba8cafddbc81782", + "0x313f5e97c8abae84ea76e04cf3bf395e0366c45a1ab3000e0bbe1ca9117fd45f", + "0x0884177d61d7155e829c5fe35c7f8f3506c730c34fb8670fa6e208c86f8cdd97", + "0x6e7488ff259c2abdce6a3d5b425a4e08fc01feb010a33df949b8c6753dfb835e", + "0x0381bb5e2b3e3db32b301b72dbdc75002b535a6c66ba479efd62645ef317092d", + "0xa8d5bac49a7c98af7c9d4082dee01024ffc08bf393287490e291f8b123bf7988", + "0x3cfd3bf6bef23507c4460f3f0f1b9d5648e9da055bef30742dc8b273772326f9", + "0x80ac23ee9ef892726e65f1dcbaa5e2863b860bfa7d8138fc62c7017ef6838f03", + "0x4629f3387e07462abf7e1c2a960d77fa01906563dd365f1a456f76cdfb33c3b5", + "0x3c9a97ce0a0bb80e08365f0172fa5a1d61326288aeb2f6fdb5dfccf96cd72ca1", + "0xea7b031f13798bc71b847080d3ef6ae3394ced11b116e52c38098cdfc2f983a8", + "0xdb503700481c2d6574f187fd5edb7cff6f0b5ada9cc494cbb60ce2662e570fb5", + "0x2af0acfd12664f385bc0c8ce1e1ef1c21425235b2ad39d6da86fb2216ce247c9", + "0x025e7604e5795927ec8623358bb64882a209d2679af257053363238643c7a755", + "0xfb310e2d6fe4a9c4be960384010886ae9462f39b44150ff90ca1242d2ebbb037", + "0xb1547544666236984380aab790736e7b33b9bfcebc9cf8692eff1737208635ca", + "0x32b5d728bd31581906b669e15fe18e1f1690e6c1a2100212093ec922a6e21b67", + "0x33be9e68dc7537237cac7de7899c827f25679d6950b3cb9ae1ad6a983408f0e9", + "0x2e5d5cd3862232619af679698779ab4338b933fdc92797e3a7d3861cdf8450e7", + "0xb2e0e1d34350cfd8deee66a9ff811c5955a2b2267b6d93a92d65022b3bc544bf", + "0xc800d8c600017e38c92740539c90c7d050948cc69691633cf924a874a92e4335", + "0xf5ab8cfc4c9aa122d7546f2ee6cd045734fe07d15a1d448df6515921aa4b3db4", + "0x760585fdb49e6787e6ad24aaafd7617e62e746c95f713cb661c8070c6b43a650", + "0x35ac9d15aaec0cee2d360398d44a3ac7ea785a527a19fb53bb2912345fb0ecf1", + "0x1fce12b501c1fcfc17cbcaf5af033b0fbdf42d84ff840d6584a6ac5aeb8cbb93", + "0x749ff74e0c2e2fe58a554e2a36cb5728e79dcac1dc4dfe85e31fe613c61c70c1", + "0xc4f6b905d162c5cfcc637c934b1bd1a736846e175f0b99918fb2291c644e85a0", + "0xbb7e74d47748ddccc97afabc3fb7637b10242176449d7187934eab4613b295c2", + "0x271713b2012165bd7260b184052cf5e0819b17ddac6aeca7aa9762c697efb374", + "0xaab57881e24229a65df66399217f89e3fc244f1ff5782ac3b654b540e23dc892", + "0x6672fe29b80f17fc2b5480fe8b76a81c749d62a757160c741f7a4b0c930b296a", + "0x62d16e2e4da1e9a4395cee9826514e610b2f98f8a70667c7ba68d101c869f7f5", + "0x96f052c7fc90d197eef682e4d9ec2058f16f2923b2a8ec4b5954f9957e741491", + "0x3a08e61188fbb4fe8dbdcdbe3d0894c6476670a220650778968eaa67b871a9d4", + "0x543e140ee873a29aa619660d7ec44c7e8b4f73d942ed6f4e3c810a80ea77339e", + "0x9fcbdf4ac6747f89e6c812b9087855a7b72579e73e89392e1312b79bbb0ec6de", + "0x8f7895a33963e7715d0bd165e497e07eb6232457ce599f842022f0db3b90b1b6", + "0x63b49e04d7a66bc1c4cef26a34469185b5c3efe61664d5ff2a494134545ec213", + "0x90dd2d240700a215e43b8b6977cd9f4e48fb43964207135a4e5c400477fe5f46", + "0x65391ede2909ddcd6054d67962dd8c36d482e99155ea2e7a558678347582b4c5", + "0xd5b1240f9b7be6ae47f239d6424072503bcfacf572b5885a5b0d17f1358a01de", + "0x515a416975ee07fb041a8be3fa75e497ed7cb2c7d99592c1a35c059202601bc4", + "0x9f5d45e847f2d80ea58ddabbd45092970ee87cc06218c4fe6ea4caf219d2fdee", + "0xca1c40303a12215260b66f5881f5cab6128492a1cdada085e6c6eebef8f3d95b", + "0x105d47981ec0265b1df0d5dda68c3debba13034715d7b18cb82fd2d7ebac8418", + "0x6591c8f6d0630b366c287851e0020bb3efcb5e7c602bf14b58f8b51b148a3272", + "0x103e654949f9a33ee10602d9a06c331a56a935413acdd0a5b5057de3e514be90", + "0x87fad91e0e8dfddfe075c641c2c3036dfad8fbc91ecefe26e0251befd9674cdb", + "0x20cd5544588c68048d443589b0b06f2802d2eb0327ceb179c660136e4284ae35", + "0x65ccf65c7e239fda395a293f7b500092d0ae4e949ede38111b04d356bb16fb63", + "0x251bceeb9f7f7ec0751143e64ff92803e998fe50d0b31c11bb53aa4a060a0eb9", + "0x285a7910d5423018f5a06d8e0e237371adc98584250f68ddf2eebd7c59db69b8", + "0x150227d917ee957d0d59f6ce389f3c1c80b09d2be29b688d45b6f579300f6935", + "0x8727d5ba12451bad218a1fe425f8ab4b7bf02df5788bfc3d08b425a1b53ac63e", + "0x990830c6f2997c39f8aa71be9be83901cca7549a3e90bda2f2b9cba2154298a9", + "0x6aed0588ff9ef7012618b02b4adcace5bcf31e4bf5b5444bbe32792c7a3efa45", + "0x8562201c1e149529a08459df1f6b08a75e9652aa39914cfb1e232945cb42728a", + "0xd0eedc8af0a43ec0ee34de1589795f9d085712adf58a04ce1fd69538055d4e87", + "0x708e364769cdd9185fe15742edb8e7ab7e7c0b038e9417b75689aed697158020", + "0x6b12abbfabc44c902dd01f946a60a9364d27e329bca25e705222979856249ce0", + "0xe37530688abd329915200f81e4f6b8347adddf573c0d149a392e235eb943ba9e", + "0x79114512af0f3242b3fc4edbb1d4b8623496a97bb3788d59b3d9fdcb572c3ec0", + "0xf06c888cb466ec75f5b33e71bbc9fb961f38686d3d2c0146ffc23a895548ba21", + "0xf97b12f48630bbf4d85aac91927881d1a1aa4d62c972425c52747512adc726cc", + "0xca7a70a0717894403a42ac3b72467d83ff225fc4142b992bc45c24e73d3a2fed", + "0x35d255ffb52e6bb363cc85d15b47cd355331d67f9e57c79178090d770fc066ca", + "0x40d94f4162bdb032c57dbf9f1a520ca745f999bd475748ad804adb9e2b7c28d5", + "0x90261899e9526cbab7716db50d2ebe0ffb09a757fd270015f7a5a19f391b080c", + "0x442f7c9ac41e6a69f8d4bd5b17c7e2da0c28dd026d8dda2000300b6c90fd389b", + "0x1bea70eaa93d8f79e8ccf3ffa9664e172030dd7f5b91a44e5e72b5bc58182bc5", + "0x0a86185cca0c7634e140762845a8428193a7f5706e6061aad3bda75c5cb78954", + "0x7a6b49b5645ea0ecc4f55eee45877455f45d74d02db9575f5e4aeea4628e9c03", + "0xd2760bf1741bc53fbff64c23b4d9f7210e70985421b84778708aa9fce7b8282b", + "0x13be4d2ae2a00b52b8fcf382003588177e9202bc5b32cecb5940ba471157fd2b", + "0xf2f0a5c0c8a202ed455d59cb98bdefc5c091a2f588e3440bb717e87e125847c2", + "0xe929f741bbb47d59260d899ac994689f960320dde7ce673358cee1fef5328b88", + "0xcc29e859a8bfa933fdac72edc68ad4b9cea70bba8c1ebde6b676ea0b7b80d5f0", + "0x461f5ce9a0f118446ce25c2a3f5e43b37c3284e505d7e293aae107021c7b67a1", + "0xb5084576f6af30e87d297eb65cd333e674905e04195b529dacbdd079d9c4d809", + "0xad4e74821b978e67c30b07cd5d11f61fa4cb639a0f6aae8655a2f0a1375aaea5", + "0x790eb6744b17539aacb48f94a1003e062dd8aad7e9ad01fea0572acce3f2d790", + "0x778263e0774909c23b450812b94d696e598641b8cf7cf5fa0b1939d6f4310a58", + "0xa5a596632824c1021576e4700065af61365faf589f45b47ef9781d0762bdc96f", + "0x7225159e6e50b84d28d91bef9de584727ad34362093e1c400021e0c53f21e3f7", + "0x6857dc0616377950ed794d73b89b2e5decd6c07fb42c0cd80781c34c3a5322a1", + "0x1a0977da00580b2c539d12ad1f752a42dc18468c4894903b12c8dd5bebef9d34", + "0x0cc38664126e7eba6273db3d5507eac4776c60a3ba6f8d6d97c6fe2d85809a1a", + "0x5be92a8d3aba034661bb0c6f65b33847e363e6563b3544662f85ad87251fef8e", + "0x3c78cfff673cf95193b0b8b83144bd4d1b310ab7dea8daace059ce5a862ce135", + "0xdeab806d2a09e40e3cd4724efd2078307b00bfdf3a3fa52f75a7843269f6e674", + "0x4b21fddbb815eb989dda5564be12f7081ec4b8dc1302e88cf312ed60716acb4e", + "0x4e264624ffa1014c5a6af29b49f266e304a0696647c02de763085b96598ae522", + "0x82d776925d732dfe851050f7de141d124a43a97d97a93863594b3fdd62870aac", + "0xee7aeac89b3319e2ce104b11d92fe4c126866745a774f20944fafa1ff073e66a", + "0x8a7ba366e26ea490a0a29be5b41ce72a9a48df80df38fce97d72b2db2bd1b799", + "0x98382baa2036178616c47ac045da7d5531f7170eed903f8508494135a2828a54", + "0x713db5c8a5afd45e9e7ffcad4ac8d2016fce3ac106beaca671088df2db570b04", + "0xd0e15febe5624b69fb43cdb720b44fdf1a08f4848599d040a2ebc3fe24a57500", + "0xb3b6884e20c5fb7796ad9f9b4b1668c90770e4fb207bceab704abd90f5f08df7", + "0xca1384ba44b2504bdfd6a2101736c76e756b925a4ec3fffa83c475386fd284ea", + "0xdb53f28f27b62c3e2ee5a9cfa29fc13a9ab2f4701163099a44cd83e2080b2ef2", + "0xf5565f3ce77e0097b86ad54c044eed6f716d9c545dcf20eac8580e47a5fdf1ef", + "0x88b417ddf0cbdfc6b599a1655e357d886cebdc5988ac4486922c57d733771bcd", + "0x1d27d96ee7816dfce7064b741a2a5e923b9f08279fc936c1549cb4abd7460717", + "0xc91024d83f415703a4fb2e6e9da42ecdb5beeabdbb3a9156402c085f8b29e278", + "0xd00a3ea4556a16c6da0f3bd836800fd8416308365b5cb4d4443916922c04e5e8", + "0xabe1bf618436a631f4b44cf9c4f49432eeb688e033bcc132fb7f0b6dba67dc42", + "0x798d27c82c387596bba9d2fc3f012e7baa67a9c64c7fe25a4108f514aa56ce25", + "0xcbbd67696d7349a268e5a84cbfe5df2f033651d243c77046e2dbe63aacb4574e", + "0x8a8a0adde0be45138ddee23010a7d7a58d40630dd33ed4f08398290ae12863d6", + "0x6b00b9f093582dd3fabb78d5e6cd58cb5e703473892a72046d857c5a741f4805", + "0xb36afc0bbc45c11679a0af47b3a2da73693a123d61f33e71dfbd1b9f5b68fa01", + "0x5572c15d64e73d45996e327aefaf69026f5e1208b8186943cb097990b8eda29f", + "0x33913c807d3b86cc495a7262295460d2f46fecacb64e98f8dfbb7bba03321141", + "0x067df9c8f776cc92add640b988c499f6e6a672726e9649f9206673d3bae3ad0d", + "0x9f21aa371e4df88cf78802b0fcfc7bc7b1bd3d9dd5cbefb3df6ca2b1578964f2", + "0xe4a07812b22a763fe58d7d59ef601f2a3cfcfc82f5088464f8586d971a25e21d", + "0xc8834829d0f670fbf5d29861540f66a9b1ea0c117cd6c006f39be07f4b51f38c", + "0x6580fd5f5b081481dbc380ff010becb4ab1c7583ede7dacf24426f32503e77d3", + "0xca96ec4e65a8b2a8e405bf2302a6177b78c0daf8351a500130547b5c2e1c8f6f", + "0x384dd7e5bebb28eaf212140b8aaafbee927d7a5902571412e4e80f8516e69b72", + "0xc3f93a6f8595c7158fb4a699f7defc2bfc0ac9b276db7ff9f4256b5b288185ff", + "0x1a23ddf4f05f7020fe8cc5ff360bd2c191b79dd051c7fc51117a99da91d7b733", + "0xcbc048779eae13a8486924b5c005e9d68768e85bb42914d0ef0fad5ffcb107ef", + "0xbf86882760f5853ae57ceddabb739b12de9a9f97306853952eac715aaa6ee5bb", + "0x413abfbb3eedcf34cda720cbd5d9e1c5684f0ff86e9af574aa7a1571b8cfc270", + "0x41aa9912c9c354371aecb6651e234790ad2c38636a839e368af409b16d53c1a5", + "0x53b7c00e2adc2505ff724b026d1887f7f1180d3596cfd74bc740e54cd4dc3a13", + "0x3c01f606b3f4d47a414b296326820a9405827d14f8f0c4ec804ce79e0aec738e", + "0x25f718ce3aa0785d792a4f352f66ce88414bccceff4e7820d4782df29f865257", + "0xf4a45a43095145254559b1bd70f4e0560b49b957748f91653c06ce28d358201c", + "0xc72b1f87da8b58bb3efe60b8fc1fd2501035f976381911c792e79b6d293040f5", + "0xfa5c0ecc6b2d6adbe4ae970bb38e5b98062e58bda0860da2a99fc05761cb6755", + "0xd57825adb437b23293677e61429fce6d66b8eaad659d7191dab60cb62dcc173d", + "0x1520ea5401be20ffa0b794b59337109196653a10c48d3ce6e37a6116f509d029", + "0x85b9859573f3d6d15cea599fb99c0a2f15be346773774d520e9b4f3ee03957fb", + "0xdee68fd4c481ccfda80f923d4df7b720179eb8c6a34a5ebed1e1b4adf2890681", + "0x12bffbe6a34a374e1cd0ca07e4be2cd3e74f73f59559aafd73e6c195f60c61e4", + "0xf2b945c34ef98a1641b883c92d02c7a5c1db29f655967247aa3fccd4914ab163", + "0x5526ffbfa196415d82c37a425fda265bb088ca353c8494a9f4d1e01df8f6a0e5", + "0xea358b2346399c29d37e33ceb71b04660a26fb7a258d808f0088d5aa2bc393db", + "0xc9473317be96d00e22d6ba4317da3d9356d42c110f07ab9cf0198f5402850f05", + "0x0d889bbdf98d9e199b9025a1d83b580353c4a77cf9d0d0b979c31871e984c8c1", + "0x49a013b6497317a5996c2bb91466bc6dfc89665e20e243f4f3f634d5a481e6be", + "0x3069284082edc65ad404f6497d82334d16597d7ae72cd71084a00a50223b6d3d", + "0xbe1f1e0bf7b719d11e9e0f953d0fd14b84a6171c7bfc544fd2936860cd990c60", + "0x27e203a6c0bc8d86116f4eacc6e7c9665470bb38540f1ebf2fc489afe33563d3", + "0xfc2c38dcfc503a50902f0196edba48bb8e0ba440fd67eda4be364b3e37a83c1b", + "0x2a39109a61c3e4ad53145ebb018a2036f143ff87aab5c3096613d5db457620a4", + "0x49279e945ac0bb5984715a9defa2ede9b5c2308ae74e0fe2d7f89d0016a5dad4", + "0x3adde4353615fd35b4919040ae12535036cb683d949b9ecec8766e103a33d71b", + "0x0f1ff8ec52024077f17b60dc9442a17392cb742f74a3d774cccad362dc5b2395", + "0x468f047859e448ab1ae177cc130698cbe7c584a81c08955125b0bfafe5d31ad3", + "0x33ee8d59a048b119722c7873c9c099918855e02e85eb22c779a9116dac0ff18e", + "0x818cfff158f7b0e2c7d6df10edc78fb1c65d06f5865b6520b20628edf2b04498", + "0xea87cbd43ebb58d7079bacc161211961b0b8b4e57b0fd67b64917597599f50bb", + "0x1afc51cbb564f069a76ecc577933328adf0666ce64745b321792ee39a3041ce2", + "0x83949500772d78e2ce8321072a2ce844a23b761b63af80b46c8e4403478406c0", + "0x8178531cc89ec57de22762121a78d39f1c8b8441a08e24b2fce06ddbc821b90f", + "0xdd712e96d332172bcc35194234512fb118ba42c835d19294e9d0154e2310c60f", + "0x2bcd968f95abc79b722242ff5cecc3fd7a785a40378f04763b958ecc6be8f13e", + "0xee80a357a7feb1c6932bdc2de76221c1cf35adac5012e63b830a5248e4ec2683", + "0x0bf56c4b78c2bb27d781de76bf4c0a2db7cf6b3e28995255157d4cb3aacedbfe", + "0x2bf40e55817a095111c8610ad44617915a58f148281afdef2fd77cda25d4a0a2", + "0xc02b5701fb6bb86ca8d3ddbfc8f5caeb2e08fa8fea269f5691602dee6596fc50", + "0xc3e67f985cb33174ceea7c83ca9a6508501b53ce52350c265fabbc2ea7d1df8c", + "0xed9a7f168cd41c4bec50d0972f6112391916b868f6667e23b6a6c81c19511925", + "0xb101fbea18b8e096834c2bf5bbb137ba74338c681818b9a2a3d7748a8adbefda", + "0xdb0f2483cdb4c654fbb575c00bad12c22de4b4cd15687aae233c80168bfb337b", + "0x7723d33db7242c1c161e6aa36579a055d28050432bfa447b1b86dd3e62d4448e", + "0xa186f5d41876bd7387fc25e3b70ac614c4e31165ebcdede10cbaf0a778168c28", + "0xb049eae95e2670a4032cc534ce7983a6f31a6f652655eada6e6e06642adb9a97", + "0x75345b5e84d3006b6fb4e8941c3d1c9213d07cba0bebf44c70d93864f642fa34", + "0x20f605b061f09cf3da044877f818da8b6a6fc2a35be02da8481baf8ab1480975", + "0x217ccfbe918a06a2d285c941c827b934d74a61a02caaedfa2774963b5b04d508", + "0xf8051f2e32c3efeae9ba713102b2718a8df41e5ba62fcfa44c3deecfcc4799a4", + "0x42b82577a2f805762ccd41217a027f940cfc8fbcff173c9e185ce0117e407e71", + "0xd7cd207003171c7469d3467abd20660d2c2c72a399bd0cccfdb0c5cb0deedd8e", + "0xfc84755e288701f561d4288724eed1fde59afb369f80910261cdc785e296d861", + "0x638bb5f1998f7b3bc8e66bc01a03755a4c10e98f2b51a163f34b51d37c428d30", + "0x2375760afef624d83c6c70d83f6ed2eb5f3cb47316f35361dd02bc5df7f25a37", + "0x54179a2babb945a17f9db815e138f6f2ae53f3656ff0ff184e67f0fb3dd7b453", + "0x5f1aa0dfa07a9a8176559fb050a299122dfd12ca7598ec4ae6f955fcecb12f88", + "0xa00c3e80fbc50c1d2132a866f956b6d178b8f24323cb4e5cc42056b9d9569a40", + "0xe4dc9defb8f2785851000a508f8de844254c3e3d9cd5d2c7f1c6f5ef94452841", + "0x9796fe60f299176196152666f82cdb3fed553922be04d8e29a0beda00a46b66a", + "0x7aaa8bc55ff8b467c0fc8c213d971ddd97badc7bab58c1fd01c86b6f6bbda89c", + "0x90af8b735909e11e0b3b0702caa296dea38cccf3499546479e53b9b9741feb5e", + "0xbddd88fc56295038f5844efc6553e7f019c3649e685a322b1baa3aea061980d8", + "0x6e94b8f7f6930161e35f4c063e2beab5365c6f59ef0c041dd49506ad5cd4b9b2", + "0x1e05e13d0d64a4d737789b0092a2f6e0b36b423ecbc9595ff16c82f2232c55cf", + "0x0d28f99ae5eb27adb400c36e6215cdb93a635357cb8adc95b7151be108663ee1", + "0x322475f93998550e4346ff9623e4e537d003f2830347aadb9b4195da6fd47ad9", + "0x4a15d72e1f7cfca4fbe3bac32d5abce43122dbcd480f247bfd96e28281fe8912", + "0x6e1adb72f8355733fedabddce5850a4e88c33a1e84f4b9054fad835c75096ecb", + "0x57987a763d7356d53cc5e41a3c776c1626eb7449418e88c7920de1d1bb6ed7e2", + "0x781bf9c90bbcf0976d8efeea1cac97c1c5e9ed273a555ff12fab787eafb53b06", + "0x2be30909b06893cde4996ea6c3d6ce4bc549877531629129667397c8cfdd2a53", + "0x57030a007ab3e997bd798572488df5e874b5ed3451ad77aa52e76a1ce2ac02ba", + "0x3a0d676e0a52c0d901249997c1a09496c83ddf05f564278485ddcb7fdb64f10e", + "0xccc7f77124661d1d1a95b3ee6abed63df89883f2fa062fdd624d39937c8823ea", + "0xee52f5c3fa8981af1e1270e1a23f7d542162bae94e54e61a79e573d78f3f02e4", + "0x599033217653743093597d422547779681b71758aa1b57d82db09898640c542a", + "0xec80d27d984be36da95cb442521d721f97322f75547378829e55d36224ff3d07", + "0x48be385859d61cc9d378c228493a21a98392c1c9b909286e1199f738e3327def", + "0x04bbc823e7f15007910bac856e69b0b5bfe3821c6bf9a05b110b25d850aca52c", + "0x5fefa100a18f0834b0f53865876e8c0b94b08f5a9d9d67a2f0a2d1674c577b6d", + "0xc40ad85af819ccabfc325c1ab1b7e01812ccf283977409c6603b46f9a1d2b7b8", + "0x5e1fe018acc2fc348194d32e5a3c7564d281f3661fd8112dfc827d78ac0634cd", + "0xbb3593c7be37df93022d21e36d00977f242c611963d001df2520d7afd56b845c", + "0x78dd887cfbc3df27be44534a7e36dc49ff8bd4947ed91c1de3172d7f5b7823bc", + "0xb476cae089bf7e7831c215dac624880b7182ff469ae0111bc64c486b84e5bb90", + "0xbfd250110a656d5aa6cb7ecd0df362d9709866dea9b3eab066f2ded69af2c3b6", + "0x9980efe2a0db42be19550acd092a28ca89e62ef16a3c35b545e0353632252b92", + "0x4ba7c59c184296e0908664d8ec539e60834c95791311e1c72a7475398277c964", + "0xb65e39cf8f57528955ed756f30947152e6a38d3cd9d7db57ad776bbc244dffae", + "0x698728df575ad88541523622c33cc763b8f26ef7d47d00bbedcc3d3c9f4a4bbe", + "0xc8b910e5baff1f4cfff56093e3261e5babd1ea14ca51dd7dfbf1132dfdb9d17f", + "0x7b4d5308e12ecda8d303f4a4a14cdd582c8abe136ede5c820fca6bb2e98a98fc", + "0xf967ec258c509485d495091a5ce27e75bb7b0d54791ca566923dea292e15e6e5", + "0x46eabf3cc18a773945fc94ba9e4f1c6b799c1e6b6fcafec63f7c0b552cdab1aa", + "0x45d6ad7e33494861b23ffe4547ca28b6b31c3bdd27383620ae027183b31025f5", + "0x8af9fc64ff33ce39e93d24ed865d38e200b51b7503d892117b8a874355972f63", + "0xef740448792fd00f7bb262536aa8b8afa637e9616b2d2595400d52a64fe9651b", + "0xeb134f026c6ff27d421a74265c3e6fd479f4103ea0f44fe75c7f2fc46bd04ba9", + "0xfff821f1924c78df7838286e8cb0767c2be8792ec62c2b85865b5ee37e4743a7", + "0x3bb260c6c585eaf5a9f84a33c80b1bdaf4f89fe33c5fd621155812acfdbc2cab", + "0x5549fb649fc69617fe9563363b7867dd3dba1058d25a635e9b41f43b7ec634cc", + "0xc2b48fdc8c371ae7e537258af076cc3f6acb8c5e683dc82d97ea3b00c8326d94", + "0xd4ba8e04e69ff1b9a9266ec5a73aeaeba3a193e56dbf10f109ca23163fdf4f54", + "0xb8514f2ce80ab00777b8c127c278bd8981aa19af0b5885cd42fceced5a38d839", + "0x87605053480a305a19dda3782551c2bca7a95b02dbb0689bf4197128914dd2e5", + "0x3077a9feb3dffe3666c29df42bf234e07b47b923177d091298111bb6b5ce10c9", + "0x023d917bc887745fb4d616e93f5e3d3a95e2379a1eb222feccb69d7b43b42b9c", + "0xf01b778917853839f818105949629c05329836adfe80f5b004ae3f0c60a90522", + "0xaddda2925f932499474d777bf60e15d45c67be9c1caba082f913a788e166f955", + "0x61158ac6829b9285c6dd0a1b5786028dd80b8a3a5311d9a47e4162ff776326a8", + "0x595566b1fbe08696e5e065ad7f45600d735259d107234a07bb9340d43a159476", + "0xabf2b5aacf171cbcf43d07d696f82a156193f94ed22ec7b3e44d0cf46f14c949", + "0x1b1a233e8e8415cd58d4fbcb5501092cc178d43ac0e9daaae9a41df1db18c548", + "0x94a141e3b900e6c975d1554a3d3eb2d23ff0155c1655ee2028dc9e1d1b44f05f", + "0x67dde5137ca3b5d5296281671df013ea84d2a1fff7c6f5ae1f5ba290da3f33e8", + "0x640dcd0ba2cb4132b3e50778cdd3f172df8701cd8b2ede1ad25faa3440610096", + "0x47ec766b93e8051b5f6e270d819e688e0b40527398404e43207a2b63af0816e6", + "0xa4eaea813f277813860d24d6061fc7a7ce26931b3035ff358f046c0e9501939c", + "0x3fd36e56c587185d807f3ef4509fad76be0ef4c8795f914431d89885e942aaf2", + "0xbf5da095acc428f94d13b4f3091b2631f9bbc9fbe0af80aa41fc292f19935210", + "0x0d4956ee64605a4d4406fca00e07d4732c8ca001d4dba630d31174ae573ae0bc", + "0xf052c7f7420f10daf6111a291d62e1b19a27fffb36be49b40ef71bc6f6369f4f", + "0x1ec50bfc374e1caacfcef124fc66fb29f22a0194f16a70c3c73c9e37d6c20afe", + "0x2d482573c15f920c1b8d8e3671ccb11a708003285c0e652063629d10e0bd511f", + "0xe27bc7ad204832f4b9138c1bddac91f1f52b96886d3f8454f27477595c6b5b58", + "0x10ee6ffbee020bd02a43ad7d456ebab2e6bc85ebc765decc01421b913d039452", + "0xda397ae9490e2cb30ae447f20d1c81ab6164e19139ab2d06bf3d07fc8fe2f9d4", + "0x1d543582aefc8ac8b437741d62fcb92fa92ab2bc6209d2e99d4171be5c8c5a07", + "0xeaf6185c1a06c8b6325e291510e7cc1a4971e54af19a00d1093f80bf9b797b20", + "0xa5c007db023053a6c10ddf84b271908c942701d20a3370a6439e18ce5cc33671", + "0x4f765017b4a1c89831b76e23854e056208e73d6a9569685611da7477ca6a14a8", + "0x6229f2d5488779f485d0c67a57a9f2afa071aabe51080a32f4a7b5d5362f11e8", + "0xe90a94340d0c952cbeb5653cdd524806571a75993c3dbcc2732805cea87002f6", + "0x61e85be30544ce256f47a0dc90f990232cf00f8dfafac9f015624a6c2ffc9914", + "0xd9e80cb86d129cdcf846d823174232a6ef5d700b2caa9c1254b8caa00ed2f079", + "0x31004925de922f89387890b3e7f0ee71431d67298f701a2342144415c722ab6d", + "0x2b0dbac50a7a873e34b8204bce97501f01d7b487bfdb3676efd50476d324a026", + "0xe6bc788c60c1560a32556fb658f49a35e60660c1aa2724561090a25e610e9a81", + "0xfc55dc61b0d502fac418c70618e119541800c02dd4e2f3285b2ff800b16c950a", + "0x58067dffebf182622b26a06102356fed64417ab783b04cfc979e11262e3a3acc", + "0x8e00b13a0388b657ede05e1423322e194d3fa4e3fa680a9eb8481b6bdff4bafe", + "0xda840d0c79764b16757673227c80ae50cb576422ceab100b66a17d788e8d6033", + "0x74e3db7ec603bc612d5c5f808fc97faa01c0cde0bebec2406497f20a2ac757d0", + "0xd8ff3f85bdcdd1aa6a9703d34d046b668830f4a0b12806ee2b2c8e09abd95959", + "0x554d8290962ce53402e520b4377d65752f6a4a831bb14abd0fc334489c33b9ea", + "0xdf3525583839d3acd441acc0bb044dcac9d08a29da028e1c171be61669fe1dad", + "0xf6728ee37f5bf4ee39b18212b85804749bf3b567012d6dea5c3e4825cbc156e7", + "0xe6b1dacaa117ba5cf6f7523324d0a244ad49a8c485831f69cf8ef73e92a01138", + "0xe3f251492e7b65c49e725b5da53b082db645b7b7d1db38a3d17c06e3879c0709", + "0x4c052d03e69c8b5632e179dcccdeca5189fe19f57f3c7ccac21e71bd5227bef1", + "0xd9e59ab443703b86436c1ad05ee87008b78ef5658da3d5cc692333f28ba98f6d", + "0x71c77385acc1ff063aac70f4136acc3ed4135de6183f295f73c13e59b122697c", + "0x604944c9e9423d74550dabf3696e6b6a1df5c2654891ad6e6cc6ccbfc10328dd", + "0xc7330640acdccafceabf31753d1e1f202a3e73b33ad2f1f9a7ab911de30fa8ae", + "0x5d1e070ff8c89e7e5e73dedaed4170ee8222bc5ad69bd7697e5924ec47def196", + "0xe31424bb5925fc8f34fa25440250f66f733d81465413949aa919494d3930546f", + "0x52e992a4391c3773c311ff92eb4da60da659b6ba1245ed9c6ab9d8b8ab747fc5", + "0x51655a25c7c97185f865a94950e48774ae8bc658732a1555b089e35e6178c709", + "0x4b2394109e14b498cbecdac2d561c458cc8864533054bd45e064b043bcb7f453", + "0x33e3b2f487048ab92e924ad3dbd5d9b8dfdfc694fb74bca7313d6a3c57a8b8f8", + "0x942f289c048a870f1697f2b9a19cedfda69c24601e86bff37345a7d5728ffc46", + "0xfcf38a9886d88968b3c91c020df35822428ad367cf65e60929dd8e5d3b3ecee7", + "0x8bd2ee12e1ddc1a6ff3bda4a1122a765adcfc232ffb17aa9d96eabbb48636bc9", + "0xb0d2ee5c1b1117d037be2b3ec7d652a4fd6bd2e1e548743654f243dc1e0bbb18", + "0x26dead205d5b5d93180e59ea370d1a55f15febbf465915c0d25f068881657f0e", + "0xe5f6c33987ca041748b19cd859f4476333d05637da7de671eb8e642f45ffe33e", + "0x596f5f45efa7115ab9b7fbec97f1386fe4c5069c6222e24d066b7c8fb667d6c0", + "0x8b4a810c945d99a05c136d64c55353966abefd34a69860545aac06d1e02e413e", + "0xaa93f27dff8dba5888ef574cbc91701907ec267ecfdc36cf7d5d210bce69ec2a", + "0xfcf588518228285336dd59f7c7e71486f226743135817ac20d052497526603ec", + "0xd675af91cce4dc2495ccd150cf8973e44b3fbd3dca704ee4dcc0ad77beddfcb5", + "0x1a43cacb9fe62fe35f946c9def7b48f1bc999ccaf4e118588b0e44bcbb6496e4", + "0x432123920da57900b53f9312caaa72de4406cc4d71c976b5f2b1a28fc933520a", + "0xf4c6b023b3f7cd522bc0ff7bd51da834e3f8c981be007738a2c8d319dd57f59c", + "0x0b10c58a4809d8716cf97a498c04089a6815bdec07dbe7c600563755d79cf0c4", + "0x6bc38caabd1ddb2da34149e8881564140980a846cc9c7fb11936932d5f2b5009", + "0x23f267003eb3dbb236b0d0be3f00e328c99d2b7396000a4aeb812815b0752d1c", + "0x7d291c973dd95969b77f0a46a9aad50fc3a51b97181bb30d7fef78d2b8c4f250", + "0x0e11901c035bf37f0ecee8d91bc4becf224f886238ea2f0627ec679cf6183221", + "0x03e71ef18c5f932ca99f1eb898ffd1c0699d68a52a509a62f6dff36ac5cf5b93", + "0x518c49c2fdfeba93419d0bdef6aec1e603b7e3da440e57ede65c4a8ed558643c", + "0x2c867a1348ee59163c313097ef81825d2adcdee5bb24921a70ab67c5549b875b", + "0xbdb494880d5868a527ab150f9f5ef4f6cf31d573653ab227c34d4ce53b3c62fb", + "0x17713f0d60d78ec9a871df91178a28fdc429d29eb76aa97a8fd6bd6a3b768979", + "0x6cbd2b050bf2db1e56bd308d7f98f59bfe317e0d1b4ededef46967557a02f351", + "0x90eb4b9f9aa9142b6b63544d04bf8c1427234fb5d6944ebd0b342ccc50f69749", + "0x2d5cd30cdb1c5cc2cd836079c3289c2cc29636bf3b8ceb9513c425bad3c66b4a", + "0x47c04cbbb6fe7ab0ec0823d65a81b4ec250aa662a78ee55ba1fd37b8b6cb7947", + "0xed1793136d4806eac5317b69c0814a51e384c52664bce56ae10613236ad1fcc2", + "0x2fcd239a00acb419b61b0c9f2ffdc358290725824df957a352d7d1aec4d2731d", + "0x2f0101d5c287d14dc891f3e8ae465af995ddcec89f02b381a3f36d5aec65bee5", + "0xfdcd0c69a2af8bf99425998c4a398247911dab41423d140e8deab2dc8035cbaf", + "0x4d1e239e63e5b978fee0154da78f84a1310d560881ba8c6aaa0b2c08fe72d419", + "0xf4762fc0837454412dc04cacbfc6fdbaac7634d13e39e01ce61c6844506ff19f", + "0x0fe27ed6c051eecb408767455b28940471849be52020db10042e2603a4b8fb4c", + "0x2887a0b07a3acf3bbbbe27824971d4f17cc2df4c4f8ae4d8668d516cfeac1d35", + "0x7193f4e8694f6350597226d7a99c9191cab36df6d1e8bfbcb701aa34b8fea8e6", + "0x9a342a7554f1425bf336fad53abd7cab86d216ef36b7c81d91098ea11ae8a6e7", + "0x3d9498c28ea4e4bfa8c2d9089773a9d832e76a4078a706a6b380a349f7e65021", + "0x3b8f576022a318865fb063d517ef8b9dfbc294a5fc7f2a8e9cbd76f72366c232", + "0x74647320cfedc47e327e4de14ac2c2edee1580cad1ebe160e61c01e3fc76401d", + "0xe8b29fde7f9b45795ee6fd0c3675dfbac9139555ed74ffbf62dc78010b452a6f", + "0x1d1759b133834fe4baa440b0b1601310c9de0c25c9f2d24ef48ea66f4807ee73", + "0xb0b72159221bf8f1ec194c1cda4782b5ee40ba6ce8817c41fd05fccc2c6fa672", + "0xd77c604a6d0001559e4c1b450ff40a651bf41dfe97ad00824e82adcd6db0ffc8", + "0xd7932c898f5e7c90e21ab089032161074f8518d899b50b5565e00ae32700efb8", + "0x04e07100126852a0755ee92bdf73ae013c2ac8297732d0bf17ff32f1a0e68bac", + "0x1456d756d30720aa381c6f46ca97ee6e37c6302037d084e0a9446035fb626da2", + "0xce804cfb0a400f2f3b77f5f8729832b795f556b6f1263a865d9d557fb199323e", + "0xd68449a6c88ae09802ac3ed40470376630264b7015334052be5b319276ff260d", + "0x954a41a3408bc15b4d3db7e9231d47dcc6d9a406d7761b285b550d48634be9fc", + "0xed5e046dbbc087e12d974a32ce328233ae0abf470384163046512786829f2c0a", + "0xef5a943156bd00adf23e2361aa870d21bf12a820cbe5a4ae7177f60c962f7373", + "0x1ba67df41033051815cfbbfa465e1d84bb2fdda6ccfb1629b0ce2407059266e8", + "0x7973dc85dc575a81ef56aa04ea255b806cc19be887018c7a7df8c899b1693872", + "0xd00dd11c5f1b99c988db73cfa10b873685748c9d7144b5ca55ab689741739b60", + "0x14bef729df468cca0d5e0c16844bf55f5ea03770278aab6a12a9ef434b9f28d6", + "0x907d59ac2f00b7b1a605e2a062a2be6efd38b50e4339707092c9d8bde2fc2c09", + "0xacc70ee9e1a6fdedc582d60a8f03b0737315e1319f7a9670024ece530a780283", + "0xb073e1019837cc63a5ef01f8244726a0a28d2efb013f1218cd7650b328758bd6", + "0xc414b05eb58e573f01223129a8f5299597930b774d18d8f49a5624d5a42bd33d", + "0xc9dee1d5780e0f25362fc569d1de15abb3eebb037923be6182277e2efa9f31dd", + "0x545d4f93aabcc49dc3a0685028970f034a66748b052c68ef3452fe44090dce2d", + "0x34eb2e9c58db92f3582bb222fa7fc599acf00db44244e1cedcf947904d4e7ad9", + "0xb68e39379b0c3fdc663efc939509f322d722503dabc8a5748dcbf3c5b8bb72d8", + "0x9cbfa944ff532a08740a9b5f5074e4d5e66acc43bf2b65b4acffa94ca7133074", + "0x9ebe0452bbc51b227754a891d053f0f359255710b9cb40e4b75b47e13d469691", + "0x64eda967aadf1c7ee872cffc7ccd279af760a17b7bcae2b0fd5be8f84b8a5b85", + "0x1c0590d4982346fec3545857cc1f5bc1480753aa81a3c05749d04775b1a286f3", + "0x417b76416cbebf9192b1923879b471f5d57dbad682efc8d11b58648dd5f7778b", + "0xdc704dee2de533182cab79c0ffdad21b6db8ca91f33d9031894d0bab2ed2575a", + "0x45cb6a764af5bc0485d4fba3335437f68a389c14f67b4061cbb6a676f60cacbd", + "0xcd229bf57bfd9a2faa4db06038a114db7d53f3b23d02b78574cbca236bdc78fd", + "0x07ec05ca8564a7aa02ee98264a94828ed4976946f81ab7324068f00d9fa1b872", + "0x06189ad4ff68f84ce1e8dfed5f702e5c9f2231aa644accedfbf7bcc976cf74b0", + "0x7319fbdb1d54788a4ad14bfef68b06f9c753ac9ab29be4992658a81bbee796ed", + "0xac6d85ec9cc6ed4b4e1a50d01309d1ac89c4b0d56e4118809d63b89a19bd2e2c", + "0xef1e3caae54ed38e173674e119ac9bb4910dd3399376b4c78b5b0fabdaf1a43a", + "0x08ec2b20d351015295cc7dd9b297d5d89f362a25a05360240af9caba653bf8bb", + "0x39d2c337de9ffdfab6ebc833d1975534a4db1cfda7be482dc4e955e668b8c322", + "0x888219caac34ca5cd3d8a131d74d17a89bbff3f571dab4c1ed86dbc87a44bd33", + "0x982ccf63ae37140b34bda7632e44372ef0a8f8ead7bf19a6368d077eb3cad9d8", + "0x4940e11618b90ba7e0b32d803b6b1a9e8c16c1886428dcc7d66f5f0d8ecf952e", + "0x0ac7ca79c8d441f33e67b78cc27b2836e4bb552b597db3eb763aeb5732b726db", + "0xfd6b22bbad8d51469d516181426ae17cd8f4bc0c2d77a86763e8e8f8f4157836", + "0xe166e401509ca4a08d70c9564632ed3c740a07bb526e47e9b0f61fde5865e139", + "0xa2eec9783e0cea503de68f1a686f8fd61999ac4c858cee4b8d7709b26da3dc77", + "0x16231eb11ca723b9fc3bd6873568cd728941c00e6f73c3eae4448093990ad932", + "0x092ff389fd2a44f932ec5509433d7c754075135860c5ad537c5e3a8873499864", + "0x0ca64dad46ffc6fedeacc1da0a9a1344c7ed5575664c15b29dc53b6bf0a4092a", + "0x9c084f7661f64ff7f663b8029d68aafd661afd468554503a14810a66ded823c4", + "0xf8894a0bd4d27384906ada732cc60fc27c1b381f2ae5a3cbe3b8861cca5776bf", + "0x53d365b92bfc7742cb3382e484709f622b20870c2894924ada09b4cc1f2baa24", + "0x272ed795be963d7e0dc3ceec238482ddfdaff413f1dd6c80c2eaa702f7f71274", + "0x053424c26ebd41a682ddb99e69b1a9f8ada7890ff9d9a9d3766ba8b25c9a90ff", + "0x92e099a6c48b93a78fe620b8aae5823a5f2765fa084cddf08f3337ca4ca227af", + "0xe623dee921251707bdc77c6f90457f7e47e1e03f43b539f9337a514972efeb86", + "0xd53ca67a3632b20e90d8f2252cc6e19fbbc605aca4a4901119fef8ebee77d0d7", + "0x33ab08532c9f484051bdc148659b8f63e67731c571e28938ab615ec3f26dc24b", + "0x71d73bc5d7fd47a3e7862748d7511e7891f653b76114fa29cd9338afd2aac889", + "0xf9de00293fed1a610a7cef7b1626c6c6fb0f1cd2ac3e68f70a4cde1cf577a543", + "0x48e348739a12481f302a9d1fa73c788196cf26aec218cd2de33456b35a0bef61", + "0x546bb26306559fdbd9ea947c3463bd1437156f556b88c3bc2baec2652f1b0fa1", + "0xf101fb403e709b211b4d91bc0c0546e14ee361ed1aae89e3675536749f984b30", + "0x946a320e924a5bc9774ace1b67be1c2a2ba02569bd4b8596d803ef4004d67d91", + "0x17574fccfbf525552abbbe41270308efd0958b2f3ad01af33c261bc13b4d61bc", + "0xa47043aa9afba50949f64309a530e8fdab6db2480b9ba132ad07e01b195ce9d6", + "0x53d3e9e10418a0dbf5e8bba5ef0ca6715d1fab7fa2c8b48f3389b47d6ae63422", + "0xfb83930746632bae5511db9429ac09233171b67ca7b929aa972d0172c9f030c5", + "0x6325249b0ea69e458b0806b2529338459073320776bdeacdf86bd142d5e38f95", + "0x8c4b83eaba64df745981d827913ef4bd0b2835c7aaa26ed5c845d7a77122dd03", + "0x0e532b8e6ebaf52a2d08d77ade60fe677085936e8e3eb8c871c8b8c6e9d9f615", + "0x5200f93c675c94541e831f988d520099f975f3705b5af4ca38c7c1e3ce24159b", + "0x00e42e67d7c65d817ef677f83bc2c35aac6ac3f919f056f1811c7b3397d6900d", + "0xf436cf830e6a610d5a20de1e1a8d03c0438e833e355d29dbe9e5ef38e08098c8", + "0xb786ea0f9c92ac09c0f2db5f9ab979ae0ba55ab64639c60cad9ac8318bb369a4", + "0x25eba3addc5006b8b34b5633e1bc71c00375ec449ad6c523cbdf3765c67a95f4", + "0xf51f1b0f8e808bce0ea5fbf3b7172efefec0285855c91e23c24925a8dd35578f", + "0xf904f5b407c06cd622f210442edcd303931fd945375c0969a0d37fa98c870351", + "0xa3ee03d5bde5889525d4fea43fdee06cec1d0491f52419c3868b324517bfc2c4", + "0x577881b5ae7d7bab29aea40f59096efa5ab3350a16106100d78f59717a945b85", + "0xf0d44930e7a2ea541f0f2109680a665a24d048e9e208b07f7128ab66d6c78e40", + "0x3a298541a917ba4784ba8069df333c10b67d43115d78e700dba75ff101142df5", + "0x686640bca89e95328336f7849e745a92d7a2dd754919beae7fe0ebe753c16d8e", + "0x4c7f25aa6bb36b2de6f964271c2d69b9f2d297a6f028a81a5c5c6e4984954303", + "0x7af46c35c0329d559651aecab514d03d05e8b656138eddc6efeff438ca992ec3", + "0xacf0007ccecf61eae14da3d9d17f4d9aee0a9a64263471d56b6be645e0529ac2", + "0x9034c3866321e99d057d144fcd8707f1b6a4ac58b47279108e78cc5157d8f5a6", + "0x71c66aca56573e380c126671083a84a5e6b1d6551abc946c54bccb8c191a3bb7", + "0xa37d916640305bbc636097375c20d1af58ba8527706011ab1b6b15364d207841", + "0x12aecf9f53ec4291d7bd25d9814e76fd51abc2d156846871fb814205a4105e74", + "0xce8b1c6c1af6d90d434e822b07cc0306cba01a8b639521b0ebc1ce3f2e0c46d6", + "0x556904b07ba34a00b54df9420037ceac4053b2e21a6165e3e47198127989b6b5", + "0x0be8adeb1da71117d3b3548f10c0d36bdac9a426b16e2c90c3b763c4948b6738", + "0x07d30d26769dfa24acd2f5227ee9099224314da3170206cdfeb9d96c37061dea", + "0x417ea7b8e7920851bc5fe84d6b51a8b62018cefb6b06a1c87a46d1fd596e6457", + "0xecafd4b1b2908ec2b42c4d1c4f917b21aa68a338bf3fdc9e556b52e05266cd4b", + "0xff464e05f0d94d291eb3596d8084c0c8289601f20012855b1e63a707ebd33ceb", + "0x4fe5e9fdf2dcc48de619b158ea59baa489e6df86d6ffaba250c8866f890f124d", + "0x6e1f3e0f637254c4f24fac85197ec98e2b25f6c39f67d1572c088abbc7d63ce3", + "0x8e89116708443aee8889c4135b6ac3939fe532063050925685283f803fdf1489", + "0x931a4d3db8d56064e56e6db2632681b750b7bf987a60bf74968602a506a589dd", + "0xf1db53212d0e9342bd687b5249b6b109ae5fcca3d41ae885a06544d78f3060c4", + "0x6035991253e48ada18ad81f4ea79246ac0095970ea563b1aaa0e35877e3e1e6c", + "0x07946f313af9afbf86df1a52def9f3869d0633853c6996bb02f14681333ca721", + "0x975e77feaa94605ce813445b62a2d4cd8e1f2d742c3622a5a06fed39e207b3d8", + "0xe608ca87cc4bfb7b42326f43309e24499614e9b85ebd2cd539304d2947902f17", + "0x814bb590b0143e35c5741a8becfc4573c6ee61582801decc3eb5dc5414f89424", + "0xc6544d6817a8d142c56fd46ccb3acca0818834defc11c4f06ec472b2450fadf9", + "0x38e66b8364053d62c79f950090eb60bea0677ce0bb0b45262dacdf9f8e6c4f2b", + "0x88a700d4c6b78e6ee64f6ebcbdc265bc9b2f61c02566bd184ac117bd5601e26e", + "0x27b8680fb11352171937f5840d0b75254cbb28343debc32be37ac10f4d34bcf1", + "0xf72f6d327844adab19db8940124e8cee2a2672aea083afb452a1f468e862cc13", + "0xcc456864e1600a833045bb60a4fa03b3e620d63822006e887f48b48b9ab86faf", + "0x5c97f352688bfd9355b3cc86ab2e3054a27141cd5387a7db6fb5fca54b05c354", + "0x7aa78a637ce11fc70a87f21f32871383d3b91a0da12bfcb6ed3fd02cfbfd778a", + "0x98657d56c9b9016fc1daed5b921fc721c179c978a57bd61395ae7759d3207128", + "0x6d464ee031606a3bbc7976f5512a0dcef0f3a4d3043b9a4a079d026c8c06efbe", + "0x2da58df0b3c0a81ef2b946923bddf936208f50740c9c7e39b57642d6662da9fa", + "0x45cb8222afbc5c12094ee9073cbc8e846b02365a8b756343a5bf912bab70f223", + "0x25388530cdd14f5fd9289186970d8ac4d00239c03fdf76510038f77b76931c46", + "0xff08218e785f77123ab6949bfde4e78cfd9b2c28938b254813ca27cb2d926712", + "0xa4283e8d4cbe64f69abad4171d6acceb51214a1a909df8ced7b27237f3c6322c", + "0x1be63cbaeed036d6842bc54a868c0760b9fc640cfe94b307d826f28de279c96b", + "0x677d1b43d74bfffe78411cc857b3106228aed61f085a1e6bfe0fb6cbd111d6cb", + "0xa5a7ba8e14c4a1517555cb060a051a987060dbfc2e35f3686f2bb6609afd5422", + "0xe7565681014bc1f4a41caf18d8eaafd665e9387c18ac8ef5844e118d3ba5a51a", + "0x29212307701011bfcdad7deec0d93434e6534d8b9465a391c3a0df3e4c449b5e", + "0x560e4e66c6b6f27481ab48c8b4472b82c914b24c8a5510bf7efbb5dd38d27d01", + "0x77b691395557dd58aec1967802e7121ad656d01a450e5408ee13ea62405bb840", + "0xaf49fbc47bf656bc63b800c24358fb815751fab456c36dfbb1f2ec1f116c7b3e", + "0x6698c28733c116a2fb1ced1593e7c915582eb05d695eed5424f8cd54b5ee779c", + "0x442d52743ebabea45be77782e799922495e8032035f35d63a75be015f6caebbe", + "0x7d864d37058d6c2e334276b86f27a5a0e9cb8f800e1ad90a8bf400a8a1b9fbdb", + "0x57188ee30634817b15933fd24f2dc9fffdd17e82e3e8cdf11d7017a8129d7bbe", + "0xa25bbb54e52c91c9f1732f3eddfd10976dea242117e005b8e0695aedc6a030ea", + "0x1f17d58ba51a4143deb682c03c40c5e82c5edc162127cf85f18fc993ab1dfabd", + "0x511f20827fa98a5bcea8166c3170dcdb5d71554c660c4a6bd57ca680e0579446", + "0x82e8ba56560e28c7b849a0775cb38a7c422d2a3738e5638082a047044dabc809", + "0xdb2fc4ac209f1fbebc7962fbc80d95fbedb7f36ac11c6e04ddc661ba76c04631", + "0x5f7d0e68d80c8655192325f3b74655b89bf827f1f639b628aa1de3401b3c23b8", + "0x07afa3785bea221fab2dc84166c1a9f83c488d2556053f317ad0c38e41bb870c", + "0xd7427083ac37a4df3a5c3fa4f48af90bea3690f1915445797db8b35790494f55", + "0x4ad78ee5835c690dd3b8774760b6a5a090e9f95eb2b0c8cb7758b03e9d450f50", + "0x3eb36e1b7a98707475ea9939ef6243a858d389e14af1dbdf57b0dd25e4bf693b", + "0x417bf68d739f9bfe640aa8ac826a614aff2acdc8d4adb619025b0f5883d1679a", + "0xb60d6f5dd1a5d6fb090c65329eafbe1ffbcd5db57dad28122a72204b7dd761fa", + "0xfc52a20d52c31bece4c1764ad055da667a2960a7d199294868801da299afba10", + "0xb7bd183d7af90730d530ed5ab473a5feb6e8b1354b6ebcafd67b1b8b9f0764ba", + "0x3fbe273baeacd83fc0625c7dfec595b23f91d291bf69c7d1abd76a302adf2bbe", + "0xf87bfba2d8cfa17912b0fda00a4d1aea7e879ba285d6f65c81aa8336b93378b5", + "0xa555ff2af0fa7353a111b9c002b997f57db5a61e385a4e41748fbd3b4b964505", + "0x330addc5beae770991c11395b49e1667cdbd5af2c3dbf5ca8f818fbbf3a37cfa", + "0xeb88d26bb0a5a05adbc66ea8a199bc5786dd18f500b27542386c518db6819e8f", + "0x03222ee703d890b9d44d1b3dfcd56b25682c5cfe139eac93d727b7e1750ee2bb", + "0xec13cdafccdc4b4a5b4a2d2d20f22c29d4fc349eba2183bf5e96152bf8c9c514", + "0x45b535062fd3df6d2b8373aff34bf617641e4a782bf1ef1bac82197b0abc0c10", + "0xa912621a90ece80ffba902eeaaa8b20567e9861ed818ef06aa80d280e373de5c", + "0xb59634acd0fa170ceb508cb9e78b04c55f91eae7b2d923cbf7a02706e372373a", + "0xb4572ddb7a85dae317a21a1ccaf195cd5a1b5bff462199e7f7e63de85b6dd9da", + "0x5468b7cdae4569b97fab2c27f9a963a41e38690407129a761a7b34ef02308eed", + "0xa4a4592f5bb435d3354393ceb2bf39fc4458415a1add1c801d9404ec5424f0fc", + "0x9cc2b240c60d29afbbc7a49fc64b20ff3c2b9774a04939b6e9bb5a5c9a5d7203", + "0xed52a0024c13b17f7e7ca7266099ecd1055d2b9e779e7df23f00b80035615626", + "0x84cca14133d9510f2186febfdd742537b12b23416c2e173918610bafb828f48c", + "0xdb8fdaa51de24fc27cba47ba9b7eaff843bf286f796770827c3d57d841bdc6d4", + "0x8d27410fb463e92ff3e84366194a106f7ca899d37ce2a9fe7232c457c1422644", + "0x25ad22edb54978b462f3b9f8eb4696ec187e019523dd1935078004f23b328428", + "0x6ec3765bcddfe20974af1bf0898e12dc1a5cad017a955f471fe4c4558fcb0cd6", + "0x8f334b6c435d456b808336341a86929d0237c786003d78b2499fcad57c43fdd3", + "0x3b5e9ce0ddbb0fc084ad5cb1de851ec19a0d7bcb2346f8dec0370b70920ec498", + "0x7f997e796f1239234a28f56e7f52fc47531c449003590f7d8a2d6c05102d7156", + "0x9db2500306ca0af45247358680e87b62d8dd906a17cac02fbe8552b97ef301c4", + "0xa66ec3c9f19ee4e3714ffc0b3fa11a416472d2d9aad060841631ca3607c740ff", + "0xafaf807ead0382f869a918eca237be3f960386665fb5823da7ef0723620a7f39", + "0x95765e3d7bf10d1c34b81927ae1b99c2f1919dc4cb731dd9b7ab317308eb7079", + "0x02c8880e0d0597a9ccd9ee1b3929e2a3a505e5c2aa7ac43562f19688f4cba2fe", + "0xfa7a171361a3e03b72d92cc2847cd6abb9331ce2c1e812f568c3c15b9cd50615", + "0xe61390126a329b2fd7a514cb1e6b3596624a47bdc7f70b618edcc1a15369dacc", + "0xe4c1e109932788993706769ccdc4143c973a397aac3e6abfafa225b6ce0537f6", + "0x4a8fbc5523af79991de4f87a5736ff5a0aa09c6bd1340e8b3dd0c01727965861", + "0xcf56d027fb0d1deb06d2e540506c90e1d5657ea6dba5f9941128a0d87d352ccb", + "0x7cecfa24ae8c896b358379ab9b81291d8c4afe9fa69cee427a64eeb8f750369d", + "0xfd64e1864688680e77bd02486da5f2dab5183e07429fa84f6736d01daa6f15a8", + "0x81809df37c52c208c5dcf66ae2fd3fce661af649543e0ee3b617a6885c22e6f2", + "0x7b704be789b4e8768320b862cac676c60e7cf26efb8daad4a80a99a94eb9a50d", + "0x654a17cafff2bee077621d920ba42b28a87015ca10476c8d731346748d761cbd", + "0xe5901af9fa2ec39e8a5cd9fbb1f8120a2bdcc204aa88995ca7b41a412f80b26b", + "0xea996b80c57a2fd0c3c5b154651b37d9d67441486838967a3d079cd35ba55bec", + "0x1adc19de37efe53db1c9f868f4c148cc6ed807746ffb18dfde56ab8858cfd3f4", + "0xfeb49dc2403f9ac15a9fb90285fbb588e6ebea4a8dc3ea69950948d53b5715be", + "0xf17ec3ebb36cdab2f0ecff418eb75b069e710a4c1b0450d3e2428f3733df0b10", + "0x08d4c8b51e8b9181f845765cf94ffd9b7b3a4ed273080c7e9487f6c9feba7b01", + "0x9607d9a882c74a3a7fda661177d2b7950a7910d5edba3447613a80628473f8a5", + "0x74bf73f0e5bdfb2b287362f6a6a2fd127d97d4cf8d421590d0e54ba741e9c8fb", + "0x888e33ad6fd9077a3c6b60219b2c9fa5f422bc26c2d85b8ba5fe42ceb30e0c97", + "0x5b9a4c4e5626bd4fcf8febd977423c26ea2f1f6d26b5a1ce05bd5767a8e8ef98", + "0xdfb1367e4ef81913365e8d56be5ed96eb0f776e643acbde7895fd4f3ba9ab5a4", + "0xbac596c19b904c6355b115a164fd1aefca775fe5a10906665c00c361def160f5", + "0xc7db171eb1ab2f7a1df76e8c901ca8fdd992e2d5e27e5a8f5a43cfc7968d4ab6", + "0x2ef58d1d5b6a770f3422a25b422158f03ff453578e3586185e51ab2ad8f76a4a", + "0x7be9b7b1901ded0d293f41e8c7e03e5505f3413b3f93648d53b859061fd2fbd0", + "0x0e5aec6c12294ab3153c869ff3e4170a7a26c25895c6b56c03296ba7e1d2d627", + "0xd1f2b89214f1e8cb5213da982af7c013df52a2a71b152c814067a9cc41e86c82", + "0xa8a285e243c379b7ac1c5ede96bf4bc48bf036c2d9f246cc13fe6e35fa4035e8", + "0xd514f44eaf654137824f0a2c13019c0446ede463fb3e790b681fb668bbe4981f", + "0x266b4f5ef4ef5fc9f974fe74d68a52ced14980d11ca17728f9279c803e29f4f1", + "0xb47c9ab284361347e17cf0284bdb27ee1df97decd766348886f70c70d42cf764", + "0xd71977f0ff9b333f98ec6a5ce04be37737afdd5d77166d4472082bdb0bd191ce", + "0x303202b7e9d19df04ad681106007ba876ecb623cd9809e8ceeb34381fdd8339d", + "0xd16916bb51342bbe58e85dc01e107936d134a415aaf0f4f125d6f64a2680b36d", + "0x8f9f9db8fafc2e2ac65189ad5944eba08e73186b4883bad9b72858ee33615983", + "0x03dd8d116f53253b818ec26023615da22cf58650d239d621790d56ad4be4566a", + "0xe2aee5670efc9dbbb26d4aaafa91f9447dbdd9587098071eaa9ffff0deec648e", + "0x1f12e2c8bc154615319ac5a9a02cbd04ee17dbdad707678a3c8f9ece39b15215", + "0x7b3eb070eaa459eae7ca3813a691cd731a29a65b99d228c74f392a7dfb1c0f5f", + "0xe6d08a49a59ecde1c7186ceb8f6a1f5a3cce4f0975e535bacaaa4bb7fa44e93f", + "0x5b5ffb9e9d53d1f2a6ec2fa36cc8470a603f333642497ac2e009737df7090e15", + "0x039271f572fee21682b0b29cbc7268056c89c09a878c55a80076b7a5a5b12266", + "0x956e664eaa24048ae158e7b879abefa72c8634c17279741cb4e3e1d1cd1dcb87", + "0xac0a980dae3203092527a76d40edab4852bab8fd9898a4afcb992690780beb58", + "0x109403c2870dc95e19b5fe9741a0d4a3cb968946819eee145893470653d8eff7", + "0xc23d38d12876f37c8abd8b29317208d2135ecceb6a61c483dc84a383153ce82d", + "0x5559c644dfadbc49a1180e39daf38990fbf098738998e36a5c1b896b9ce25a17", + "0x1fc83d4d4fb1bd7ccec7939269304c350b0f2a5fa5f8a9ea7656e030e9c02e24", + "0xe01bbe46c40aea9865e21b5a85bcb596b51f82b88aec49c197b858d7b2b17dd2", + "0xd06a72af3ac480cfcdfb456606031428f549399f0f394c85ff4fee734919e58c", + "0xe9c30163c1d00e2070e1becfd36de7149c5a3a5a7ea7d0eeefabb21f86e55f24", + "0x9030307d47e6ec28de7dd57846336cb431e0145174e5c96cfee711e1d399a5ff", + "0xb0f04068a2cd1392a3c2b0e8a73e0e6ce3b0fdc2149b0c42b26f9aa3af373aef", + "0x428bb9631672275625f602c23140c06e678d05db7ba33098e533a0228f4c152e", + "0x2d0224764d8acfcc14260855ddb623d3c21bc515745a052d7231752ba469c94d", + "0xe5e36acc7f1252353782fbf47606ca5511b8c139472c0d8dd8d20c71a65595c5", + "0xac61e960bd2ffdc9d5c93ce1399311a44437f8c25a0be1883423e0a818194d48", + "0xedbc5c891b4c34d2cba52b3155cc740c3cbba70cad3bb1d56dadbbf7d021d4ad", + "0x5bd4649a2ad2c2a983c734db0787336a7a20702f9ea4bc85339c9a09cbba1f34", + "0xb0661b4fa20b727da771a0c51d0a1e8c6e366c9c756ad638cb618dbac9028157", + "0xca2b40603e3275ed409c2b20565dda17ae0ae1438a2c9a7eecf6ed2ce62b9251", + "0x4d8b442aecec1a1e43534d38b0fd9e9c91e3eca2f1b7c7a37ad7fb024e0d0d99", + "0x1d9cce772b094196c940e789842fbfee409a41a3ac5e624ad694f64f90df52b5", + "0x9f656c56c11fd9b1ad67c4a5424eb5f992b16e7412149990a83fabe42c8e63af", + "0x45b8eefecf351e5aed4306568ed23c5daa1331eb56ed4bf0b6ad78027ff7c6c6", + "0x88a97442e7773157a256fa6f5a4a9d99b19ab95183864bf644c035f9313a9226", + "0xde771a66da265d8f3ebb30662bb7914fd06e0d81ff4d38e6764f0d2edc468f0e", + "0x7c1bd1e1527cd654b1a7927b23e358839e5d5706fb0dfd3772c0cff999b73f92", + "0x6d63e86d34732aa8091ae511aef2b0c17a3db5bb9404a64f72d3c667bdc09661", + "0x60f649a05da294a31527682f6ad5d22c33f50909d2a1bd10473895630bf1d15c", + "0x3b6396e59533a1a2019bb748e48ae727a03921a77995542ec7b78e3ddc12982a", + "0x681657d12ee8137d7f92318047972787454cdeabae78f838a178a870f028b601", + "0xacb9c68718018b41b5b2e8c223d43d6766123c7e460eb52e9b572ed5a131fdf1", + "0x5a3d97d51c355ac1e708c660f398ebef63c1f7be3b02ead995360f7a0ba6e40d", + "0xc935ff314ab3ddabab8a57461d36546dfc1c4ee4e8c91da6b4a492dee38be2ce", + "0x866bc26b716398c072440c4965c02a8a7e0e5ce0d683d023c4e422f505a2e151", + "0xa16a65eb87e26547c1c5130637735aadcfec49086bf08d2294052a082bf8f80f", + "0x48cfe6a1bae0f33a499b67ae4076b03ede8a7203872e9bd48eff4946edb53fab", + "0x1af286be6543ab046509dec7cf9aead8f816ccf2904453b48d4168fc95155ce2", + "0x2b07b7d0df04b071c3817ecfc0276efcb86f4b94c9b20ae429db28782e8386bd", + "0xddd9291d10fcd0aa6034ee7c275c71362e677ac41823fe6f8c6bb61486d2df29", + "0x241b4c93fae44cdca2231fa4bdd29ae95243f523028206e51bdbfa30534e698c", + "0x1a10b6cfd7bdfb70d0f5804900a329dc746081fb04e6a21383fab4369ae7395c", + "0x3ad5d5d2ce98b0d70a8bee2f2aa1025b30436d673bedc6691240fe13616ae671", + "0xadb44fe044a5d24b6f9c5559d9e16d7fe4f9b88da8e090cce7b67b24411bb56a", + "0x5587a6d3a0c8adc92d2efcd2d4d7faa0bea8d713f61d2c011114ed8b04c689bb", + "0x10e558a20a61e6c7c70043f7b0a3659ce99cdfcb007102ac08da6d936ed9a352", + "0x0a6a4cde4193b203c0129950535e4eb41fd9331221e2ef583e2412594ddfd0f4", + "0xed32c70c68609cf102449ffa824b397c27b726260e6177663ae4ebcf47cb37b0", + "0xdf2532165bbd958b89f45929ff8f5f9309746dfce9e639e1ad0dd2dee1f144a7", + "0x983f9f7512f726c1c5eb5153797ae4d91d4c3879bb797949139f802fee35466c", + "0xca7e5d64966b0dc955ae603867887a2549e073e80b815840eec1ff5c7a988f85", + "0x62e171570bbea1a74b480216252a97478f4ae56ac671333f5a1a5c2881aa8479", + "0x198488e87b005ec852ac5d34e7bffac8153b79de95f017d2621a8e3db582a86c", + "0x5438119ded7cdd3f49fc9c16065bd285eea329d42972f1345e6dd92d7bcd1a6e", + "0xa763fd79ca54b964cb80dc71421bb004f61e459742405ab347e69ff8f6bdaaa1", + "0x6efc44d9ba94af33b74f60873ee40200f4eae208a95e97827e8c4db40569a96f", + "0x98b761aa10e9812a2347f4b5754d12859742cd8e2069cc40b07432e7e5daf60a", + "0xcc7fb6b9a10ebe1cb69e763233e322e6c98a207cb8b5535e7573101c610bf1ae", + "0xfa3851f05c0f0a6b188ca3a777ec7dd333b6219027ac7d757a79c46c4d383ed0", + "0xc167b034aae8576c52478151537bd6b9d474bca44d37508f359b52c0f924330f", + "0x988cc7d6de5065c0762276718475d92f98a50a03e49d96c85c1c8671a361b874", + "0x94e03d88c1485bbc0f79b3faa01d98d5530b09032ff74a48a8acc1ba11e5b799", + "0x6f82b975f63cf5e437a4c963fbb78528729c488e059bc7b9fa4ae6c8b75b8426", + "0xc6ac054f2cb2077680633f30de0f9a544eb28adeb15b998cb178f931e94cd9fe", + "0x0a5ef6405277ad511cfba78b8df80efbb82a25a3667aca9ac103026b5a7afcaf", + "0xe5b545f3c843221ceb9561da8c9479561c276bf2d643b0db255c945ab2959a09", + "0x650200e5fc34cbe4f07e9e7ebc16a2bab290dffe8fc1b1c3122ba1c8606ebdc1", + "0x27d72cc6872510b3ccbd797312db580a86fd806eb832f3b294f2196cc39941f6", + "0xc292b942a16255640174195d8ecb363dfd50579533ffb81769de726c0aa2cfbf", + "0xf1fc84be7e025b82644f7eb68ce452bbe2357965327cb4f7f123c7b6704bd43c", + "0xe0c7ee1fbcba433d27c50e898e7b937cef0c6c462380c66e88aa25fa072271bb", + "0x0cb44eab50e1e98d90b759ebb78d287258634362ee3607a9cdd420d7f8983a1a", + "0xbec7a70c615f94b4616e220bd21cb067c8da2608b116ca60a1c13d6129517bf9", + "0xd96bb006b01d88790ad0f170fe7f2ea38714810dc08eea70a11204fcbc746b7b", + "0x6cf19a7b198e718093954b4b46867675f6b1cf4e2d9754b215e138ce93adcd7e", + "0x91827f2e275adc56625b33599cefbae0fd3f0cdf1bcc4e5c01d92a588c1c81c8", + "0x3c973c84bbb379586af104e58a033b7b4b2a26988f06af231e1c5d9a05a7fcb9", + "0xd2a446a7208bce84884f910c31bf83e2b4bada7c3ba46a4cc3878421360ab56f", + "0xf8c8d9dc94c74ee56839c9ae9bea5d508085e9442321667f363159e084b349e5", + "0x72a83ba2435121e5c0820f53cae3752db5af245ece9d97dcec48bc7304ab417b", + "0x22dc5441b78757659ba982fb08f1133fb16afd8c3484df024cd13c3af8d99dfd", + "0xb6ccaa1596bdbe8b017d952d7ab0aeff16f777d91c06064c888f6a4251bc9339", + "0xb64d98a26be38212ec561ac03a438575b0cfb6f591c3e9bc1d0d22ac2d650295", + "0x262eb0091ee0148d7430fa6fa9b00bb113f41dc258e980307914305c8674958f", + "0xa7db5e6f8cacf55e11c7c1d18248d6f3d1adcb6c21d4829a41393f043a4c82b1", + "0xdd772b3de50a50805526fa585ca08cedfd1f632dfbf1fb196d3b9d19a8482459", + "0x4b74ea73aa3dd4d3a0a20239bb4b44f474628aaaf7d2eb232e78d9160f9ac4ad", + "0x74157ad1c5ed0d7745bffe6e7f816571cdddcb4f5501d0fa59ca99b3171e5b8a", + "0x58a3f054f172f6ea73e9dec13ea58c5e5c3deabea1b1e092ff503c20510da9c8", + "0xa82eee89777b9c4b64fececcfe7c10bdb5df4c71c9b58431915e6fd2834d6069", + "0x90771662f86701e2ac692a875ab01f0849b4b0a1029eb24254bdadc2e6b172a9", + "0x60639484d7b56d3500d3f550cfa574c85699986ecacbaf80e78c94ec0b3f0734", + "0xaf74b4be658f941237eabb85ff6202f28056110adf2d91298196f654c39d0c55", + "0x20b1f6fdb8f53b44a29552c554164caf8ed6070581357c3ff001db9d60746d66", + "0xc5cb0d9d03cfa3e723622d99d5c4b1a94f5b323e6b9a8ca02cbb74cd12896b2a", + "0xa1dfcf845a08bd47d158459cd37faef4d5a103d485d892ecaf655ecc866dc6e2", + "0x77512f151479e02b4a9d026b46cb4ee20fabb1d0b078ee7fe3a9915ee0ca6839", + "0x75a54caeaccfe54269c3d7e5d428dde8b434096637883db20f62590bec535f8a", + "0xfcac1500dfd6df4211ce4737cdd521a192ba13d9bc327b72fb87462893ae6e7c", + "0x96939288a2e5f807767ac632f1166a1fb7c8bfb93cc9ac40ef9d9b506c6eb5b7", + "0x45e3415bd06c233349fd00b64317663caac7fd7708f30105e923de90552a7995", + "0xa36f7d3a1be71a495589c85d6de08ab32beac7863c71195c8068779d631b217a", + "0xfd872b6d9047f999bf3f3db8d65c49973b1cf6bb5681e1d994789882a0b81fc2", + "0x98ab3627ed66b6d22b2e496238f499b9de0db391df9ba4cf384820c74a2a8ee6", + "0xab816146607820dac4d2861ac60b70b3bfb5b1fd5b9920afbcaab8b39f8e5577", + "0x7772a4f6dd8057e7b76f027407bec10a863c8b553059f32196779238d62922d2", + "0xefc2b46715001f516c93409abe35a172d81224ae73e9c014ceb9c171bdc07295", + "0x8c8be28eb940daa5c4c6e50afe4436da1cf257466eb9eacc7ca97e0e8611a52e", + "0x5e368025e4428504f94eb8a8779d676a6fca3b281a10e3b7c5016e96d8cdde66", + "0xf7cc91a90e0ebecf4d715d84689a0b9662331ab00c4e0e729de24b9a54a3d443", + "0x4250d6182e04f4cb2db5808ce59dacca1ed3f5b9a8143e7c31c427afb59a0133", + "0x3eda8b0bee637e94d24332983e77356efc861cc427357dac41e257348f9e7593", + "0x2085b61708e0f855c1abe2fb6b2f3bc0f7831843a3859d37972096890a939a18", + "0xcbe49fa80d4391208c4a85fe1fb386daa4298b2b922cf2f3fdf80b9217602aa0", + "0x410079bdddfb2c8e7d9bf1eaf99596f13f40fb3b55253657eafad6c8d8601ef2", + "0x3dc07dcf00d30d5b0a6adbc8c45dfb5c832d38b595e15568e07d68a9b7bbf1ec", + "0xae0904e3ff2aea6214a03077b5089c28340f8bd6a8daf0b7d499357cfc704977", + "0x44449b2617a5c40513fefc69b85a82d95a13794f39cc1568654e9be435c4f185", + "0xfae88ac3a2e2b48a77fedd64e9adb24c9b39ae01cce791fc93dd8f194f083a30", + "0xbb0aed2f13c286b016b64ee52838c69e539d3a5631442b632c6c540ac45efcd6", + "0x8b140f28e435219fe6f64a8653214cd452e32b3658f8e4aac160c6dc5f310e90", + "0x2468f79bd87fa0708feb490c810ba5c9669b2627406fca7b07b8285144b29bc3", + "0x67500f9ea701f54682706f552e2d7405bdcc452784234c45d8debe0412553e0d", + "0xa7414af64d029e991382c15b54b8d79c4c2399bc54bb6d94248c6119aa0e5d22", + "0x71643535528bef6885d8bc4744e47016105f39064c30fb9c05357a5a7261c33c", + "0x47374b852f4bd8bd994c461fa59b9f54c78c5898da0de65bde1569601c5797be", + "0x3fe38bf6cf8a9c2cf4485097842bb1df5ba6ecee4a065afbdd21e0e185129bd7", + "0x389c58ac6dad6d633fdcf3be33d9ea709e848258415dd7c822cfa4c7de965360", + "0xcef742d21fca971a9a3f5a9ad059ff74b5c17e3f1ef3df6a64959069fca00f0f", + "0x0f3c52adea42fd1e881d0ed9d02a1c8881d5746c1990f4caedd8b155a7c6640d", + "0x5373590dd867905783eb7118471f69864a2d5b6f5fa9c0c3fd1a81d848554799", + "0xabd6d39f98ed54aecf01c6fbd01103b9fd66e37e62a969ce60be21c9a4c593de", + "0x341020a28d98b860a0c456beb30545e325f513e7ebce83ffb3e932d42502a20d", + "0x244d04ee4778470f9173787af5122812da2966da21270b053fb93f4bc54d15d6", + "0xaa747f984f78e8c0e8dbdb437f89c732455dda2826c504f310b7ead9503a7de9", + "0x2d72b7d03d41a98f0ea222c2abbc618eccf553fa9475688c0b26c941e41f6355", + "0x273372c9c6796eb418a2077d7c108dc7d0c72a3a4a28b2568a23fb9ec40aded6", + "0xd41e3ea78b331c5ac04a15910d8a9b24f8a5f07a46fe9f2527a062e3b02f0de6", + "0xcde1424e6f2c24c1da10f5e701784d634b650a74bd496f4bbc4bb1d87ab52f1e", + "0xa4243a6110bde2a34495ce3f9fdd72c415e9304391c0a7b4d459531feec320d5", + "0x98fb225998191983b66e651810d6fa1fb1759c82781d5768b7382866e559759c", + "0x26e31145a000ed0866de66e4d642de3e3c94694e841a95c16026cf482de719dd", + "0xd41c383752b5b0bf7443a98424c5da8d8e3cf759048d00e72112df5fd0cdf6f4", + "0x3e26a8c5c2fd71ecf98026970c660343266d437743119cacafb69adb152ccc8d", + "0xc444e0a2acfa1f930dda7e1bea25706103a0b3274b1f39023dddad1ee55d08e0", + "0x25103d1cb1119ce6caabeb9964573af986f40687e71f345091a5148c580dc736", + "0x1b0322bdece4d9d2dc2ed8998f20eb982ca3416822af145ad6c463f52bc28dd4", + "0x6949c231f71cae62cf9c505eabbdfc436c15babd6eee086fe2079f0d26f49a55", + "0x12552d43715564745f7d094a1bad4d218ecf9d235c444079ad125b155bc3a711", + "0x5f3bdddaaba0bf937f37583924fb8b0b72818a5127d11bbebdc88d5c5a1efe91", + "0x2c8a41e172675d5232338d2588fab9188e9164986ed53ba9f17303eec31bb8d0", + "0x5dcbf8d3245adafad50a35b1c3bdc678d57d4a28af35b7386208b96f0c94eb54", + "0x556a550d05c8152d7f47d8b5cfd9184d82a263b889be17204b71ebd1d0521c33", + "0xa1ad30e5eb6d482cb851cc114cc9193585fa022bc76d4e89ab7fc4df3f50985a", + "0xd534be916931442b9997c6555c8009ec6af89318da83c0610df7e2a54f0eb21a", + "0x74410e73450d3570b75bdb233b4ea9ea929c534e20c22d4170593aee047c3047", + "0x29a9db1d4d467eeb595bfa0ce7ddb53b0a669beff9af2c5c0a8f3ad8b8b47e2d", + "0x186a4cc308a14f38917651cbb36a5127d07c87776902a28ff11fff3a5ae9b984", + "0x4c22f8291bf7e9930456a65131ce651aa258a8b2316adae22114b90ab33d4012", + "0x54d021eac7ef97a61cc950cc20acc2748466f1678217e22fd721a0a435bdb79b", + "0xa56ad2059c0134e3d119ae6a9c28b02551bd818049e9a5766b709271dbaa3a85", + "0xdd85f2fdfc1c1efe7d53fae791bd34b040274b7ec39a0ae5f22761c0a337a82a", + "0x1b2f53b5c4483dd348ee810a9d230b7a7ae3233fc93b5a78b445918333a75646", + "0xd2e02920cdb121e71644e9a72a9287cdca74c00fc5d28eca70720a5ee9437413", + "0x3821f0ecbd90a25cb8db08aa4d8967328a93309c9ca7226d5dc337c520e54e42", + "0x5998733e9b89a21e18b8b1929af487f7e80211852a69b03449d3216b519e3b01", + "0x984bf0200998df132bf445aacd7613e1b6f28b2aedf5e5db9dfdb0e1a45e3923", + "0xa7a5c2d8611294a9c15e81a2c61790c50b552beef83f1457254ad7f77a6c8c6c", + "0xcd6da95b07a19fe46677d79f9d86e07175ba2e955d96d07912330c7af621e627", + "0xbc519c82186ddbc6624df645f33de6c93179134ac9f45bca017a053dfa194808", + "0x75cca153441f0a59dbd3ea81a45bec26395266c38f45b22f81d3e098f481f86b", + "0xfa2e353c6dae0dcf2c60bc6a9c930f3cf4aec7bf63d2440b6e13d72c6e04e50e", + "0x8e2ba56c19167b2b9a10c8021bd7321a1b0c07ec06d5a41bf67629741340e24e", + "0xb819ded635ea0c8970ddd804b0e245904d8562c8d515ec22286ea2e948249234", + "0x15e1b9e268780c5141b6a77f546595258d468a95302aada929b4bae6466815c3", + "0xb35bcf31f872e528a67ee50aac6cb1c17f08fef3847dd49e5e23b2f1956d5031", + "0x7d26e6fe60a4f85997092a2556ae47cecf05a9bea18e1afe948fe0e4b603ffc0", + "0x9bf7a72481371ee3d827fc05b89bc4ea0789bf101175d5e2adc6fb3252fb17f3", + "0xc2377bfa01167de62bfefac896dc0d41a617212909a80dbcbe4beb41d758f84c", + "0xba54adaadf413079d3ac0cd2d8eef06fd6e669f60b50d3be531bd2ea08486f8b", + "0x71552f71440739d8a98a1059306e816e2c34ab6addea52996028344d8b1f427f", + "0x001b57db436d22876e9fd3fd100a4240e08d873e9df4041fa7dcc48506ce0c9e", + "0xf4a860b320921bb4112d9b038a379dc3ddb4ea2acc1b372a4890d3da33b8431a", + "0x78ba7e44e92d97809f7616a152cf1a1689dd7aeda0682f34f6205eac5272115e", + "0x95992575ab81e735a3637c5cf1187ae1d30cb67df4734b77fc0dbbe1fbbe1643", + "0x106b93a2de542765ef00d4fef82376707f23bc94fa89d4434d046f373c647f24", + "0x3858c9ecfea0c7451a07af17298711db2c6c506fd856281fcc456d8ccdc5cc3a", + "0x76a71c2e229621d387372efb3d5296046e575ffb3be6f0152c561678441d4b3e", + "0x9ced192537d459a991fcc2c21b7c29c2237c473c3e227d5805c35569c0d52b4b", + "0x73e03de24c6a229d490d9799a8ca2d6bef3ea823b26f7ce51bf58058ada5005c", + "0x6322275bd445dd8118628f2215825751a7676bb0f10cfbfce5683895417839be", + "0x0748ac8e9c3041b621af304b9a483b483b9a41f53aca21280b65aa8e4130ad3f", + "0x12e00b5b54d80b4e1b018d9f737e87a8fba68752e85bfa297fa3e51d40331c7f", + "0x8a911e483ddf006136bf7cd77e0d8d5147208196c43d807acf3363b149531fe8", + "0xf8600105be9f38b763dde3635597b619922e8d9961f30a9c2921a9d8e14c1e22", + "0xf5bdcd4b10ea9a5d295651bf8ddbc4c9b8b214aac23565bac30608d24287224d", + "0x6b231901518c34f9a505ff988255ce8f38e010d000776126fa2af8b1c17f0802", + "0x633879470aa19fa0b8d3991afb2899329b0b63690b2adec803ef355f1656aa83", + "0x4a12fb682ffde1767ba5bd119e4b3c87d766ca7e832b183bc9702563e098955f", + "0xd0e78669cc7e9c9aa99419488e0605b94b1391fb16ef82d44b7105ddb7a9cce8", + "0x45b7dcf508406c8c1673a75ccd272a9c2246ed6e363ef91991d71aaaaac11ebb", + "0x6b2cd08b4e375c92118286a707665d650cefdada0507f3112ee866fb7d0f5314", + "0xfe3e81e7a566af259b14f68dc5204e9f3faed10efc63d2271f0fb36cf91e2cfa", + "0xef61185ce80b6ce4eacf6bda8bfc780e22d08e31fc1042d6cd0808ecf2d5c1c4", + "0x86b0e6f68046375f83c83e0307e1e6d66a147bcaabfe1688616c642d31161fa2", + "0xbf199ccb3e89996be87c73342edb27bc856aae78f0ecb8b4c30d8695fdb0d9d8", + "0xc8739ef53830288f33607d64f859eb36f6f83e589e9717dd95a145b6287b426c", + "0xe080daca48298bd36e07e90b917d0816287a0ef8efd2eefb968e727c5a9bd08a", + "0x6fde0ea6fa2e79897d3eb5356889c44974a4fa99337bca23582bd39a55cd4f54", + "0xea14283add10c3f90818815078ce0a093a09836110d4735a02bb8076d4facace", + "0x682e3ce4ff9cf4dc99d63579b600b7653e2c042f77f5e82290e8e727231f64b3", + "0x7aa4850aad6ccf5547a30fe57b863c75d8359e7468415ecdcffb964b9df48ba6", + "0x680affa5b7e703cc3de9237ad3a327c21f12d0c50cca2897c2a093fbdd29bac1", + "0x8de3a25cb56424618d3cd96fc6b32aa9b23cfcdf785ff24a456df988d0003461", + "0x480d2b09c992afa4bd68e6daaced5960d0a5c22f9bf2976bce641618db31ecd0", + "0xe3c800c29b6604473249564e74452eaea6a575037f12daf3600624f66b93f163", + "0xadf9b03cd81610be3fbddb71f1807d56798ff07c3c37897a8770c9958295d7a5", + "0x50c3a44c32394bffaf86da6d6b65a900354f0e459b3bc60400240554387e330e", + "0x0f12dd3a9f57090286c928f04d5e6bf9f176a74af3c659e51153bda835d0b081", + "0x30ba9f15928815515900cd8b7627ac6badcefa428e5489072d8ae9d2a008f421", + "0x9c3a9601a2f8ff881da30c2de2a72c578241c247909d6b3c4bc40ebe36545892", + "0x59a5eb34f38c938a42146e834d96d93438e23fe18026338c3e8714066cfed703", + "0x26f58ed29f4e5c8bc350a7f9d7d17e6d1387e381b9c350f8bd4a252917e1e998", + "0xea65be808eb9b17c169702e969fe3759f9727e8243d8586950f2effb4269abcf", + "0xef3da1c3c5b47102aeff0ca3f7a0f0b2b106859c02225b72f43e8560bba7204b", + "0xe8dd37c1f5121fc9a6f95588abdf955dc5684c20c6ebde5d4f7e83a7fc27f6f0", + "0x593e65fbf7ba285406ddc1687375408626aced1e7ea503bcc61acbc6ab0337f1", + "0x98ff73bd2055a26491915c76946c4647f32c385da9599261f7a147e6c7d1ffc2", + "0xeebcb53cc69c755bef8ab8e68e766509199f9e22f3e77e221c3985b33ce923a3", + "0x9de0236dcdb8de5b509fd4f987b12360b3ff55e99210147b89bf08b046712f25", + "0x3a6b5a34a5a5f3255a11ac45a13c8470f430f2e28c1e7ba76a583b26caccda92", + "0x2517d179f00d1fdfca133d1abc54bb1644b2f41e66579b317ce6f9cbde26a982", + "0x9ee93155c3facc1d6f0e826cc40f753493fe0bf80947ccf4a5712351fc3a0b00", + "0x409b210d74f0e4c52e3d0045d1a1a2208b0509f0bf19451e8f73d301c5d6960d", + "0x90d577278aa33b7a2601bb1f494aa9c39296c5b00b15021d1ef4b2673b812b6c", + "0xe01f5507393c74f6b0803ae539bf729cd7bfd2bc1a306d1b92b2777379d31e87", + "0xe86fdfdabd158e97deb65f74f28c3a98b3319f8a9579ee799db6ec152477154c", + "0x880bc8c6da4c81f27cbc3578b595b58699e76c61257dfd41c22767aaa740068c", + "0x49438a4376b27c9c9a2baad8e6ab15b4573a8c063e22efa4967b1740fd55dff1", + "0xac4b770b72e3f62bb63240fa8feaaa1a3805d8576f3b193245146404c7f1c7e5", + "0xedf97c5387047f1720d476abf3ee5e8a1ebbade4a7219d7b58880aebfde7e59b", + "0xb165d97640f86053b1680b8c05476fc5b082e434efaaf8d4949facff80726ae5", + "0x29c03881c063b4717627949e17ff4add2f661d5636451fd021ed41f84bb6d0df", + "0x745a37370b2fb7ae7a2671533723380038f91ec84ef3cb1f1e4cb578236c22f3", + "0x65393667cc70993d850986aca368f07f1810510f701b6cb1c326050fcf393a7b", + "0x2a37a490dbb908817339ea35a3e42f35b21c3e41dca66c8a894c1301b015b406", + "0x474ade170e764d9aceea806bb97f82924ee3eadccbb2c8bfb8caa829dfd1c566", + "0x832668cb2421b21d39cfa0b44d96b0d1ef5bfa915f06566b9c4913a822f58de7", + "0x8fbde5810297ace600b0d171cf5adc853a9098d84fe038ac551ea78958ef6876", + "0x731b8b6d4de84c348c5bd5d0fe150934aae70c6b166b30808948214871ac8fc5", + "0x9eda63a1dc35eda059592005b0883bee0fc8e7bf6a8bd3de5d0bcf058b624f73", + "0x2aeb8b48363e9b30e24c69c39d0c63e8de177e12e9b0740cb6ef54022b00a011", + "0x4e3b0ad60f5a6c5f4544cc9946227080c8cece9650a12ecc85f02be31216bfab", + "0x3ad2fa0761cb22060de01cb62a8034a3a92f2746ade88f9bc67aeb53e6a67fb6", + "0x44449d96ad2492259b18d900bf9be684ad8ab6de5622dd43bd6fb5e50e37f6e5", + "0x6be0c94490df672a254fd49097b6f87fc7f38271cb0a377c85d17ca3059238dd", + "0xbb3f9fae92514989ba3271beceb3d60006bd875e1a6f2e37c5504704265c385c", + "0x2a2c5020860ba6289ceb4294f87759157639170b931b517a08b8cec4d89e3233", + "0x265ae2b690f13f86dc3eee51ecda1a0a726b0ddff688fab990d31651ea144df4", + "0xdb3182537247288b8517070db9b36126c0e41ca2b99045e4f503ffca08822c19", + "0xb3055105db645de7fc4d5e1b7baba30bc051f5afdf3deb0e7fe12f8e4ed77628", + "0x7b24aff237f87e8b283c0441204fdb5978b4931aee9777be34cf7e65e2570d94", + "0xb3a0caf786343a58a7022996053be34c9f023f79781c0d529d7e3fb28ec75674", + "0x95757275b3172a8a79709d72692ae7768287af4f263adf6ede65ae1e28461d1d", + "0xff0782b8657f0f9d185f641f5071704fb6993fe86e339bd3dbd09142f6fe66bb", + "0xf0e83c92d38ddc22f1abc13b9b97ff502c19a24e82afb111ed0ff7daf94a1c62", + "0xb231a161d32c0ea494c72d1a377ffaa02a9c1b549269f3f00450ee4efbfc793e", + "0x81d2cc0c9ecb5f21da0437ccac1cac0025c4e82f593ecc33267772a5bab47ced", + "0x169f3977e89b368b9f5c140576347b748ea33e725d8c0c7c04493aae4e78369d", + "0xde5bedb83073d3af1fa7b89c0db9d8a91a499db5cc486435043ec52d0ebb8cd2", + "0x2c3ab0afc4c2cd7868c7c1523491f48bc47a21107a270a8f3d51cf9b8b2a7f0a", + "0x158e64da550ea42dfebf9b43172e03b928edd7bc9444eb631a75565fa162af2e", + "0x7d4abd823616ae90a57c5a2e05ad6feb23b6a46777bb8de96c835d101fc46fb9", + "0x6b028d682f5cba312d44d36c038c32296472f44e5301898d9d1c58325cba41aa", + "0x219398b6da307d5bc2778717aea1743893b2d2029bee17322bc567e84254ad4c", + "0x50e0eadaf1457427fd3e698f482f38e6444f18804cabfb2e244645530b5641f4", + "0xb73ad766c0c2b372c6a5f4874a29a37305765b3200a1c2655c65de3fd13383cf", + "0x0749b5cf7efe95a6706bfb636590b159821025cca15af0bb16911cbfd82c2a82", + "0xfc8b46a5c9dbae674a69d42f00fbfa10c085a20d85f6ebfd66312a66d6b0f69d", + "0x6d46345af7769c4c4ca03e1dfa9427640f33864fb4bbdb9e8f7e0f8de97d9fe8", + "0xbfa7ff7fefb5f8ae61077ecbaca1a6cb1f8d8488e08036f7800d9baee68a09df", + "0x532eaa445bc67cd920243ebfe1409e296d6c6c057b955b7695438944fd302926", + "0xe8c5bd174eb63271e94c17ba1b1d229cb846d47633d5ab0134cab5c772a7b1f8", + "0x6b5a686fb4e8396b5ccc2e39b5a82fe35e0c55ec202a4285b0be984182d35b98", + "0x116f43ae82e474ec9566e81373fccae85e5d67ed09d61c9b55cd4dfc1bfa1a69", + "0xd2982d8d61178389af0e8342da10dd9f57287b322f7e2b37c49f593899044767", + "0x650e209c722d7e8f400d801eb16454c4490f31a1f131ddfb2e58116037b55738", + "0x7e27852d4b4bb37219dd248157518c66c6da3ce608598b7754a716c9775f8093", + "0x26b53ddb9ffd4412a3ae53457af519643c0f3a8c3c7062f91e15b67e5abe0a29", + "0x786f7cea846cb40327aee65634c52ff25a99fc34138198dfafd66baf4d6391f1", + "0x5e133321df43abd01d6e97baf83b45cd4a3212665db1975ab3484c9a0daf1ea8", + "0xb0a03445c55667bd356495635752865b81253fd5eae7c1237167e2062c58f624", + "0x790d3e9c6426fa48283d60405d377e3dc710d8b247e7b01f673b8ccf8e9ecd71", + "0x17f5ec280592027d88cdd56d2027615ee5c3a5f40b5cc7fd6bee24594bb7cbe4", + "0xfc4b26f0a582ea1034cca85f013fa3fa19cd7569755873bc52ab143da48f0df8", + "0x52df4d39d1fac8fd14b6dd9095ba6a07fbfdb7a23994a119ad75170ec6b69982", + "0x57ff3263900a834759816c3ef6b23b677a06b10ed9569feb55248ed97cc2cb13", + "0xb846971576b429f537f8ec6a88bee2e80d8271410fecce9e360a4cf373c2ddda", + "0x1ed8f867b8384800a779285f127abe48f0ca8e65a400e78a7ed53b2316fa29fb", + "0xebcefaaa5140a2a0fa18c5805e29da2ffc01509b59a9ccfcffc2b82657998793", + "0xdb9fda2bc077b6930bada2c82d05031eb7c12365fbf4c0fc78d980ac4a11fbae", + "0xad1b9419f8185f7c0e0fc302ff9ad440b92b17b645e8d3b4444ae4c2c60b048e", + "0x864f42c0c7227344c08995e03b59e6a68699bd7956532287366955cb7b1dbda3", + "0x19dc57a0befcb8cdefce19558734be5e09067fcdad06c97270c88c0051f555d9", + "0xd68ce85bb78c8b7055b3ff6dac1f00dc1b171b134dcdcb287fdc9f9ab8c35a7b", + "0x6a6ed8108944f92ba09248137f66b742d72245a4df50eca36e1acea3d2e0a168", + "0xf35915bbe3cf6604f062e1cfaaf82b1777316dc6c79036c10e3f6becb7a1342b", + "0x39beabc5368a99b506bfbd7eed77f330d85fc3ada9ec37f0c5109c4f99132b4f", + "0x3c0701ebc6d6cbd3610afc8da57c3d2a746634f2073d6dbf0a8d04059aad9aad", + "0x1cadf3718099972686d7797d0eefc4d7a4b7a1400af7582aeeb70614a38d1793", + "0xadcd1019575b5defa77efc87dd3d47f21462184363a48bd2452e05ae0bd9dfb2", + "0x48ee24a4e9c7ee04d0c8f134cf0380f0a4414f7d62ce3693e572791d7b84e54a", + "0xdac0608fc6c842650978c55a7bb668af6cfa43188714348c3e5881c22cbd57d1", + "0x45e8f1acbaf34eb1dfb30ed0ce7fc290c892fc882eec25fc2a0b0aee0f0de581", + "0x8e5333c773e330883105fa1adb6c300f2629fb5e2d070eaee2f0258163b64393", + "0x480f0c39713a21c382b0205c4d3f67e0c9fd9b79b473d89ad2a0e462fff2ddbd", + "0x704b9f920302b9814f300bc719c5431b04a5bfe6db92b05ed03fc95380ddb669", + "0x24b53cf90760f7793a06968b77e069a05faa747a0c39bfe52a21cf7256025e75", + "0xe8ce9617076911f098b03fc2bf9e4464d04a0d336a4fe4ace45998b782d2c724", + "0x6775b06d3be34dfd7b196d658806308490815f1d3ace65dd1abcf12ad87fd354", + "0x6004b34ec3f91f76e65439ef3f9366b271e1866ba421783d8842bf6d7edf6cdb", + "0x2c0c20e0f07eb48fabbc23c02a43ba1c416ba7aa89c3c1d9cdc5fb465b0a4c3f", + "0x7a6c9bb6de52b8ea9b9c8f48e7bf68e05ad4eb38b86f09fffafe042d95f155a6", + "0xbfd1eb65b98a61d77610588cb6f8768ccca21b550cdcec81089c9f739d78638c", + "0x969b5455b89b1253349ac79646c50637f1bdd5070762dcf74f1d284c7a7f8a71", + "0xdf3e0bf35cf160a932c09ce1f60c348f1176dda9e5ca8e08af4a43568f9c4c68", + "0x60fe8de17da987148778934addf3413917761c6652b281f178fd084b2f3a6a6e", + "0x3b8557d683f70dc02a6244a54aa58fcf113e3bae49796c0bffd47584ac65ee5e", + "0x8714050ad2c78d0601fcc8225d58513cae8aeb6dc538eff1bc96177be547ad21", + "0xa43573840c6a731c1c23855034bd58feea39f75b8ca6e96a492c1b91eb92e97d", + "0x187b94a5fa29a7420f599cb061f30cf331d4f9f3ed9df5a4d848c6d396323c58", + "0xcd5747f2bc9eb2447bc604ea655924946b3242458f778a1c2782195d5ab61e90", + "0x74af7b6a17751af8ff6f2ec3a63804a3670aa019b0d6e38ba830557a3492e019", + "0xf442cf5ebfc8a2f173ef2f46262a3e711b821ef38179483ce516393471fc43b7", + "0x9418fad1d59d3773f8c79edd00e58adcb0cfadcc0f66e31114c66039ac2a14da", + "0xad4ace3899584c1c21b630604edad8f78f0ca9f07bc21403c8648949f1c91579", + "0x618dcded9cf1090ceaf0ed722dafcfd8806d322d4d24cb8b5ec5677307580d28", + "0xc8ddb47a9ad7507ad703c75a8d91a1623aa671b8ca8b38410cb68bcff2a5a86c", + "0xef9dbb74c6f50c3d08b50dcfa5c415ebf977d984d0df56f7fa8e6bf0f00ac2df", + "0x217d9355d69e062fc5877c3b70ae65265695693a2fae9bbc72e3f9bc6557d588", + "0xa660453ddb0a14fa40846684397b1dbe28530cb58425d97396e9178113e5c0f8", + "0x536f6f8e5c9e631f25a16a3cf474cd9f64086d0d2e710221e677c00c38525e30", + "0xcd4c91cc9893820d0005e7af57421df4ad2d029e7ff66e170ee3402f27b35155", + "0xde35f6169aa1e485a2c00b4d3f61511e1544288cf89b7c859654c1281b10b069", + "0x6d19da28f5b15dee31477d9876d0527a5be5bee1635efc846dc87e84e4ef747d", + "0x6df6d39f6df470fcc0a834d433295d2e59321199df9d0a482487dd499536e2a1", + "0xf0a25020406b878fb9ac3605646ec945b42c8baa78828c3c9ebaade506a4a5c1", + "0xb16e31b093b3416e46f0635c43c1a543cc3c48c98f0171f2516317b982650a0a", + "0xd611e749ebe95c4db6a4f7b9dd692d9d118ab246370db0f634ff68cd4dc41ae1", + "0xe6b19fa55f66a9d7a6358ffa2e53c22b53287a7c0eb27e3a2e83ba01fb09d433", + "0xb51417d68ef3c1004513bca739da0cc1a1495b779da48e3a73ab769be8d386d6", + "0x78c4c8e246cdd082ef3b15a48128676455b717151386cb62fde4aca130a09468", + "0x7dc38e8c7fb470c656edec84a5eec8ed9c8ea1725ee5e363afed5aa43ab6cbfe", + "0x23bfaf1aea1e34308df124fa9561494a037080a64f4a62fc79c338a35b6b5bf8", + "0xe174c11a6af3de302efc7e240e8308197b4d81548439f75cba3c9630821750ba", + "0xe3223a09f5fbae0c7dc82a497ffec210433e310ecda47f85f0b074b45fa927be", + "0x30486f320d4b1c41ad724cb9f79d1088576f00490046f8462771c1168ef263c6", + "0x8f1bd6c8985d51550d2747e987cd4b62eb02033a6df7b73850a7fc2026ab9fc2", + "0x52befcb4204b278bc14fb935e1d052d75e79b4c4516ab21dd814155066b82bb1", + "0x337bd3c58360ac8db163b15530c08013e7d60d58c125cb5b06bc746b1071e912", + "0x7cd4997eca13f2d5ee5b1d82db7d24704122e6a66eddaf4b9ab02552a97bd887", + "0x0f76e66d0ed0c89445ca08118a05cb597ff05ffc7099d067c2ce23ecf3e0eda3", + "0x596781cf862ad4d269942c09526efddbfe6ebf5d799ad5183ddffcfbab989467", + "0x4441ddf4e0d774ec2520e90ff5548012c8c1219f5b118f99027cedea39f2e494", + "0xe10e9f719042e9150e34b41c33ebdb7f39241ca7e4bd6c2822acc3d9784ec049", + "0x3da325ebc7396f443f61b73001fef8db9286b264716626531c69eb1154006aef", + "0x17a67f256a35967f87a00fd1d1ee3b91576cd7026eabe12b3bb7c58e5abea8af", + "0xa95443992ab0892651f1999428c84384ec79469ecefa2182015cc0f1821b8b45", + "0x92c297f0f17a50f6cf082531b13c48932bacb63c7c59b34a7a9364c85353d00b", + "0xc068d86b10a44a6712af632df82cc83854e404e0479d618d88f9bf981e936648", + "0x4c72087cd1250b098d0f48aeeae110cf08e877c2a685f331d51d49eeb9d0d871", + "0xceab6bbc99ea72ad0920f365ae8891ff84679a4cf91192ca71dfa11cf484070d", + "0x80a57bb80bd2f39631ee1b0ff95de7b71bb1de8f5b4a04aefa1e052f3f023ff7", + "0xc7ed0d787c6e353ae0d57382eb4d67dc1a0695af87360b349ec6f11a5af948f8", + "0x2a47b92d8b1bca2bc53b1f4291db7cf28de0bdfc8c205cfa6dcc56d81ad4345a", + "0xee90fb4e42a329cc7ad657e83acb13c5bbf68e13b6b8a6e812a83127b274450c", + "0x6e50b214aab1a41b26d29acd98cb68c48db41230f8b3026d4baf961b15f4dc80", + "0x4dc26cca28a627ace7993be869f4276ad5f1787733f1f1fecc75251099ff062d", + "0x4cfd7a7fb5ee9e8a202b0e1c40b0f6d191ad44314bb4e398d5b4b891aad68076", + "0x8785963d71255554890a390d714ace502cdc5f6945310ac2d2e9dd0704b6220a", + "0xcc954c795e2bf08b8898b04af11cd46ffe842d68520d0c1e208c996aebba45c8", + "0x583c3c235e0c82cb2ea65185ccccebf3aa3b652260060a66b983ca27e2350f77", + "0x2fb90ad8a9d95944cfe59b7163c40f05cbc8d3d4cb0ab60bbfc388f0698c446b", + "0x0aecfcd469afe8edaf4a616f2fd3a62866579252416f0152c4bfc53a5804a833", + "0x06908e9134da88814461292410eb70ce42602200863a55d3b8c0112d2be9daec", + "0xf5edb6c994d955cdab31f50d861af6b8937b78d06cffccac83422835ec783cee", + "0x92bff6c53645a2b1da440dc35a9f56f28e522ef2faa3343a3c4b2ce24daa89ea", + "0xf2a776577a90eab8954bed4fa0ecf9b82f6e905229196b8996c20bc16bf75713", + "0x981186af6b1d5c4a73614f0c596f43a155cbc04cf94f0139fadb7de1155b4546", + "0x9c412729a51fedd5d7dd0fb28dfdfd84aba0362cf0ac30eb0403aa4691423508", + "0x944b866c65ae6b8d66b49f583ea8681be29f6df606a4c60ac10a519d2f54d65c", + "0x36541fd380aeb4808ca6b281b6c90cda034c61ea92211aa2b4d145029a79996a", + "0x7e99a4a6de48db2aaf1bdc4f10569b5f2745e174b4c957cd96ed984e1f122b03", + "0xfe7185e26b3ab9fc23e2b3df9cea2eb455dd2450c743da7cbeff4da3f0a9058c", + "0x7d6145d07d37e51d5ff60e3304bfb07e03fd67bb0feed8a8204d8ad49151507f", + "0x0ecb8bd33a1b5aab4d1062db1f753f2fb85d63b4e492273bd712af1635f5db41", + "0x0e9249e916d8ef94687c3f0e88dbbcf628002c333824af53b391a422e52838ad", + "0xd7fa7dc00da8dba45be2ad4477a0570bb279ea8f97103b90b34f1ee543e28d14", + "0xc2f19f16778a3c64d6fff67d4030cebf860c7b83f5f0af25eb6bb3dcfcb1d211", + "0x0aa56591fdfe9971eebe43ca93565c9f01cdb902c5007b5c3c893b92b99d0bc6", + "0x2f9b874d1be09d2de0e8e473f1fb4aaa87e66672b2554a16e2444f7ab1e848a6", + "0x0edc9dbf14b2cc3fd59ba2bcf0aa720a52a460f62ab0f526e5bfe1a9dbe091f5", + "0x75c45303b203e0385202844229b4df842664c89c31fb147a1ff101690e24e663", + "0x096e1608c1fe07a65fbba661c34e3319950ca89d53ddfcc099fdd1f359419f46", + "0xc2500b933b984da256414b6a988f639a8333aa03e4b80c264746853d22d7cc12", + "0xb32e83c407fee2a5e22d39412038b12ff77efdc039f543b5fec9a47811dd08d2", + "0x5592c209c53a59a3ae15bfe169e8b8f5d5527bce79e5685295e475674dd0c0fb", + "0x47d6398458c26cbbb982684266b3327f604512d3d8734e9d85dc89b2fcc8f34d", + "0x19ae55dfae7b685d678013fe1b843d33ad843e39dd6b925a08c95def78c83ac9", + "0x748bc9ffead356a1adc8202d46ade20a28050ae581165ffc1135d681e2af98b9", + "0x7ce72fe083609f2a614f6494d87bf77314b6123b9a41870579b464bc15ccfcba", + "0xfd1b87c64dac0c5d697d7b66424b718863559d2bfd8f8504e8dcad393df4b28a", + "0x801ba887f2bac3f6b40f2ebdbee740eb8acd9fc998ab135596c1517b4af8595d", + "0x524a570e0c6e98e134ba788c8ceca89f36ba408248690d592382a32e4f69bab8", + "0x7c292216bde6b8f42d0ec59e6713b18a624f22b3c261e7bc3cc82d806bfd8f98", + "0x579b1e63894cac37259cfb2a8f69e6510fc8632aeb1ba900e8c133f87967a5be", + "0x9ff585a682d76857d07ea11cf5efa8be0b977492f277bfb97afab8929d573d01", + "0x6719b8b2b1e4b28f3c65b564a940ac63b85285f9a008e15ea7afafaf25085994", + "0x6501f70b67da3a8a858cbcd1ec6e7300aacb6b8ded587d19c9e1a036f217c1fe", + "0xab1b7e2c59b6962fb2f380e557c70610c47e9e695eea8d663f525d4a8dcfffab", + "0x6456c616259cb0c65666f6e8d76cf8b0818ce88c2b03884069428dae9d7b36ae", + "0x0539863bfb01e78eba7669f55199e41514d23370412445eb7a21ad67f3ef73e7", + "0x84d9629f87f367ce17ed4b4ea15e501bc983b5da7361900bfbd241fb21280ee6", + "0xceb4180e7093402db7cd2965a7653db518c12de3f6ab2b2ce8c93ea0e492b33b", + "0xb74a79a0763b454e0569919dbaad891812c49774726250821446f51f56b7bf11", + "0x43845e19c575b4b5e92657119b56b03d4969d2d3d89a7730b623a435323b7c6c", + "0xb747976c6aaacda7f0d953701ce887fa0dba7ccca5a51e13db3d6d2af8c3e72a", + "0x2e1b76d611d3ec9cb18ba494947a1b9891e957f6e3eec14bc54e3050cb6bd2c1", + "0x61f2f4514133dc2988a550f0f79fe2a0a4e865c7a9b3e6b9ead0a3f8b3551082", + "0x118045eddff304cf036b31cfd5e5b1e1c1b823db778d98d712e42680147fb751", + "0x7855df6e547b3a5e70ea147642a1a36040f2438f74f90e9f24f779e036070141", + "0x3fb26d4b8cfc0d927e61438107beee1ee959b022b9513b786aaa158a0662e856", + "0x172d27616df66d0586e1d49221ed0aef91344fa3e294866dbe065a4e7535de57", + "0x4d71d7eb5b3e57880db7273136d1e93569d4537f499f6ba5e222b489249407d8", + "0x03540bdf9d88ff9ef618400c6564b7ea915fe48959cbadcdb63d4860c2490c3b", + "0xe5f575f97428bd908194499aabef563123d1e2025cb9367c786d2bbe429eda9a", + "0x348bb695b22c755e6debaa15379c71ee7170f29e54f870974b4e8387c300fba8", + "0x33de4ef543da8daf8519df13bad391da8279ef6362f5291291feb0946aecd8c5", + "0xf31ce3922654309f758183c715781f9e41acdacf3d6c6b80d37466e2270d8b46", + "0x4afe6babcd3ffd72e7a493ff4e73d8129520c173011303b1da23a3eb052ae19e", + "0x526d61bb9d6f7ec735c8c04e6e80dfda5d5a0253fdd942c04c87ae2d161e74ee", + "0xf33750ae4701e0fc3426a2a8effc3b2f8b6cf965409d3ad4f17018fcfa1fb47f", + "0xf4f4b88acf90ace3b782bf3c6ba7709b01375bba9f41014a3dc8e2a97698eb5c", + "0x504a06845f51c7372b83152ee8e18e8694a71457140f4bfef75286fb7fb0c718", + "0x14a471f6949f7511f2b8d29c2304b683ac61cf233d11ac05969931c83f81423d", + "0xc9e324fa559ecaf1b1acf158b9076e1ce1770c6eb7fada5a28b84c003e004be5", + "0xad5da6d138c3a60ea4cffc44ab1c61282b092b0c3f553789c7679e139f87290f", + "0xcb599b8c039f6d68c44d7a1ba6bdb055ad263d2d006679fda895a085981bb0c7", + "0xc2c79823554845d5387a61424c6145b6833405700748e6a71ef038175cf75768", + "0xd6984680f938da2b7f51662db0ca7597fd008c18c693aa8bbac8995ae1afeced", + "0xdb5777873f84a46d8253af21517caf231d22ca83fcd2aa84354a58294768cbf2", + "0x43436dd16396b38d28641fece7c5e08d1a9b408b24a819b7f2f82ed3152f3edf", + "0xd7b9fdf907b8afb5f8c9766f1728651e96465c0bab1af63ace5e7e3e32ce9343", + "0x672597342154d8199b3218a4a02328f1a656092bde9a8d859a9b2a6b564bcaab", + "0x338087d9f33d2954f53a8c92fdc5b51ee6339c5e3c3e06bdf93113ea397acf2d", + "0xbfa6b6e4731ad25b3a7fce21069cd8cf70e6457faea65b16340b7b9ab8e0d678", + "0x0d57d6b07d66083c7553b4347d79de5cfd09b0479f3fe3e54d40f9693f29a42f", + "0xbc713f0a43a8c30145a5545a36a5de5a338afd7fa186d9feb4acad42e1402c6d", + "0x7355888af2d64ffc349d14b4deced1219ac1ed45096a5884354ba4b02bf88248", + "0xe7fe0c1a90232815deb0cc8d24a5902106b29b7748060523423e3ce917af9697", + "0xf72f4b0a517ddb3248effb8c179530fbeee4f3a8d46e8d0c8a3696938b5e80b1", + "0xf3d37bfe15665a4f6edd09eda99917cdb524eb961dd147f72e7a3f8945819a65", + "0x4b2a4aa391e6bd11c3600e0d6b52e2cfacbe1ff53e10eb1162ac448c46ff24a7", + "0xea3168ec727215096bbbfe86067aa8c2979cb283852e8e25e6d3ac1076e6a7b0", + "0x1a577f14d6f97681b6f7e24f64a907e6f17108089ffde2d812efb858368ba508", + "0x81c254eaa410975c6947bc61720860bcb68f6ca4082188454f70de68411ff591", + "0x8440ef917e4261961eda8859fa8b1e73a64cef8ee1d57022de7a8a64696d7050", + "0xe096ae0eb49f2400c535754600a34be8e0543b62f01b6752f166afe23de04e7e", + "0x3f6a7b6c12bcd622ab8e41069e1d78d69e4f68ecc45f20ea10ac47b45782e642", + "0x1d96e588a1e1d2eea0724744621bbd7728592c35623038e0b81124629c9e96d5", + "0x6d0f1e543817e40a49f2fe005096ef9ed61ce1550fdabcc125822fff2896b4d0", + "0x6c042ce413e70245cbe7e874fb16e5a6224ec98957eeb9e7669c76c04482632c", + "0x0ed23b9582b552a26c93981d54af18bddea38a2ea1cd9913a72e42d8e66e79f9", + "0x944be2377865ae58178094051a166637ad39f422546190c78a33cf6c308f4915", + "0x93c060c9f85d7a352fa8748444ccb49e4acbc6db59a3df3f25a71d954ac84279", + "0x9a267bb06c9757de319ab308baf2ced1f0c6dc2c9f31cc6516786dfba7dd34de", + "0x6dcfc9f0c5bf15d751f0b9565351b5dbe403cd14c82cb6cd981729004a26c019", + "0x2df21c590896875b65dc9730101317ef7df431cff99eda59cf9c676d8bb30818", + "0x08549b8088cb205a2ed944f0ac24418d4f441410c9696581e5bab235f70cc3c5", + "0xb4ed7fe7627b054424dfa28b3035f54e1eff9182c40d35f37eb1448b7878e4a4", + "0xce1280487ce3af3a35855908dd9ccb3d4534aaa1315b5a93e34c3a61169bbd96", + "0xa908cf1ba590cfbd8e9790c16d92faeb7263b8eeee245f1f31bbca483660648b", + "0xd3e163740a2c6bc01aca2b7a1bb6841a70f7bbe7f45f47b79d46e53f24fd83fd", + "0xa9a36e948098838aa90397e113df7d09746fbdd0695515fccdfe7816216e2605", + "0xcd4e33b87dc835b5adc6d993a075f422d8bc301adfd8151c14f3828272bbfc5b", + "0xf1a51f16413cc253e55c19ce530ef8e4d75e211f6765f3b4fd48eedb58c8661e", + "0xd4e85325e7e1026fee3bb04e93873e373360d24fa96cf94753b9fb9c6065edff", + "0xf13b50a9aa1a60153842817a86f272a21b38b9e8072e068e5b0711992790b487", + "0x86f234fd58bd61c77d9a21e2ac497d9dfea24ce8609496b8a70d965a05767804", + "0xaee3f052f8ec4a73f099695749fcceced287c33ce6a32da32917fdc3e5633e13", + "0x53f66d047213c67c8554e7e9539d66af509b4b65fc538308c6cff72c6362b04a", + "0xc1e9dcaede3b883001c962d24330ad9ea02a9f134f991add6829f0abad0d11cc", + "0x19b37e2281daa42256a5aa97fbb00037ffacf6a995469dc1a1cf6364df4d42e3", + "0xace8976207f58a498fba5b91d543e27099548dfaccc554e3a2d315f66b0c821d", + "0x6a70c3dea132f1ca03ac7410e8f0da781f45d69b6c71d590e233676f307b7c16", + "0x42b988aba04bded3b463a087d837cbe7190adc9fcc2875c88d2847fccc3db8f9", + "0xbe37ea92c003b41fadf63dbc0c3caf640ffea4f40c7af6addb51e47f5d42e9b6", + "0xe048199855438e6996229487bf4176b2a366ded527b5f02e6ec5834932b0a8b1", + "0x4350ec34e36fc2f2f7c2b6f9fbb0a48567ae1a0d479dcd1663c78e9a84276823", + "0x199b5bdb370194805de42cba251792babf9ab4d1915094de520237921c87b64f", + "0x6637a09bca3b04d53664bccdb8ff032d656fe758639613419e81125193ced5a9", + "0x462a315f45bc5add51de2029f164b390f1f20db0ede616244bd237d1bf659985", + "0x8be3c6e3ad87d59d9fe7dd3ecc491204ab650ef9e220af52132dc05c3370a6bd", + "0x17cefca3dd79a38f5c3091dcc25f3550ef23d03cf234c54ee2768611f06ba47d", + "0x6840f6285c48d1914eea632700949c8df6898001177c9ace25f960e14bc3fe14", + "0x5f5887f886de276c343c9cc19041132f9a72f35880519ff0377692871c1f4e15", + "0xdcf4d820d0060fbdeb6e55d8447430f4f1d8efffe0031a0b49bbc02773b07c60", + "0x6ca69e20e2b68d4321ab8866511dfbc2c4f2f4cc2ac026c7f04b567b637619ae", + "0x5afe659777c8d6b3f35e96f10ef742fd2bab4e291bb5f34637893540fab858e0", + "0xaf1c81995a5e9e12648d970d65d77f69d0b909fcb276dc88dda7f2916101428c", + "0xa9873d04686456918cbb719e6645dc50fe3c70af70ced627111f43e4a81b75af", + "0xe1009d10a78b87beec0e6ab14014611c013b99a93bf5c287ff35983d055e38d3", + "0x8ef296c3f048c5ce5d88b3605e47a468689712dcea08d5449f9935536e1aa92e", + "0x7c5967a70342de6324e923f5758f9028065d6fcdf4e31bd66532aee4dd7feeb0", + "0x9e6fc7878e67df52e36890173574d1b22833bb039cda6fa2e11f4578a41d2e1f", + "0xf5bb0a525104c26b5b06b26f76a46160ddca7b538f057ca1baed826e872a3903", + "0x8cf14057f34c39662810b7a8c4f72c8b0ce3ce3c5da6b0cb325f42cce69721ae", + "0x39a15c1c3d90e91c94529b7b2ee9741399d68ae5813fd9cbf01d7ac77a4567a4", + "0x633d2a7c60e3972cd5169849ea9d4ca00fccd06fe7d55550f96e82af41530bf1", + "0xf22c9e2b0f6bb5918dd7f883a4b878b1d036bc541462caa84d411eae7e44ad89", + "0x1dbbd55a9a6e5fc96320079a74611a36bab3e067188b9ab5d83f0215aef36109", + "0xeef2c529ee6d493f3daaba8d5cd55634db3934595dba4b2e810cdc12ca2ef143", + "0x919143880511db75b57a91a401bdf884c309ddedcd5316dc36985ccf94e5d372", + "0x3318d2aa5ebb701ee2048f91d1fef7471b15ebe3c60eeeda789689d129367e04", + "0x49f2030503963c6ad18509f47474fc227e2cdca36d73439788648a56c9db6c5b", + "0x955ee48b04545c2bb65199592d91df5d7c7ee1592836406792d4ac03e64916c3", + "0xb73b34a9865afa9f638d12f7a608f32d49ded7645a3d0b7d7af93e56b7c1da87", + "0x0c201562c942ac20f4aa967088a6528834e2164d9c7f9b8ccba4341af1d69fe1", + "0xa0424a59bb07d66c5b68bc12c0b1a7c9fb6551c9d02ba5c7086cad848f940f32", + "0xfab54282ca6e3b7f4e224e0a5a7399c77d20043e2220acde8e23ed2cdb1dc729", + "0x4e9873a6f1548d60a2d8dcbeb3bd0159d1337116c327f9e8c0668a12144fe4ef", + "0x3ebeb4d3f46dffffea73e05f51246d8f12526b7b3b9b16532c0b6fd24adcb342", + "0x4c51141646f06628d325b6ad3724158b5cdee492f41cb9cee8b456cec826429d", + "0x48a2e220318d88862d10619eafa8a36360bdf3fbe2cf10cac4685552894f6f00", + "0xadd7549baa76a1e92062cd67f7201bda72efa3fab3feef57d1321ef16e45d116", + "0xdad4da649b62b22b7fa911729a3647b191703d1eb600b0ab1865bf05bc571e6b", + "0x43cd34997b392aa678b6baec26056b639e11f2bd4fa5ad95eec1597d6cf9832f", + "0x1cb60d03418fdd19856c21f2041ca2d6b6d7b9b5126648731d2eb10027ffa73a", + "0x13350ae76f8ae0f9143fe238402daf9fba7789517d1466d26c1df73b56ebd276", + "0xcd3fa57dc83afbccb0eac4834cf8b2e0cc0e6fb3c244aa76418c9280544adaea", + "0x13ecf64b602cb6aadbf5c820132247e5f3e922aab71cadee04e32a52235f8671", + "0xc12b6d995e52184779fc2942d94ab0ae89318bbf81adfbb401829a5a7f331e5a", + "0x112aaba1b0a33dead95f5ccc435c9a396dd7ece27c88fcd5b0324197309b29ec", + "0x44ee5a9b90d429d1fb0d1d0d2de0c3ff34fa514a2aa56d5ffe538ea6494a6315", + "0xf5740fe37aeaa7740e8927f2959dab122af94edc699d0c82898dc5e31b986d18", + "0xa9a4a4361a1138d60ff7021904d87e7c4efa2011a3a8649619329f2e70dfdaf5", + "0x83c9d2b72df022abccea0508cb84c210cc4731403d49f294cc62e0c6198b95e7", + "0x1a6888dff8d7d88e8a58775806d4fdbc2fa4587b573839e02ae25fd8ab4745cf", + "0x85fec67ee7e3f8734cf8b3f35a7df12db9170173db9d32b4052037d81ce5209a", + "0xb46605ea793843768581b5c60a820cacfaa84ec0ebb81985a1f5943b69884405", + "0x5ace5a01c7eb4e03b5d8a59ad5be2c2ee92a874bb2402d29703769d2f06ab2b8", + "0x4e7085803121b058a0cf6a57353205639a727ec14ecc9facbddd174fa7c66701", + "0xa380ee22fd0c833e3d9e9f58b51174c691a274d064281d987a6105ea1d55d3b2", + "0x82b41eeacd1e405b20c3bceaf9ced3810e3b371beacaefc81b83612493d2541c", + "0x55eb22516b24c873e2c3450fc3b53917c744bee4ce2d21cd9370e468a0e541d0", + "0x4b1638cafc3c3ceb36d70edbfb659ec009bad6b16fe95697d3635156c3cfe8ba", + "0x9659605a008117c16a81d1a6196ad6fac3cf045922975fdccc5d2f20caf192be", + "0xd7f4f93f49650fbb08dbfe41f6e6b833e03d28e23389111d6338b5815fd79a8a", + "0xa442990557df8bfbb3fb7499e864c683ebb1099dba2bc8c41007f9df5b41497a", + "0xeb27b10d08b07505a58bc274e15e547233e24756835f7b15f8c3ce3e36d96122", + "0x1615f3b979e3ab1a917853818ca394cb266a8a7cd886a8d1ec736aa9279dc43c", + "0xe5d658758351b16f5d60b7fd2d9971c449dc683ad1e6a533cd3be6b5e72b3f22", + "0x7c4cbb46b9721e4fd3c9f0bb92ea1bfc48b5835acdaa037d5aaea6530d8ceca3", + "0xfa34e98906c9c8494aa477cd4f9aea88b011a12ff9e9908a64a1e1c817d53f54", + "0x683a4b24ac7bf91e337d1301e94a0ceb5974fd26bb921fd85fc48ce507624cca", + "0x1e023ec86e29303c18b69fa90d170b2a7c4dbb1035f4011311e75fc4af4372a4", + "0x6661a1b88456abdefa487805bc3b085a93efa2de2cf0d6e36da48fe6ae98dc61", + "0x196b2b858fe5e55ce7ed08d85d869f0bc416f5ef70258c2f130ed6ea252de9c0", + "0xe7f4e7299f5def49f5f68bb354498265278089d46719422e98597761b2fff9e3", + "0x9fa1ab5e248ffe310d69a6ea0b8340cb22f2df1af8f83b6e16b246dbb3946dd0", + "0x89ff17a6fa347b8b5358e13540e7230e73089691355126973be8f5a8312868ad", + "0x462e29f59acfe7bb5bc76387686fe5049dcdc071f252eb10acf4776b5c90b91b", + "0xf2c859b9b39f734b043ec3c3f97ee845455aa815f740e836360b8f3c3135284d", + "0x170a1a3079430e8e153271a5dac5b97615d72bfcadd46e664189440fa137c9f8", + "0x8c37c0dc8a0439838c538d1af1b5cd0c36108a039114f21e314ed880956d35ae", + "0x16dd69478b71d159fdbdb77c42ea16873d43621c2c2f4695d5dd9aee23a94ff2", + "0x267dfe6197abbcda6b487e140bc55a0162034389214eb429dc0f6ba068d8114a", + "0x61d4bd8311129264099fa502a0d650ee75ca0df03791fdc107e1c7abac3d7455", + "0xd1a8b5941062961c4a05107a1530f3bf3b5b48a6292e6d1cb183c5b05d138e1b", + "0x7842a41cfdfa26fa7d93bd2bb0bc68a3d82e68c149a9a085f0520843133f111e", + "0x2eac1003b461219ec1e1e9620646142a42cd1083ed671d82f13410cb077cdb2d", + "0x41cf4f64bc45a1e9d3504f17dfda7517e93b03cd0e8026400b941087e161f037", + "0x28c3c8a2b72fecac11e8eaf4831ad908432b40d850dfe5a11dab3746916cde72", + "0x33c5cb85c98a380722dfff07bfa925b76493aa0cba9a3b15015d35df56939797", + "0x1ae501107b163d8a332cdf5fea42329b1f4c5ac93861985cc5b399af07454765", + "0x12f92991afa6861b9324cff764068b90f842caabed11b59cc8d2a83f9108665a", + "0xe848f504ba76378f27c550c871d131523fa54d477be25fba48d2ea548d12386c", + "0xb10ed488343586f73b5b169913667c615317760651ab77d7b55b1d4a3921aceb", + "0xcfa2fee42651b477b7f8e28c7930749b75fa781e33cdb8d6f02401155bbf46e2", + "0x64c7c172b565f90b01f23930c61988ffda8ca436b902a6a52735ea2527aa6dd3", + "0x8e1bc891fb4b4dd2315215e4a6384acece8d62119b88abe428b4c3d15ce29cf4", + "0x145d08e0091bf5d30ffdceee5e8344a4f998bf603575556b61d3be75bc7bd36e", + "0x17317e0f911dbc9ed0764b0a8e6eca130f2970bb3257d7cac50e6e2d162cc6de", + "0xf40de8dea9259c566658a2b3b6e93b017a97cb1c91fc38d186a622026c47e8ac", + "0x262acacaf7a3f8853b0a89ad61e42c66e41917e48b6490d5d93889143b63c149", + "0xc254e4d74abeff419231c64533427c6497916a8647358933fb7279c7071b4799", + "0xbfbccef64e2f8bd079bc9f71d8f15050e84cb51bf959f48a04aa863ffda3a05d", + "0x5cbff100038345e0b432b47c204dd237fc1ed42f0a7db1cf20d775cbb5f44e8e", + "0xee6e58288eff326247032c75ee89c3b61821b9dc7ada1b9b367a32334887a12a", + "0xc5ae49580e7863a0c99e6e8b9638b8f56bbfc0866fd54c417ceea82f50872883", + "0x33df9667c6b5309feda586e0842823fc1311928a0eef06d0e165bf4ce7820675", + "0x5ddb73d4dbc32532e26c061c02d9e3d702ffe16e02accd7cae7bb724520076c5", + "0x828a7c33e65a00399ac8375a28547d6b3bc0318e56d6445c4e5b1a5ef0c26c1c", + "0xe10e954065f46275cf805bc180060a99b2cd52dae6d6c078086dc2b00e215903", + "0x52cb9de0fde45d9e00fca41c5d0582713d6475211ed88348a35a7699a0c808a1", + "0xf130fbd25b855b09d0fa2fd399e352964dbb5abc2187047b3eac76ce5028ee30", + "0xa1786b3182142936486c00fcafe4048c14ffab3ed13f5369f6b08fe5e6eea28d", + "0x325e0c572c2fd315c187f743b7466a1ac41894e35b940f3f65b46095bc5b2c73", + "0xd8fb66b96864401a95069b09d2326dd2e63903f0fb1f20193b1764b806f6153a", + "0xb4f916bf394e7136e67b00794be40e13f290e48dda58e0a3eec78c43a64943a5", + "0x36526f2bccbb5979bd44972c0dc669eca92f1e8a5fb22de772c67bf348a06f0a", + "0x77ac66f9846d60a578c06688d0b54dd5da0f935c7fa6be22c00585db6aaf34ed", + "0x8b9b19648c746304152d72147d997c282a94705b07813ae858f4fe4b91c22299", + "0xd78989a797a96cce8285d3f494e83bfe928a9f8e847f776cd9a07e1884acdca0", + "0x89ebd1a12ec6c62948077dfdd304b57473138f77949c27c9016f313a3ecafab1", + "0xb22c86ead5f5a73496cbb7bd823dfa1805fc9075d63981e7644245c45d5666bf", + "0x632e7278a5e9829b5092e271795a004cd59c6d1aa1b07583109584e5da98e431", + "0xfaacfede911057ad195326ad121b94ffc604c259a5e3a1d10eba77856b4d9ea1", + "0x0f7d626bf4e93a8491e2b7227973dfefccf0c8ff181cd52684088f381de979b1", + "0x1c6388b2fe54589482ea09e77249ffe199ebf2892c2989b2679bd3a2c74c28f3", + "0x7bbf505ee91dd5f2a22d671c6ec4616234512d3cc707ca1ee4d97b6bf8c3f7f5", + "0x024bb457a535f20793853163451608bb4921bc44887cf0e716d15ad24d214220", + "0xf6c7d9d8b8d32fbe33128fef5b193294d0ad4cd15a2eeb9591c8ef92112524d7", + "0xf4d234fc7e3fd4bd212ff25cb669da17290330fc5774eb3a26e04c4df404a86f", + "0x662ea342713d2754b698ae8818bbf392e12ca060a896837713d559b35f89af64", + "0xc07a04d71b873e846e17cc6db17d8e0a462e74cf4220e4fd1fa8d047f88944c4", + "0x73912603ec14d3fb151b6535302eb6d741ac734dca89985e4d7b7de54fcd036b", + "0xdfeee57ab5844251fc904d6d0077a1f664c16008620f89e179eeb88d5f280c9b", + "0xc655763567cda2b8e077ee17f2024c9237c718946018389457b2122c22ca1b11", + "0x6d2b745f33a7a7cb4e9afb7198e4d4bd48532f903fc34fd088c84efc55ecf2f2", + "0xe3d9d94aadd22b87be5ee119740789870ac8d656a659d423a554ebf7f46f78f9", + "0x8b78d269e9e582044420d5e73f3702030b437248b659760d41d96a6f6450e1c7", + "0x4030b102e525e3e138aff7139636becbe9dacd45229cf5d9432f4bf0e79c88a9", + "0x0d0436084489d3798cd7e1dbb6b8891a6e8d6157457c6aa80b1b2f30f45eec90", + "0x1e2bc87fa040145f2226048732b5781929d7660de32455aa171a1b02d286c17a", + "0x4525b89b782861df0e9f6dc6655636a78548b54032f93cb907ee2a2b6c933676", + "0xb5153d38cf83408fa858b7a53c754e4f912ce64078f14ba5d05c2e3388ba1a25", + "0xa6f3eeee698d315d8324b9b8896d310938b6ed225251575bb2920c2c2c473853", + "0x59cf05d0e94a57afb278e95a556ae0954ee11130d5c4a346341c87c61ca28cc1", + "0x8e5628972c46fd514dc2b11dd7bcd3150fc109d02b8fdaaa9ef1d0449c67baf8", + "0x0c7fab20d80f12a10ae8c137683dd5d4a9b4672954769186f7576efd44f9116e", + "0xc5a588693606ef5bccc2733f6cf7d289d24046ad87d2ea1b05d0510d95fcb616", + "0x5d295c954602c6d13d7862059a7f2ea0f48786d5ff75f6975abfe5a60b12999e", + "0xcb19bda161f6546f2c2c62d9c0714bf58a34b794c7e9fa0466a3cd2ab83f2c7e", + "0xa9ab415f6b34ebcc3f383d6fd7416b4ca2fe6df7671b67707148feacdc0979aa", + "0x540b1fd56732661899ab387371ed181670f77d3db8080313ff59f62753a6eae4", + "0xc22bcd1b5202c8fb0e777055da4d812b0403fc04e8726f12e4c91f7095562642", + "0xca396dfcaf4a28d0b70b37e8caf280e27ca72405ab0e5aa2960648f0505e506c", + "0x3a65294c3ac67c835eb3c264617f85925ce16987e867aca9a8f9ac3ae04bb1e6", + "0xa1df6f2c53d07ec3c91f77c8917169ca833b23d4d8e55262ca9dc8ca89487d98", + "0x9cc3b47dcb7eb20d17aa34221eca0382eb29a7ea0e7bbc1f82c150666b792bfe", + "0x3e198081338df22e4cb154df2006e04ef55496c47fde0498633aeae053359f7e", + "0xedd13543ea7938c7ef0fef029b524a015e7f617ec1dcf7be15ecfcd9a555fe01", + "0xb12e32d252b1f65e486289079d10edabb60a8a22fe79a0b69bc911a40de5265c", + "0xf66209fcc7bcb71ae3871dcd431a8654d8e614a0f2091fbd2fce5ca5d6b44f16", + "0x769c8f52c37166e32a34a18ae715087cf59d504af90d2b13a064d1f77ae99ce5", + "0xdc2e7759809e5fe7f06ed886b4646942a4bfb0e1c9dd1d4efbf3fe4772434567", + "0x7fd330c8fb93d52a4d56c5c3c371eb4fa89ed75932f4b30472a6952d72b63371", + "0xe1c3d1c41c3e6c3a584922ca46e23e8fb02469f5e08e41f22edc2b410f52e1e5", + "0xeae2c615cdfa94a8a686d4eb32d9936bd79b8e4f8682f21964811583714f7813", + "0xe6110c0f18c746b5063ec960ab2e5e934c0e5a6f65d60cfbcd81036960e53e67", + "0x0e714e6f8c5e95d3339b0b6d097e11239cdfff6ceae6845ca1273fb74d269a23", + "0xb34647a4002df79e7eb038968a57fbc64610e3cd37466291163b9080df965b76", + "0xac47adfdb37c227eb41ff075f9a31e12e2bfde1a60822eb6be362e7a2b452949", + "0x44e8f24722294eecb1bf92899c25ce34edd1c93ebb137a8010db0b9b57d4eaf5", + "0x229a018d337703f0fc36a8b1157aa2894ad3f7efde05a980dca9caac8d622a8d", + "0xfd4ee709436426616775ef4b28c941a09225742c7c5b4dabbb7504e6e171785e", + "0x320cf0a9557f7fdb2956416198ecb0ec163da6b042e26f08f174d38cd94078bb", + "0x4986d4d94aa1e6d93de0245539a310758a3194ec50d2945c04f0ed2fbc358722", + "0x61e2544646667abca3dc95f34276d1bb4b9c514258dfaff0847c03c62dc01182", + "0x74d2b76707032d239cc91cf7e30312ca2f90d80319467f0303c736249deaabd1", + "0x8387305622ea9b3f108ed826b72e1e4ad29480a3eb5e811b6938d69ed8cfc628", + "0x5d286426a6422cc575851a89f7bfa4e05d5d106da654943dfaa8eb5793b7b924", + "0xd5c69d59a53d3bfc40e198901978602b276e9aaf58c5aa0827df6a7cdd98f952", + "0xdcbe03a2955d2c57297d4524b5e925d4b085ba7927abc93195a761ae1c704f0a", + "0x3d417139c266b315bd3784524199a8e44850b02e150afdca1bf9bafae88cee73", + "0xa9c8657dfcea5047ac89c057fc34284cbb6ff5e9b8e28835d494bd675927eed3", + "0x81ec3e9146c3e73815d07e58bb57ce27cc74f54d851c1f4be905e08d5cbfc5c7", + "0x17cb03e903a09823e68a61dba99a41d375ae60e0a5f18cdc807846464b5de0d5", + "0x4618caefe22fc0ca61b1b9b6607a10b31f318059038399f28dc3af24c0aef619", + "0x192190f07291265891d978fb608ebf35b5daa66642ae898bf1b250c198f2dcb3", + "0x309fab024e01673f019b294bc4a6f0e6df0ae3c36168cba1de22b6b8374ebafd", + "0x5ecca9710b57d2f95c50d64735a70bc7170a9cbc9a51e5df10eef2128874a2a7", + "0x392283a8805cb1bc0d56bc3abfe75e2e787e9aacc98407f2f36662c3e1fe55e8", + "0x211dcd286b59d2692ce6da9cbfc8b097ef776363f86077b0fb5dca29528b9255", + "0xc1db9afb43957d1f06febc5206c8cdc5fc0a4e34c4f04663770bb4bf635250fa", + "0x519b17300e6d0eb39dd04145ac6946cde6fa3a0fc74d898501caf0fd278e5003", + "0xbd4371581e81a672201a890bcdd5651574ba4fe97cba95af40ee160083a76135", + "0x2dc865ddf53e10555320fe5b8bef985c367c93a39ac9dc035cd2c5cd9d29ab39", + "0x187b2dc5ed5c47e6f2381417a3147b0e02be69b0d7a52a81d4e7ea7a553245bf", + "0x6146de7f695c077949132b02f451969b4536038de4ab980c379e0d1527a8954c", + "0x0f3ed191564202ac80e622797769d60067da4457cb542bf1d578450c564e05b6", + "0xc86ea713988f16ec547a9265537e671b2cf8ce0620351cbd569e5079a75d5c02", + "0x31daf9d622e9f3b2c24e2e3afa8a2495c2367610aac14e1f2394e10cc6be1486", + "0xab663aac4396a38a3c2e5b9d8d17628dcc2fe9aa1caa5daa593ddb938c98504b", + "0x80727b7ed6e02b833850acb38e3f04791b5a3ee3c45f7a75d4be5d23038c3fde", + "0xc0dceb17a9dd9c99345d71a4d9d480c0d8f1197cffc343b97fbfb8e1791029e8", + "0x720240fe2341cd1cc74773aa26bb5a3069771a30b6459f9ace86e689b2c2a29c", + "0xbfcfe0e179636a46bff825fb507b307560df52a6762e07d3b6633cc06dc920cd", + "0xff040eaa4dcda7ae6f9d3d2c0a2819431535fbf1d92e63fa5819b5885ac30880", + "0x091e141f11d14239e2dc35726f1661ffdb15356094a02ce503b4ce97824c7893", + "0x944293e63fc07316f12024ba7fbfc974b6272b84b5918098f9b74f9fc50a71be", + "0x76aa54674a52517d35c5e38fd5a45924fc3929ff0b73d452ec499d8b2a448a7c", + "0xe013144a00c0d960e9779e6438207f33a70bc119994efd9ffd4904308257005f", + "0xc6b7988b8943ff9b75c92a09b14b138c87002d78b0c4434ec096f46d59479f4e", + "0x0122d888cda2abb68a8ae84c9bf8364defb040367f580448e0f711a0b6287c7d", + "0x686fca74486b6cdb931bd1be4c341790992f881af2d79c07e79b0db960023cfe", + "0xb5a5e270efc1b6544026b2a5821e54ac56082d586c171e7b3c5ecf100162b143", + "0xe8b891d2de2a655afbd65a3d4b7b95b51073c66e5a110439d8517be9305459ee", + "0x36d521095e510994dd2caf4503d8c8649895e10dbd3c7c3dd9021c1dd9a98deb", + "0x478bdb03b628df8e729ce68443dfe6664ee9ea566d6df333a2a60989f8c570c5", + "0x004fb68ffd3570246572ddd43ca0589e61e78148bc5a3907b826f25a92446bd1", + "0x70751487e451cbcadb87522f127c9be252ef4b75d28ff205eba5771273a4377e", + "0x91176fb5dc1e00bb57ffa74a7ee709cf4a79199d303a23b20a8a6ae1093a0ef4", + "0x34ff61e8cfb41d0092478a2ed6cc2dec728f106fb39b301e8ee702e0097950db", + "0x64bb7e2b949ec0c106fd7be90c8f57824a96fac9814dcdd1b7a4d72cf92a6f71", + "0xd080c5dfe9d3b991e48e853d6987a63b438ea9c06f5edc9bde4b1055db82918c", + "0xb3f8884fdcab64429c6f67c6b2a5c55fe50d59476d8a2fe626b8a64607e5113b", + "0xd4369153f120fbde55c794978966c19a242a0c056abe53810fc6943fc603368e", + "0xe202bba10ddd25fc0c95f0bbd72bd315948964eb0de87e2f7ca95910bdb6d359", + "0x2a2154161364fa870bb6feab7c657fcb19b4396fbcfcc5b114b796ffad1d45e6", + "0xf8f345f47b407b2b286d17b98c50223405556c9b953e19fc7573aef045821c50", + "0x5c9d210236db479713d24b4c21f482b3096d93c20f95665574f828c203e79bec", + "0x05eb33f257d982eb207f1a170eb336118836b541a8c02d717c0b0e97ad3a4571", + "0x5fadd149de6e183bf57a08f03bbb6f237fb37b0032667ed542c9f33d17af9ffe", + "0x71a96e1afc9f78515e83120350036ad36e6066e1600a885a9c366fe4597e5aff", + "0x012d5759cb1a4ac8893507668ed003961e7eb9d2154c1a00d6dfe8c67b4fa301", + "0x55cc34fa794b020d6b436fe382a95df04b2841ab5827d2e7d89ff54d26021649", + "0x0a38cfd5edd8aced43bad2568aab77ecb32a41e48be467a613210d1a7339ade2", + "0x8291bcb876f9b2c9b18738c371dbd5c44af768738a677a2777ce170a3189e1b9", + "0x71b32a2b42d899ce7e72876d738010c9ad5d87123c1250cbfb77b50bb5526a83", + "0x35b9b2f2ae0001f508a01595cd5e267a446663b491de20facca75e6d8c5fd514", + "0x8a624ae0918d7ab542a0a947851830ae570e4d7406ac79d0fead726a1cdb1768", + "0x4fd40eeaf80ff542993778cd4c766f89c71582b53c40d2039035270fa46a8490", + "0x3d9c1bbd60c092d14185684201f401aaa2f5d5ca3ab63be77c8782b4f74ae021", + "0x48d86c20684cdffc2d1cea90af86409e48ca63b761df38b9d133298f1aa91174", + "0x755834f5c9e304b850f5e1abe3e85f7480f2a34c78350d81476e893bbd8cd91e", + "0x75ee382caf9dc11648834df60ea416c85de090cda4c637e50b306f80c6a89de9", + "0x8bf07a61b9b679a704375c4bdeba767cb18192fa2eebaa4127b68256c7e3da79", + "0x852f0b96900b0fc258482746ac158a5d8cc628f18b7716d02f72023dea55694e", + "0xbb74d9a16ededbb8f31dfb31dc6270eb966d6490234b5f927849e044b9363711", + "0x1e7002e5397f276a3ed738ac1f6c8a268fa9d87a566fd4abf29ddca85fe085e5", + "0x2e2f71f93bd30143305435549cecfdad59c77a89b9a883c8583496afe1b004bd", + "0xc0cbaabf6ce2ef7830ebffc8d9bca79cca2a0e958ae345439d0753fa5bca33e3", + "0xf6d2b9038b5b46a363d4358bb55277ed279b30b7d4b9aaf5c7cc67a0c40f878a", + "0xb9d1dd57ec0074a14db63542de5dc61691352e095ec524f7efba3a81eb4938f9", + "0xbe9bbcf5834936c8406fc744bb39fab64cf62cd8676c135f702b8864c1b02c59", + "0xe0a7b97eafc6677d1a4192e91a8c55299fdcd5ed713138e0c7fb3745cc4c50b5", + "0xbd82f96e98750c2fd4de52bb6d38eaef46b2fc3991afd46759524631ffde184b", + "0xf7be4d26c8e4a0317eab311d2cb13617f09efa9371f075e50440bc852fc22b06", + "0x8443dceff5a298cece24257059c90224739d4524be9028261e7c4386175ffbf0", + "0x72a614de62e921d836ca9f82f23ae2885f7992346fa6d806d629704b2eb9e8ce", + "0x5d67b2cb32f8128d42dc3f0c86aa70f7526dbf371b63f4dd5b90b9b7d7b953cc", + "0x3934854ad43f460a85ca66e62725ac9537c6b5fc4be363325d176c10a2de9365", + "0x8f8a6770df513365d278af59e8fb0e6b5817e38f99ba1715e52643b5ed644431", + "0x022c1239b425cd3923b8b34b723cc489cab4eaa0bb056e08dfa2f679febfef36", + "0xd87dba01263d6c6f0db3dfcba376c4dabf434f88aca0104a48e08fed7356ac84", + "0x1e77187aaba44f1b0ff290007793ee3be4484f59e2487589866548ad8f7e02f4", + "0x083364d0898b9d9d6b8da3b4bc60cf57dc69ed2cdf94092d0ec4c81e587b0b35", + "0x053e1cca34b46b2e9adc68bceb4ee2d65d0762377a16d23fce1eed3abddcb5e6", + "0x18db9c6c3f3b866eb7fc3704d0249415c49f9890e4b4f5ceb4aa9329d2adaefb", + "0xd36f60ef35bbac65a748920449b2ad9d19f320f35e653265932acaf2463925e9", + "0xc6aec36ac900dbd1cddf964f21bf6c344240b0304e99410cf0973dafd4db16c7", + "0xb47cf01dc23d96d5a8d62048fa6209049a5862ab0954f890393837f817cad02b", + "0x28cf43df66658d2cbf12c606041c2806a4bd23d907e21e3bd4119f67503df53a", + "0x52317be7d59319eb0cb45080ab474a33d0f33671247fc989bc2b8261095a2ff8", + "0x42fa0d89b0736a182e74145562e709c7607405c651a018b8279af89c63d6d8d4", + "0x1baef499277d2d4844011c668ada58dd4e7e7caec434fb8763490b6bd0eee8d2", + "0x99d97f64102e6dcd7c1f19b7930e54ee98c0a99980dc87db547affd4aad101bd", + "0x3a70fc173a323e7a4dc0d96ac834cf0cebd3cd8e822131bce295fd9c18b02449", + "0x59f4b95c4de68666a2b12a68c5eb0c92db8cd85d5fa813fda06f5204657d6a3c", + "0x3568c0eb98eaf038290d68d1b9419e1d2276a2efbc49d7d148b05da8c1868645", + "0x7cb0affda39edfb990b7703b51a954cd2b971929e34862c3e62359008c6d651f", + "0x39f56a3664856cd6eb33173b3802a3e265cc66c09c70f6cbcafd0b2f35a09e92", + "0x9fc33c198a697b74de869e77b0c3e92ca0be3a1cbbb3ddffaf04c997db8290c4", + "0x7683f8eb504a00f1ffc03d484f402de488ebbc2109eda5e1ed9becf411d05b14", + "0x5aa0b90a76ba9765ee090205f004cfef75a04309c7a09c83abad517f3a4655c9", + "0xf187ecd885e15d8ee05288b22d728aec71a3d4de1b2741a0415dc3ab57a7bae4", + "0x3f37c143181d4876e090e9323ae95535dcde79298f5de8380dd7201298a4c521", + "0xf11c930640517067fb84403aea813ec6017a2adac09377001ba30d7e18539558", + "0xf4f3ebacbfaad7a0a8c3edff950284c6494b17839bc53c391ab4c9d6ab72a48f", + "0x59a2872e425ad46ba09ef45eaab9a424bf4c8d2af0a38bae21da598dbf0b4e3f", + "0x100ee2407054d51988517e0505e945a2b8ffcdb923173dcb26227e7c33e7f17f", + "0x566c481b1c64273401e6dfd21388de5b560bc74d067959002c19f776b6a8fc36", + "0xeafa554cd764f3e85ea316f64f0625604aef1d3ba5e4fc7fd8d2e3e0225f2094", + "0x17c447aefc454ac8647f6c614c0fe8cb2d0b973e5d8e9abd986cda1862adca10", + "0xc6ebfd1a43a32b1eb5b4aa52cf8a974d26d0b11dc86fa2fa053757f4cc7ddccd", + "0x21bded7ab874353d67ffbc34716d1709b5d71b61f384e50021d1ee2e64067f8a", + "0x3e1cd21573dfcc38155ae20d58585ffb2adf60d501b3315a09589c48bec44e1e", + "0xd3d73abc66880d5defb7455efed421342de6fd44248392e0c1664256c1de7e05", + "0x6f69f45fcfaf0946afea2633fdc77b378f0493b29ef0e62683256dd67ec7261e", + "0x185f3ea972971eb70528389da0fce13532bd8b0c3c9ae7fd1c9652c68e7cc548", + "0xdae99d3008cb2da52d48a9d7b45019e7f8739d78b3d2ae60ef162771a228c79d", + "0xed7173a4e0b45614d4b17819bc8f51cb252625734e0990fe7593f6001c9c36da", + "0xf7f8fdb9104a009d0acdcfc74caebb88d6b39f8b7e3a81301bff9cc2870ff352", + "0xbb63e7f85e65460b5c6d26a3a0c0c840f9b2d8b7496eee5dacc7c6a26cff67ec", + "0x2549ea8e9651e019401649b1234309b85e46d8744677840883916163e39861eb", + "0x149357557fd2de7ec5fc54dccd28d8539c803551581af469ec15070aa4d54e49", + "0x2aa265c759a7e572ae5af6d4d76d0fdfd3e9a52445b4f805eb8512baa905ec31", + "0x5ca750378b301c2150f9557647a706ce93ece8aeb75b7c61549bb1ab4420c5a3", + "0xe744ddcdb362901dfb37134670ae1e477a203405ac981ff1999b3888a36cdb7c", + "0xe46f52bbabbf33c57cf108111c89912845bc7ce34e91288b96e5b596fd284881", + "0x0d6dbc0ad644ee7a4f9270e8b536ddd1d3c71a75855c89d2c27c4398794551dd", + "0xb3cc983723f0a5de85ed4043c8a1855d2c8fbe0f82226162ff8c0d9741c4f0b0", + "0xe321acd1eeedb0ad2bea185b2409ce2f5205734213af63ad51b727b54a21754c", + "0xc0c88368dc22cb1c43cfe6582a479b4b7cd05f1e06363da019bfea252dfdd765", + "0x481988314a470249ea32aef837886a5928106a60f5c96754cae38537e335d69a", + "0xf9234495d45f530d587722f83f9e6cbb37bc41210e3ce3cbb29fb903dfc0feb3", + "0xcd994ead384d5c2920b42983ae7b5c30820c11a2374fd40834251e13702cc3d0", + "0x57c30ac095f028baf03f5e805c8da62be85a428e2bf21539031552c0ebe5557b", + "0xdeaa6ae25a8dc0acdc6563f17f14603bd0e605f8b6ab797f59435086e1b8b89f", + "0xe9daad935d537946b7bb83b07b9b351f444c0a41965b94da2e71cc5d6249df0a", + "0x588751d1369b7f37d2a08d466051cc9e61674cc80751c3c68ee26715b997ad9c", + "0x5059d7f917f028a4af4334ad50e97eb5c184cc6cd488786db7c88155471c7306", + "0x16586f528c36263a2ce802f34c2ad0419bae0915ed30d4ec704f0b7a29b5b2b1", + "0x367a7466fccd60696560497401b995d9d12204db3e07f434b39c4d689229f60f", + "0x489a52a9ec5f068baeb7919a64eb61ca4c9f0048c0caccb025c1ee15695457b9", + "0x4f954ba2ee5f2fb04b1fee19109857dc395aedc72423752d2e785701c991617b", + "0x73a9916c28c949d10d32a8179b0eea517bd5a3c826b10128e1eb42cffce6a1cf", + "0x9d32d72e065adfceec8b659495af084b52732d1ae375634fe0f5f7d83f687498", + "0x7c0460325e39fbc244df55081ba073ebd13290d86ccb782a1d3ab748fc2c4a68", + "0x0d231f5eaf04ed04202e357decdb6547dcbc6583ab029dd6249b2d50053915d1", + "0xe978d18fac7a2c19b511aadf8d0a1f604301329e2d63d0ad7d8d2fbcbd1ee97c", + "0x5ea1e504213b2d5957612ddefda3940448a13305ac8e1c221e96391a3cd14514", + "0x64dde3f23d152b4234721680c9f1250066c915f6b36bb76dd9618bd8897f16ba", + "0xea99cc234818f8cd2f918fdc48daf22fcd356600bae10b90650c2f5995b72d77", + "0x340d801270c3522294654b4429930c3987c820b5812aa751e03d80f5dd456ede", + "0x9908beffa309cd1f4d4129a279bab997291d98391e3b8ec8d14b70d70a726121", + "0xbbd71b8e7aafa0eefb0dabc77e5fb051b47c7c3ea0c5beb23faaa9ff67739493", + "0xdf3ae719ab9e81525bcc2aa580a2367698657fc5bbecf05d084f52dabda9546d", + "0x6f6b325e77cf40727fbea2b74acd3a8a99612b3479da26f56e56841abdcaea52", + "0x721be490762befca9cc2e1d6b2196dd0fb426c89b6805be613abbea04a2d6e28", + "0x9ae9e2b00594d34ffd711d21106cbeeb8266172e58255e215f75196586ef93ec", + "0x47b8602e12de87a7087b046e26b6e7d63cf608c3f83c8c37d900a02f5ad398c0", + "0x9799974bd2d9438ae91ce6554d61f22802f7a37c5bb52fba84b0f7346cd20157", + "0x0af5926bf7761021eb3703ad00c5b1cc3fed42e0d24804374956f53359d9c5d2", + "0xf0acb62d7545daece6753b89b0bb7b683cfc3c073ebd3425d8b7295a8c37f36f", + "0xccf637fc7ab60c681f3b9d34243626e4332929a6b20a6463d578fc3958fd99e3", + "0xee55961255c8bb9fd6cec0cd82e8f995fe27b53fd4f97e7d6c631167c68c2bde", + "0x80cb62a54267bd2314cc12a0f469a0b9766d7c1ec3556b0a6add2e202bd42ff0", + "0xd2c1fe5b88c749e7f0a5b2ebb5e95a166040e031d782ce0b629b30a185f4cbfa", + "0xb9bf44b43622fa1dbd210a7058b36b248ae8401d80b68109015bb7284e7721bb", + "0xb1cf1a3f8ab2c14d3bedef8d1cbc075ed2d4ec0d9246024b16a4e6731073a9a4", + "0xbfd99bcd199fe90b7cdbcef04e0966a9b1128692a3052d81c73734cc0bea42f5", + "0x69fc4350a05dfe0759eb75ee735133aeaf6e1332c518a6224a734100e2043e75", + "0x75149b06a2eb36ab08b2fc2d192a4df2e3c8240f07189627f68ef1664e888109", + "0xd228b82746f62ed501ffc4be979a8be19c6d0b0dbb4fd75d3b1234d100d2f258", + "0xcaf4253afc00b8ab9358d48356ccf2f6dde1c665d839f3a45ca9357c894ffb23", + "0xc72564b8eefee4e901d592c98210948d8cdfb890bdeae31b75598ad5520a8c4f", + "0x3afb305564e15d18596033f98ea5a858dd4fa97a9d8a09aeff9d3b5ac36335a8", + "0x896e53873e3ba0f553f56be5ffdfe431fce8acf3824a8eafc8cabb059d87e384", + "0x8d261e97a355fd2bcb51c86189946f1b32e3d5e1874ed5292e709739e4001671", + "0x9564f6db4c392dcb8edfd435aed7a5898e371e5ca68129054dee09b237a9f7f6", + "0x2eca9850ed839e4c22d0cc2fb29eed6f501bab9981cd0667169cda437b6a7fcd", + "0xabac49c7d5a8cc3504260630e20f0fae1cde818d6dc5165d0c1768e666b7fd91", + "0x8b5d9d13f8724f8a03b458416ac36b2a5dd117ea64bb2bb001744e5101d6080f", + "0x1b2ce544ee26f67bc93e339d99a9e8d91c232a67db3d60d6b978b9e51086762c", + "0xafc99f43cc5d4b0bc0fb9769fef6f666881b071fb233c682d21c24c7b20bdd65", + "0xf041f5ca92f5af8fe397d1442e7adee7c5959b10eb73981560790bb93186a999", + "0xead85055d60edffb4cc09061bc98aeedf7e297b24329ba637844e2de4a5fcd77", + "0xd5f7b2282e2b3848b1012fb2c1aea0d378689db287d1a03a78e59a641e382174", + "0xcca1a6c38e7bb146b78fd6ef4d5db89da3e7a6e196f25e9eaf3c7d9b118ad001", + "0xe2556411cd06ef9211a4432c888c42cd69c07718a4e43b441cb3f7defc7095c8", + "0x7f36aa815c48397aa2c99d603963543a30cec9ae510c59c460e6db795e768949", + "0x733548a34ee4463e7e4469305e9c2d6c41624db8cad82fcafe6b1c419ca069c3", + "0xc1f8c8b47c13d962e28ef07f4b0329b10f904e34edf09da66643bc15e066685c", + "0xece234ed7a9ff20f98fc3a7aa9cf22a9668e19cc89c522adc9bd4281970c56cc", + "0x35be3ccd0e05c748220ea6510e464d750aad7136d951fe75ef8e84cc4619a09b", + "0xc3166e36e4cf36327ae70a0ce496a8883f17990bd22551b273c2781e89a39d31", + "0x1a8903d70239ad69e2898f7798c2a55ace7487eda5316f3c27a50c8100a164b2", + "0x814bffafd8be066815d0f849f354fc938f042620e7747e9867e369927746b5f6", + "0x634b5e57876d0f20684a9a0cdc2f74ca6a2fe56ad5a782b1329db3770da8e438", + "0x78b355440ca4b700bba579070a4be5b316769c1d1989279603465067c15292d0", + "0xa4c64ca19139ceb38f0cbbc8e3e12cd42f2bdf5334837a818aad51302e35a1f2", + "0xd7c1224f07fdf9e26fd19cfa79ff7e4f7caaa6a8f76af0419982ea6ceb993464", + "0xebdae8689d32251acecafad96a1b050bb548f9525daab4ee831ab7ccc05e8ceb", + "0xb5fd5ce5cd12ac694aacbb051cad7f62acd917fa85e510c08e34074d1d2f6bb9", + "0xf28b433723963736edc2dfaa286cda2c1ec00d2e70905e6c6b61339822f14356", + "0xf762e3a60dec84898918e528a3e65e3a29ff399d036f44d6be752af4f35f734f", + "0xcc7e00073a6e34e7ea098aef71066685004309c73c3d3dcece68bf6965c56fd9", + "0x30b802e9a73e1c4ffb7d72c4ab2677ee13beb9d3efff4339e65021817a4ec4f1", + "0xa7e2517e85ce9a1de91e5186cb6c7f30fbe8e64c43a27166250e9d31e8f13cb3", + "0x77d1abd8a42b1ff34a82ca8b339039ea9701813a08a06b7024dca39bc4670c8c", + "0xe1f4b35ff9dd7fc11e8dfb11051608cd3740dbe76bb68d1476072619044cda3c", + "0xd09b0f2a107f491407f08d3bc3280cbb0b69b2b89826a245a5e44140fd672f54", + "0xa7fbf7b3439c32760f71bf4f63765ae03cad20460977925011c9b6f0bf2078a8", + "0xe65291fbeea42d3efdf35b5eb55f1ffc51e0c85e230330dfbbf5beb101e2ffe2", + "0x53b76db1782a2fe5793e13d4e25145eab81a7d306a3a8b1f34a6fb96e2d9a474", + "0xf1ccd669ac0c3688af76223970cb92889acc482b4901eeb08c559d8a7b315a1a", + "0x914ce5fd0cb595a8504e97bd3a30ecac2030e3389429e8a5741812805953f9cf", + "0x9403afd91a904e6827dd396137c5ea82d57fff20e29d6440ee1aac974f41dcda", + "0x4b43318a2c7189c1da147a48b537a3b67ddb1a7648aae2ba22fbadd3319a5cee", + "0xcc82ef54bca6f57bddf5479e82428fa0277bb3be89e02add0dde7890b222c20b", + "0xcd4060e70c7b199ab67b001dec2cc23b5225893a05a9ce5ba00a2b3baaa14962", + "0x669cb96f3293295507e9e81d0d8f30518083a9774b367fe1a190f0c667edec25", + "0x0088802a69cb6953d5ef532bad2f10e227bd6d57652db1e85f71e2acfab415c5", + "0xc5c7463455f619afbedcdb0edf2d762b18680fd69e662e5b2a4204e2c7b905e7", + "0xabe02e0bcf7540f7392effa00b314cd412f7e870d04742faa1ebed7c33db426a", + "0xa930f9e2a2ca88703b854c529e841594a051afa629738ea9efcdf4cb299437a6", + "0x3e1f063c784a6a3287e2d5435b55d0ac564f967229e912498b15c8fe88367f2c", + "0x382c6caae541655b3b56001d632f2e31ffc17b5e0716c0b3a3cd053e46d0bbe1", + "0x74c348154678e374e0d37c15a2ed1cc5b6b3f8d44e44cdfe06b487ba7991ce1a", + "0x0e61748d508be621a763606f73b105d148ead28b428e32786d9c003dcafeeb2e", + "0xb7d6befb659df2cfa17624d5c861031b14944643476d7a1b123e9d477dc20ef5", + "0x824de3dc7bc486dc454b17c9cc4115f3e489ad27c7dc0d07d01b9c849db0a2b6", + "0x16fb719ce622f87c746b13402fd21e01bf170c5a749a701a80dfb26ffd4752da", + "0x298e8289d26ba269bb7181f8e1cbbbe67c7ec6398f480049ea53d4dd32e7afcb", + "0x4196953b0b8424ab1a3bab753e6de1d98d31099eed50761a7791d1fd26b29471", + "0x92db496b228ba0dbf8aed468a5acd36e4c3913f669dba0c7390a3b6f259036fd", + "0xefd93020bac023cd1328687a3ceee4faf2f7e87858efcd39900afca5978041f9", + "0xb670844a0c9b2601e9d4a69db2b48431182450ab6a3437e13c9ddc5274d55a10", + "0x49521a29ad89d310163dc7a7368778361ab56aea353d878d3418e3857193e2ea", + "0x7f10d29160d0b3ccb487960bf27d86df3fc1e3f0a8268d201fbbcdab9bb5c1cd", + "0xade8dd4d18ae015bfa12eac0377497e481b645ce23e0707d80991bb2da31157f", + "0xbeb341ec944e8ffb9649eee4a68c2832a8b87e63c04806dd297b96b999d8f800", + "0xa8e421e7889744c0c05e7ad3acda76a291bb59cd757da5a2a641602d8d2cdd98", + "0xdd6e62604b0c702a373eb3351ff07e3eeea994d5f0a98add01a4697cd2fec1d7", + "0xdb18c2883f2076198f692d05f8ed16fbde414bd6a39ce5be8e2d5261604ec4c9", + "0x6707798e0c3ba031b949c728a5043c798e5a682cf24c24b028a6be6fb50ee2cb", + "0x5fa0b64faf8dd7e0a15bc5329e9632600b4801448f5241109674afee0b227a24", + "0x1f4e446c4dc1a9220062a7946bc16e7c26a7f7f0bcc33d2a6afdbc7f75ce7e69", + "0x1db5c79b419f2aa8d5d29aafcf8f675b1646b8c4d8d5d6823d075f801297ec80", + "0x1d32f8d9d7759e288d85bbb742cd01f147ecddb72fe7a414697ee95b121903d7", + "0xc0bca86e87a615b52717edbd24110fdcc1f5569577fac9d8216a26ec71c91f95", + "0x7b6b03cfa54ca0eb8bcc32fde28e20a156aac4fd4e09adb1ea048413c1a387e4", + "0x0e8b83c848cf656c495d65686d4a836863caf0c7a5a49a602aea19ea1971a390", + "0x800c2a4fba4b2a17c406d115b8456c3273dbc971a48921c1bd89b0c4f8e9d7aa", + "0xbf2cd4422956991f93d0de6ab200fe4ff470646cd43d6d94ad51e481647f9861", + "0xf73b531678e8fc53002a69a8959d4063f4694e8a33e6055ad7300b803d9a542d", + "0x8d71aa0773b19d0f847f255b20a0203049f6f77c0f6087cdfd3418e069599155", + "0x7ab66b17f9a4f8732981284a377add744563aeff4caa52d7887e8bfad38a36e1", + "0xd22f603fdc811e344c7ddf8560a56e4cd98e2c12d983100901ea0ff9a6db8566", + "0x8aedf71b0d1cb9469d1e95077866b927964f68e52f095f5e04135b904dbe83fa", + "0x63e1f8a0aa9fbcaab92e767a2da7700186c3278da48d5c16b88c2169bfbf4e8d", + "0xa0729ba883d13f906a50d9d0b1fa5d3cc94016b98b3009a4b441063974f5bc3c", + "0xd29f81992839233904d7e36aef6f7da05787ce997687d383fd41aa0e95fdc5a9", + "0x5089e7bbb3243407cb73e2c8f3b063103dfd073b77b495869cacec0dee787d44", + "0xce6cfeeb533aeabc2469f4b476dea156cd19fc89710247b78a060baccb07a78c", + "0x870b4fe549a37ec7502fbeec1d9ec02b863041433ae2490ed8ac041fc9c8d1c3", + "0x540d3ea49bb32bb2f74ac625c9e9da98961ebca4f409907303272d56dc0234c4", + "0x5936714078997c2ce643fad2aa1ffc888b2f5852bc2b686db1122b8c21f0c88f", + "0x01180076b99b8d0ef094e805ee31afd4592164e61c516a74613b88ce4fa3ecd9", + "0x3748aede47be8415109cad44c02a5123d94f924c7621ff83d8fa96ca0bd70704", + "0x44291b0c52d9d1a0deeb6c0a3bf6f49681adc69a4128f38ad9ad3a1fa7e0a34b", + "0xc58cff5e751d1bf0006a46ee3dd36a12818d6a892316e62b6e1f5138a854f9f8", + "0xb2546686e58005958df2cfc3188277e61d1ff95e76b58e6203da2038fea80853", + "0x8180b7b04e26a437456d360b44ad4ba3f88850cc65523403f141c2d0100cce47", + "0xb615b80d1175759c31d10ef5769e23142d2d5d42307acc08dfec55039db17ea1", + "0x71c07cff823ab6fe4a0b77f269cba950801d1a9a765ce44adc5132cc43837327", + "0xdf832a1e6e7e7020d01fa82c0ad89e07e8ca684b9da909ab1e96b38526882205", + "0xc68ddf2758157359e0e50240ca1ea9bb2c72ceded99e45532577ee44f4b936f0", + "0x0b0605499e4dea025c1bfb689f4a69a1696f50b853e7580d06150c2da33ae161", + "0x00b4f8036c464267767439d02b8aa178478b09bf640be30d971ddafbfac97c48", + "0xab9bdd441e581f635a0db60ae90d25aacdd684369325604c7236ef530c8335cf", + "0x813fe5c8691bc2fa373c165754cbbcabefcfd3b72e9582b3162e092bedad57a3", + "0x3cb6eae3f45184ffd6cdc9ede9a8fbfbe6eafa28402c7ea1a8cc0de542e9be02", + "0x3240579f67d7568031c4613e31dc260f370f129ced2c5114296d4102e40adb1a", + "0x4550d0b1366d3277dfe440bc42b85c170e6004667d25f93f925a7fdb698c31eb", + "0xb6f50f9c2fe993a2df60ecdee0e808076d13171dad3011b08bc7ebb666b98a91", + "0x64e032856f4a7ba96d54bb4410007275f963c67f55bb731cb7482057098138df", + "0xbd88b033a890ccbee46c93691ee19caccbdfd10a94fb909a8cbbdfe37d7ce254", + "0x4e3e5efeea0b461367eaeae11f414b41218aa35ede32d7142ebb284305ea7f28", + "0xae204c943bbf8fc9ea12455a579b9ec14db4b4f2221d17e1049ff1843afa8d98", + "0xed211c3be22f073c0680da1c455d783ed168be9331261c7cd26ac2884e2df37d", + "0x8115a3241fbf7f838c71a172df81456c96689281da02070fab16a8702c9959b3", + "0x7d0972b35c694e12f95c30638ef905ff28290c8de1411c5dd4dc7403514abfd2", + "0x2d464b69b77c8aadddf840074dd23c2e16b12d8a1ba5db6438ace865ff7b42ee", + "0x5300d9dcd02d144e87fa1afbb3b3be7572d57d0874a918b746934c515d03df7c", + "0xe6384bebdc3e49ba9825984b4b01ca44cf705e7c32dc26874d9376411adb4040", + "0x22b23bc5db80ae43f01c9f0f2bd3e58faa51cbe4384f745407c4e91aa3fb5518", + "0x5d30d59a98fdcc08916f16dd4e696426ba97d69dd180da1756fa13655befeee0", + "0x97c56d201b4be9ccab9fd2c7dd1f9e88ef0fceac3bb57185ad127cc8d56679e9", + "0xdc94573566a93470b33e226fc9d636d81606ff5834a85c128034222884f5a22f", + "0x920a36cea09c5abbba4b8ae772c9fdbaca257f8c32d2285554cc82c10db9a168", + "0xbdc913b25cc180f135d0e475e1c29600f076bd13a55c0e1f748528469941f4ae", + "0x55d1a54a02090530a5ca6d21ec8243ac9e4a5d20a474587f775cbb8f612a9f9e", + "0xa82f3969ca4c92e17091a316f71de1425f69df12eda7c07a76f38a89cc853ab0", + "0x22290cc16acf7d3aa32d8fd8edb9dddefc25e47a2000a0d36c68f64aca8de8ef", + "0xbd3eb07253d3c108e463c1bf2fb988522c0dbe77459f99ef4ad21fd479cc4ba1", + "0xc065f453eed698acf06a7a81c79b34395feaac38e5c5cc116e17e98f58cd04ad", + "0x9167e1f6a2e95261334b194a80a125c01a41dab9cacc2017345eaea990de985d", + "0xc5fc6d3df67f04c62359048424f6c593947b0eb77b7178199f2e0b3c909eb376", + "0x0074423ebeb24562d4ae0d24550c32cb1fa6d8c579160ef5c465cc782dff4026", + "0xf94de3688f5049a69a9319c68860da1f0ad451c2c5f33bd52d76194b78ab4bcb", + "0x3313e7851ce80211cb944eb4238a5cc9aaeaf8cd4b9eb22ee8a38497bc614791", + "0x96a5b0de5e4813e6ceaf68279829f22121b2579d521c7503e2642429c743d1f1", + "0x97d22328b1f91cc087292fbafc27d41a666b941e4778063fee4ed5284211caa2", + "0xadedc411975a8d190d6fc3ed8989762cf4c7dbaded44b1cab817062890f2488f", + "0x4ef6daaa3a464bfb6ba4b718b8484c6ecdcb64607bfede91dcd3dbc68a4d6d73", + "0x8cd21852c3e1233ed8aafc8a04938b550983fa76516e70afbcd64dd70eb155f4", + "0xb1e5dc34f2363337ec8cd4e39ca858ab2e447cf40f797bbea5eea041fbacb77b", + "0xd467ed127cf1fb362359e5381c3dd2db18db9d88d330f3190b4b45ef0ff1cd35", + "0x709e2def38a50d222b33473ed1ffe836a148f8b619b49fad7bb5940b808625f8", + "0x4f6aed332b56ff1b69eac993e433bd06bb55d0d60c0bf95d4fabba616074429c", + "0xaa91518bee2d4dac9d392e21284d86d93ba027c8231d8a65b6ed929c447be934", + "0x7c710e2713ad6da0604a2ca970a7deca8465aafb3d2456d52fe7f31b67bb7e48", + "0x7d77377c6a3654e27d1d1c6ae7971cab2b77b3c9a2311b0aa787e5c4120dd952", + "0xccbccb9b484e979f0ae7c37bb83e72dd6059dd1d42d9c0c5a19b79d9815576d1", + "0x6935d11fa215f1477cfe7aeeacb69be0df3fd47a6f938e3caf8b4bfd063ed10f", + "0x95e4c00e1921a89976a57572070790da3ee16ba59b28f73beb6a422a50503d70", + "0x0a6d83c0a908a43b54d0a24f074fddf9b03007f799ce98895e8706a8cc4093e4", + "0x48256fffb9d8e6f19b4e167a0f34adca830a18161c09b9c81b3b698cd9056e42", + "0xe2017dbdb61bbfd394d26e4d0b2ce681f683c5e85caa058712b3d0cafed5bb0c", + "0xece315ae54eb57556011729b940046ea61acb68189a5fa6d05b28ca1aa8a16a7", + "0x2cb81d3664cf914093166e8b047282af011fbef85607285e17f21ff70e8bc568", + "0xa694e838790162a35c89893801c8b116b6f6235a8981d0590223262948d7d9df", + "0xbc2a640bf8c744df4245deb52e50f3f73391c0a9e731b4ad5060ac056ae25d02", + "0x26d963a42da17cf31d5fd70db813b342459e3aaca27f1f9a75a3b8b3dfd5718d", + "0x6680df2da35b476fd9598c2b1043740025bc527ac12add2caf793ae257adaf28", + "0x058f659e33f07899b47039704aa8b767ffdd0b4da4f9c29baebe92d2a970be7a", + "0xab81e22c38566c73aab0592f7631b28f545bc68550db477c737f64e54a808ad4", + "0xc19802cd638aa6b73222d7654570a1fa125e0d99bd983f12a0a245c232773a94", + "0xf018f2ce8e3f944aef3847faf87ec95a2b882609f2f165b65e8a7c579081b26f", + "0x6a8002e0e3aa9ce08f2e4fe739e172560ace8ae5bdb691424bd489ac8b747013", + "0xcfd3eadb121e59130fda6f49ee0c71a2ea38a4a65a8fec5f160bbdc586fc648a", + "0xe4793e508d32626423223770c7b43ef0eb4ce4c9240fb5897b087456e92f3ba1", + "0x2242b5c68d4655796692b49c144be5db50a42698494cb11331d9de233dc7b425", + "0xf16cee998f9fe50c3d9db80f5da4dd5e072300f03587e391466bcdfb39fa9474", + "0x20bf37a54bc4e529b679687c2bd2ab0e8a6637d6829d516ca719b907ad3f05a9", + "0x756b8e5282523c0f6ab570344267291edb34f82f269167c4ac5ea68afe4d1b4f", + "0x82ba4509e9c4d46aa8aa3e4e729d869f043cf1a01eb815a9bf365187b9a9da1d", + "0x67093772e03986d7eaddff01f4ceed6d53791f8c64b4dbb36d3977e743f2d70c", + "0x4ba2da279b09de02c54d2f13505599aed49b87e4ac07c8b342030497d92931b5", + "0xc98f62f1d175da368343e7883b6545168767436f89729dcde255f405a68d4a01", + "0x58df94f00905f4720f41a339305ae4b3071deb2d2a8609e2779c2c8a5dfe253d", + "0x84f45d3ceb433881b14b72a24168fb5d2afaaf04ff0f458b23ea46e2581d7084", + "0x840a39be08dcf8d567506ed845a05ceb6bbf35bf3124fe85a84b058325cc2e6b", + "0x296c3fb628b00fb6aac53c03823c71d01c4d3a2a518b057835fdc1b4b063f6d5", + "0x34503278b2d99167351e6884678c877cc189d2b7ded0065d345d698fd540a09f", + "0x56c179aa09cdb97935f93647ff5483826a518a0717286888361140a68a67fd68", + "0x934782558d8c2073bb8be608b59f04090bb640da0ae1061ec2ce3c09d4092bd2", + "0x64a69e79f6d29046617355f2fdacc6b32ca0924a3455855d39b2bbc4cea411ef", + "0x263ef1f6c5b50c39aec41b3e882b27ea411f52af869e27fe0817410874b464e0", + "0xf0a86bea72d93c36c2d54fef8e51dec2a1cb3f0b3311900511770f9140fc2ab2", + "0xee8c128c9af7b8b7193c8694fc9e5e1117d9edf2e842c39a39795a6f7ab82368", + "0x05617b5776aacff4ca05cd8e1c1cfeb6fb898e9d31e1ccf6410dd11bae2753f9", + "0x46490352520d3ba6b677836deaf4f70030fb78b864d723da3dfba0eb0058df5f", + "0x55958a339148adfb1b6c4dae2964e5e7cd09ef4c3667368e64231edb7f2c0e4b", + "0xda89957587a0a54b5c782c23df9d5dc7b4e8dedb1259e6a1b7bf7fe8c3ade595", + "0x3f2b3c44e56e82e5b6e58acce6787b24e0c1341e11ad805d9f2a076ce3e756b9", + "0x4b7a82b51928c152f3d2788779418dd77c3dd76799a07b25dc93b85dc22b01a1", + "0x762935a6fc9523395ef020a3e66f173ddb298ca90a689a5cd67d800062527d7b", + "0x83f04e18648cfaad6338b09b8450a31678b6bda9ae1394881a99aaa27af25fb5", + "0x2c62aa22d82320e5591227685723ad68362912502b60bdfd792bb0bcdf8a7a32", + "0x4f153427982ff7232ed695e465daee06283b16153faa52b3f205b8f0430262d1", + "0x1d0359a90db20c5b3b86f0d13b924843ad74c04d204a047077e6e7872664c974", + "0x9ffffa5c871f8d7a3411c8b1df0f1356813a463f28575429cb8e68138771d969", + "0xe6f6ffbcf2f64576b6f35a81814845d25956ae52021db6f0d56dfd060bc9fcf2", + "0x96f1605afd6944ac35caa97c1f8927695d65e772084cfc3c116d831fded9ba0a", + "0x5679c0bd51cee27bad056e2756725902c3b380540849a65edc04aba65f27d69d", + "0x1ff15e456e6fd7fb3132efd10e6291150f7e8c60b2b130af94f77431cbfa73c1", + "0x55909a5ec8d80497c659398828f0dfa1a68679861de91a2300de1a2a3f4c8aad", + "0x488307b37ab4ea84247e6e243a73ab1fdb92924f9522bcff76962eae50b18c2c", + "0x068478ba8574e3879c74fc121e78004f55e48af45625a9dbc051591075491b1f", + "0x4449cc951454153f20d6ebe337ab6f17dd8ca001bbdcd7d2df40f26d461276ce", + "0x61782497138e5266d307bb4006460c527880ebbe561ee8e904fd5dec67eea691", + "0x50158e86893c1b268daaa688729d01b6b66033f248dce9d6897285be1e59174c", + "0x988ad812b5aa8c0d38e4f57d7a1628121ecd5068e89ea8b7c4c02dfa012d0d6a", + "0xcf46a18bdd08ca7bdb66036a58fbff06658379af5fc63b9df726d66c99d3f500", + "0xfbfd521c0e9513ed7edd376b39387eb7b1fe9376fe02e5e2253883408b77a6da", + "0x9a7a9625377ada400a57c69070694bdc292b9be9bb559cacc8b7f00e46b4b55e", + "0x8d989ae15c6a754769517d8fe39edc2aeedda84844d187117cdc75bc581e804e", + "0x8d421f59641113b6641e64c178eff8217c42cd0dd6d98ee3d20e9c9339bba59d", + "0x22a6ccc5debf6c195ae754b4e7799e06eca1f37f446795c06468b3dc0cfeaa9a", + "0x18fd062c6d751cb0109af919ae98e86f50086a387f6ca1047073396b0778c104", + "0xc4555214d0a139285a23521ea03910183f335d23c3dd198fcec87597dea8728b", + "0x1c65d873422348021d568d3e06e7202145f0887b7c5a1f7a504a555b2aa1df3d", + "0xf09e83446a7e21c67d33258a32ecb74cb5cd203d00f30e891a9f1f750f139816", + "0x8a9d71e2bc19e3b73a39228e81ffc18c171ef2b6ee2b1c38c744b0a1c83dbf46", + "0x150e6cde263b5356a2c9216d6d04fc22b45b774c096182adad96208b3d073c07", + "0xd32c5388766cc778313b0b65c165447bd89871839c84f8a3c57b46062792f901", + "0x792ecce375ae0884ab8f339a96718a9e9ba0701f01504c239f9e91b8eb439efa", + "0x10d2c68d525aaecbf41dbb335a64876d8921d950e484b9ce0f37f53c85bf1132", + "0x1a4520eab46db5d6d0359fd076d793b5b2a41fe9dc228aa5675218aabf76e94f", + "0x7bb59e415e56aa7053c0bba45029734960c20ab24389de9a8591c402256a9d15", + "0xa1707c69a67645dda68955a39f0fdcf816cb0c9028b199768f93f941761b7955", + "0x4719a9a9d4b1bdf093f5b274ad39edafb8d96b794d53515338aa31a524ff448f", + "0xf1ff0495f0edf91e8fe8a2437b1c30318101abbed499a09a78fd0bb8727570e8", + "0x9cceaacb496968d129686aba43dbbd6ec5ac32aac221c84017e502ae395450f6", + "0xdbf5b1cf819c85003a2f054426cc651d06d7390b5fce3291b451d8838afe26ac", + "0x6f49269f12d96ca3e5d2c94d9bedef7f9789c5aabe1f19018c2975d6405753c3", + "0x24ea949622beb3f00998977bb651c38bccee0c1a366b0d492296d5323aa8fd62", + "0xed3373bfe04dede7ece974036268f7e31d2c0cfcc98c3c997818485b334e0f92", + "0x5814d6c58ba96eb9b8fc14c73fa42cd51e50261911db30feb381b805457c4630", + "0xd94c2c7ede98282f905e50c7df6505dd4219c938de48efca7881fc125e82a013", + "0x1b4a65772e46f75cbaa57d7eb3ecc4e15f57aeaf210cf26006bf59845fc25b2d", + "0xc0433f77dc1fddae5e66a873dad1fea41fcd0b083b5d06058bdfd75dd10b9551", + "0xa320a3a22ff9f03fc89486aebdf9274dae0c76453f34d103ee3f5b395535fafd", + "0x7e299780e72dd8d33124533fc3dcca6fb9333156bb448afec2587abba28ce647", + "0xe8f6062bba8d33ea413a5955775385036fa072fca00753f5c1243842f9bb07fa", + "0x02d7d5310a18c2e0ef065d535226fb2b0c196bae1a5bd1dd0bd9fb6eb41bdf29", + "0x7703ae8c0e8d38d814ec68fdbe5d8b0b2462a519d1ebd96f495944737ab74402", + "0xba3bff7ac710fcd09b3885ce1c9f4fe81be34ddd3a5191f4472f9fe5ecf1f7cf", + "0xfbc6b9f52999cfcf0940e522cd34c7a9390a76cafe63943fb57a466a555ffabb", + "0xe7f0c64c7cf663b89b61809d63e7173b828cf07f5ca32b8fbc5ff4d9d5b8955c", + "0xaffb29148beaba6da4552b5ecc81fc9012e8b55f963c28cdf87656307447fd24", + "0x813af214236844bd897789424b765cef63e8e75fafe4fbb4ed98121b8b50f20e", + "0xced970967dd4ddca3fb3cdc69b82edcd19fc07e8cc648b987afdc7a5b131972d", + "0x66c7fc08370d2b9bf4e09a58302d9d8bc484d942f7669b3c23469e09e2fff2fa", + "0x7e8163b4442de8ed3c385e51bae9bd0a9c05c916141c7f5697b759d834486df4", + "0xb226ae40e1e91ab88949bd279ba7cb34c8aea83244d77cb0689fb378b09c4753", + "0x5d6f754cc4569df144eae52de9b7cf7d918210ce9f02df6cd72ee07098e16fda", + "0x67d1a431ed2dd7b38e364c18dfb9597267c19c2070658d0365cd3799c94fe160", + "0xd06add714c48248edb03e57831114d7d4f3448c4b6f7ca31e48050a96e2f677a", + "0xb6f7e870360bb10fb7e9eda3b786b78e7b2b6e8cfaddeda3f0ebdb7a4899a09c", + "0x63afbbcc0b956e19fb566e49705545172054f4b07f0981fd15ead37c5abe38b4", + "0x37d8018e5259c8a6fa2f6a54c2de58e83bdd7fe558e542bf5947be27c9c0dbcc", + "0xccc3a9f0bd117d3c19aa484567372fe844fb5165db3bb8a1b14c7b770bdf350f", + "0x9545abedf5fc674d59ad1937445456fb1df698c10d652f43e51df813be2c6e6b", + "0x7cefb2ef8d1d69afb5fc20097bbf9bd0477aea6fe36e3a04c6b79a5449bf2238", + "0xb267da5af33ad6e5817f6c7486b47fb0609393c29febd8ac33f0c8899b7bb4b8", + "0xfaa24406e814a944e34a1ef08c2defca1a223c3e41840881dd52b4d3d98939f4", + "0x8bb95c78a93557ba24371f0f8ecc0406ead1e3210a1abc721747ad2a492430bd", + "0xb5b54cbc46434a76a434ef1b04738dfd3e27d19cb9cf81f13050dbd6d3e12077", + "0x07cbca993291153be1b9377ef014f4361d7447fffe9e92d86fc9e1c768f16ae3", + "0x0941267ae3133a2d94f5150a057dff99f3d70810b71528342bddc88a6f558ae2", + "0x21a7d446331dfa2a918096a1ebc27730a4bd30ca412168e4a54be2f956bc0e7d", + "0x5016982f0152050d89bbf693193aa1f1a1d62046662cd60fbe09d8660d2b76ad", + "0x594c0459c8a386db9e27c6a0b0b53df94f750c31fc38b526b69da7cea43a45fd", + "0xd6344f17d8c23db458c241d991cbffd963fd4595cd36661b5e28b99df7edc108", + "0xb90c54ffcb1ccd955af9a2c5cc4c5db71615320256e5dfa1e07687052eebc4f1", + "0x884fb29301634438a4cc07159ef47a8c79c8ba9ed369d694ea6e2ad8629eb205", + "0x842b1cabcf1fb362f8585391b31b8e010e5bb66a070c6cc57f051cd602c640d5", + "0xe130d2e0de398425d97240d67ceca5b1bbac610498525f69fa79f7f22a8977ed", + "0x069f38f6b1f5da578b68647f3104ba8f530fcfeb0b8e98210eaa0d03b8e70b3b", + "0x3865028654ef8d11cb2ee471b248d64d25097cf888fa48c5c7da6316a60840ac", + "0x39d81a57fdbc9eb9269bf4cc309c2f6aa93fc3f2e0465b677aab42889d23d27e", + "0xc2cf9555ff1d47c33a2a77583849d36ae066fb2ac0347efe3a995bac8923206b", + "0x0d2e4faf302e25b29861318689442fde5f4aa87aa3e30e11866709acd7051563", + "0x46a0f311cff75a5df122c11eebdcac8ac1bb10fb6d4e08f0ed222cbced3e5100", + "0xbd7fc28c115557fd7569692764ba8f98449e63f39c71a86bb39c2032e09b9a5b", + "0xd7b1961ea24872145901dce3bb68bd277cb69479e49b0618ffa4396e8d3cef72", + "0x24c41f60028f4a471981ac3aae6c15bd9578ce30a2715e785001edefd8c2b940", + "0xa9bd56d2531a97170ba4ac19c46d063419fca771e829fb99a190bbc6219f4137", + "0x26748145b0d3c44b5c06af41a6815ffa0dc6e7fe0b485f721bb78d70addb1867", + "0x9341474110e59ae74a7afa553e0c5bf5758f54d910b771b9f713db12a5853b67", + "0x026c5d6af714f14f3387fccbef683b853eb53165eb8ca64195be74631e949fae", + "0xf58f9c8a50ebac84735a135bdaa8db2a76838028373725cd6e8ae7a37fa6fd19", + "0xbc0ceb50de4dad2b660428d3adf5dbf20ae7476efea5cc20e1998baa3dafcd72", + "0x0135c011806f478e7e34f92e18008df9898337ed01c9061e241e8f14731fd601", + "0x46cbc59b747ae1a21344d95ca3162c22d9fdcc188fbe8c935222fa397b54796b", + "0xb316c0b7b23a82d35b5a3c5d9c60deb75b24e1a736664789aa125fa2d91e93c0", + "0xfbf1b28e72a54205f8242193b0f1a8bf27b9ea0050ff7584893b9844782fe82a", + "0xdd889e2ff6bbff03f1c90378a4ae436f516cf0ab8762fae71c5b16200f81467f", + "0xca21af07f87f18c29e57e3319fdee6efa634529a8d138cd9600b08660b64a4c0", + "0xa36f928a38eee080fabc3cfbd8153fcd6acd6651a5cab25ca6a26bc864a52aaa", + "0x5b26da52816099e5436f971926a9a8cb6fb792c6db6e9b60434b6a77d744978f", + "0x7652d71be78a8dd4bd32465561f35ecc555deef7cc77b756d5654971e15eac65", + "0xd926d1c3fdca97e6d8b45662462369b2b4bfcea1159f72d611c8109b34521bdb", + "0xf3853dcaff82dc76e4adacbad68b6ae20715302976dc3ca2439102161c1eafbf", + "0x57dea5c99162915cbae7c144e1c14d7477234c35435ae9602f4cb5eb808b1899", + "0x9777a2e4e12aee76191db64c7b5129a6c2ac6e21eeca7bd5c250e7db38147785", + "0xa664af926d0ef91fade391483dacf9a5c50cc128bb6570c41e641ace97bf63a8", + "0x51b1a5c2a2ce7b7720145368e6be270aac3ed4e64d7d6caa84887ea32808774d", + "0x5f1020578372f1413799fce4513729848276b04c3789d23ea6853f1e9f7441e9", + "0x9eda9d698245c5c60dc084d5be70073998aec99dac4974271164e4fe04794f34", + "0x9055f939d51044c54f3e09ee0180844ca8c0f492e8536d03e6950f15f1688e7c", + "0x9c43adfd41321148220a54cc79cadb0e105b2acba146a806b877595d2d865dc3", + "0x136a0a75a28e4c13c3406da60505e0052d464a469803a98a23211a71e61b1af1", + "0x87fb0be2ba469441bdbb0007f540fcdf0609486e8b8b3fc152514934f64d3d57", + "0xd319643eb15458ed81c2bf68efc3874160cf51af3f404811592bec569736c86c", + "0x8cd584b019d77ede9e5bb2374a7e0fcb290a4dcb04727cc8b72d88b4501c9b9a", + "0xfd6eefee0229697fcd51fe0f312d7ce7c437ca9e27c445f5f4185b1e659a3e8c", + "0xdd170bcfa7026e4f53b9248ce76ebdb08e8dc2fe0319fbb524b0428d667ef52c", + "0x4ec0ecc7e94a004207bd6613519d1d9f1f7cea38fc48203ba10482910bc92ecd", + "0x7914e6a1877d6d57a058879cf1786cb23cd63fc910a2bd953275d3c6d29f18a4", + "0x5450d2748cbff5765833baa04e74f893aa9dad63487783c96fbcaaa78fae73a9", + "0x53ec47b5c5ee5292223f665b7239ec475f05c74d4006851189c56bc34770ff83", + "0x5290103974b9828b0ce5d516b5ad3e09a0f660140428e31e180d2afb25455df3", + "0x1d2c767dd33b797963b5d14014c0d928acd3362254d82ffcd299318c27477639", + "0xeba1efa59aa74e25aea6fe1098a5cdd8ca2bd67b3882d4fdd24698a7804edfb6", + "0xf5a344b6157c4f219900da0c4d14c51de451095034f5438736d4bf5f3103efe6", + "0xdff8a186a19ca5d6d79cbeba4f8e6ee49c15ecbfa55611b1f0a8cf2922e15e23", + "0x6d5297ca2d24db0a2aae2267998858f8c2dde0494bbd0622b3fd32efddbbf4ce", + "0x8d638eff75fbc935e061494eb1cc60def9989943f4349be3675b976a0ef59d50", + "0x41af3c2a5c8632ce8d4ffd4ce3479dfcd2dc14b852cdb5f6b0c0ab07bd6906c9", + "0x35510d1a1c932f20d5ff6e3ff466d3c4de8adeb14781755f28134b821e90f831", + "0x9bcee460f7a0d7948185305d3c8e0ee6db385e2478d19d9db01b80dc86682793", + "0xd077c2e7994ede14023a9af6d6d95029f1532c6c8242015d6e1f0c2328edd345", + "0xd787a3fc727930321b24f7697b1e41a78d736cc41bd74f4eec8974d2ef6bba63", + "0xcd90617a3a25c63334144201164a1081b0dfedf585edc028691fde45fd17d1cd", + "0x70ba09f3df1edc45bf850cc8b101cc3374ec5dbf040ce158526d6d040cacb761", + "0xb228834765d6909eb97cab2ba286522062eac8df8b61ef38ae1abf8d04662230", + "0xa7922be59e87a2c0ba6fe2fc2d0be313246b84df491072da5e72ae1aafcdbece", + "0xb88bf40e2dc2443b9558e4d410b860ebe6dcc66e5aaf9a534f5911554ab4146d", + "0xcd0a7d9ad79bfb41fa51e21b8e59b93c243ee7747c3133b0f66a3bd004ad866e", + "0xfde48021239b9100c57d246a4ca957bc9aacefbe2d9fe873d80695c338f2eb0b", + "0xefaf321babee882141753b0036a9e169d4f7e68fb871358cd1333db21f40d731", + "0x908ea967081e089f284941938b2257fdfec37d256683cb767960e085f54fe603", + "0x899e4f399c2a1b1263707ac3dc1891e68ca1342397ab4b33372ad3f57eb91f94", + "0x8138f2e2abe1f063798fcdcb84424cc3333b6689652741a591999e5551c02eaa", + "0xdf5b611673e3135a0370da97d4ddbfff620203dd57570c38f4d2f4ee8acc09a1", + "0x0dc1bf7ebea0043d6164e6d928f4516297ba4792f84e3aed126254d3d238937f", + "0x6db09aae5885f13203c6437d44eef0974f97a64781fd5547832a243d729eed04", + "0xfde1c099f1147e5bd75f195b51525b1859ab4306a28ae1431994b9426b81d1a3", + "0x837f91372a608d254190ad8066310a967d29d12a3fc60d08b886b7aa927bdba8", + "0x1ab72885d13b6d4658b9e14cb672c90575a1e640230f520c4125e7fc3a58430c", + "0x66e1d5352afb38439b8d36dc7b624bb684e5880ce905253d91b682845325d9a4", + "0x5708e1ab21ca2f40bb1fcf65ceb890d242abfb2278afa1adef2e6b3106d9541a", + "0x0e9acb577b4c6e28494f0b3bfcb5676c8c19634ff3e17797ae84298c167e6f4f", + "0x3046612ed7c84034f15ada8e20a2f1f14b17a6ab86915ca64b6fd5ad2a430305", + "0x6ac8465e21c50ab627eda807196002b3b2762a9c4be257314380b942655e294d", + "0xa6372d26f36ccf3b6a9f9c56831d3fe6de3e4a46bb1d5c8d73d82d14895751eb", + "0x1a7d38d7afd4c699c82a180b57691847f48cd7f600cfe7c8cca37cc61081c2aa", + "0x3a2fad66d8e718edbcf84f082353fba60d6c711cf17befb4f6526188acdc2036", + "0x01412c03a1b8472fc06d6012bcb781ea89babce1b3a6381639a7f7ead1692aef", + "0xde22965a171379f1ac1c316664130e6d76445c74f9a59f64f99125239c19c789", + "0x0f08c29aac9adaeeb0f6d34cd906372fb350f3279bf1c59ce1123ad6859777e2", + "0x143a6b8ca0bea66dcf6f6a828b32a305d918eb148ea0a2954aa932d5f63136b8", + "0x2bf1595ca6efa1946efa72a77bc11d0d73d8b5a27b3669af285de121a3458434", + "0xb87a45c3d6417eb74c625ad60dde3c11ae0d4b268030e77b8aeb81cd6e1fd613", + "0x0a1d26c1fe6d476a7bea94f86f21c1081f5620e915d79436c6447f707202743f", + "0x591136b68c6934e6c566e2f4fc8602ffe65e784fc0752f70e1384c98485d52c9", + "0x4cb1b499e903de634d68b9c2a359cfec49087d758383ae6a4428538087330e57", + "0x7e0f6df45d4c8a05886e96a34d8eb5a8a56d391658d45b64b5fb22bacdf29d05", + "0xf4a12c0dcf49f86c7a60a32c1ad320223225d0fc29c70e21b12ad64c98e2d418", + "0x84a3754a854cc6a435655e5d3841769ff960bd68f2bfd6b7bdd1b4a6a4a9b827", + "0x81448b76b7123e37f6ee18c458e2a7c1f39aecbbe9b0091bc1918f9388c48d2a", + "0x78ed23672f34b0baf9affcd8342c34daa508fd31fb0ada1f6da5744f2620c00a", + "0x96a8f980568b4b4629f0a687e2092e7a1679b5e8b4d4011e26783d5cc3cd6001", + "0x9d3ddbaf95223657184bb599361a9cbb48fbdab34e6678326079dbb71672abf2", + "0x171392aadace2713f781ec104c58dc4e2bdf7801d1c2e81aa783070e55d4fe58", + "0x3cceba1d7614c8d5a20d3c7f60596b31f406fa052408d0264fdcb355a6a04913", + "0x78686794579a6d71aa8326690c0de8de589f9d837cf4e9066f6d84c2536b25d4", + "0xba1645055b02be77a94975ee80b767222ea90398a4387a9617efcc14b0f986cd", + "0x9507e4cdcc7c66a62c00e483b743ed109e0f2924a3f3f7e4b69595218653cbc6", + "0xd1abe9847382dd01a59f0ee12b8ec695c000431b9b65aca6525001171bd8125e", + "0x329114c70096584c7655749dd4f70f3b65ed5f7ae8c2366447d4b6dfec5e2226", + "0x3943620e5a8ae774e5b17187fb7513cd03c711d19de2a4354688e0759a17746d", + "0x85f2b1c5d14d65f00ffcb41b84d29e0880b45a60b34e8d47789371f6451bf6a2", + "0x924990ef241ed7ea5ff0d9f340bb8469d6980c39119f9ab92cb6e3f2e7591699", + "0x341929c8da76e70544bd9812accabdb36ccfe64edc90140b75813cae4f0ed8ae", + "0x69e466da36712d3d83fa114119219b910b06f75a46b4a3e5e28481b77c4468a9", + "0x1caa275816d922b0adabb6295f087b4ace5ab96907942ae60448c8c3fae70637", + "0x2cee29b446ed210d7bba01acaa311ed0c053c3554bf8766c5213b95e2f38e076", + "0x05243093668bc7d0d5dd95c8046e591af57845d562b49154615d802ba7995c2f", + "0xa721b48d4c1f66988e9e1f65a4d70baaa4d1c48ed38fa59cf5ca0f3a85424dde", + "0x3fbd2ef1c373fe9b2ee9c6bc8b962d677b51a576a13b7084d9c54915b2c6c9e1", + "0xe40c45394ec2688b263aa5ff98aba6a9bd5d31d774969e319bd534ace63912e9", + "0xb9e57c5e6ddc9cda5c9e73d91605356277b63d63bf7e83cd3447ce66b357d0db", + "0x40fe308de37b69ffe45da62950ee48a718a6900f5902dd444cade29998b1995b", + "0xe674d7919b9807cf23dd142b59083d71e748d26261d9ea0213f754e19860fcf0", + "0x774c47838f8e45752043103e6b0f42ac04734bde2ed2b7afee8fe7c8273ae701", + "0x2e1f34fea8d52aaa12d24a8d5f8bcbf950e0596e449b153cf9429be9ca7353d1", + "0x951b77a793f63e26915220d63f2d64377d2d7bcafd9bb43253fa69609de128cd", + "0xde5a5fc6c3a9ad0a2ff927134829b96c1ccee352274a6cdf2577356a2346415c", + "0x4e364e2e3d10b0adf1f0f54bf76599de67493c0c1e3d5be7e5683d7fa107592d", + "0xd16fadd5f8df61126e2ed0093e0ba7f02cc35b949c094463bf3634637c3e20ef", + "0xd93add6180e4cddda899d0927084ea7c553a5831dc2d44ab606fa7746ba0e218", + "0x6d4f060aca9cf08ec4dfa55fa0e9e91df4ccd84de6feb994c62829c02504dc3d", + "0x56a0f3550d5e23b388f25292e51a2d59e77ddd020170d9cf3943512d76486e83", + "0x16c0645951d307ea350212ea3d26fa7b798cfa0aee18142be52681346c14abc9", + "0x50d6139e1928dc964812c285849c0e373914aebdae6ffaeb97e9798dcbf30d5c", + "0xdedb1d3fa5f859cc3202b546d017ad6119f372e63699f66bf96079474f71bfaa", + "0xdea2539f979040fa0f254afd3fcf20c700814bf48bf0ba21fffd85abd0a4f821", + "0x26764c3c1cfa3294cb223e1349679034a76fe4b5b4e342c2230ed0b0cab7f26c", + "0x93ebd047e42080c0feed3255e90b32bcac9006f010896d5a596e22be05af7c96", + "0x6f7f641d48402eb2589a04f5e5ee3ec6ef1f6b85f9cb01c3be4581eff0977222", + "0xfe04b219edeb741af8c3da69b6e33ebee29ead8fe1ca5ef12fd5ad2e68f36cce", + "0x69fe98a43e1770765036b8cc15f39dd6d317f53fc8476b3d9f9a3425dc6be188", + "0x4edfc4ab6a3f907c65e5cebbcf5f37a164561e4e811542668a37a9d4ca4e97dc", + "0x1f24bfcfd7a377f8408e1ace8465cc99591e603a4db28a1369ce574ddc6a7ee5", + "0xd3b2edf761d249b0e4f54f9976ecd134b1b15c4e6a8563aededd7d182954b72b", + "0xef0ee0fb5d8c70be1e54d3dac65ff7a4908190e7ea3fb924efd6b43f77f258da", + "0xbed1af689d6e0bf9fa1fea2d522c243df3bda1ac00f823bbcdba594688d2767b", + "0x5f710adc4613ec1818df66b55b2c37e5cf9068a21572dbab110bc1ff69e428c0", + "0x605709d1b9fbdc778e573f2413007c34e0bd643931813d379333f8dfb58eb2f8", + "0x3e4ca4b35e8aaae7496f2ebba0df1ee7f7451b2e0a69349a00e5bb599f3c90b1", + "0x83421dd09d2ac79ec1017693c06efe7e896189afed9a378c6188aaf7f1edb0e6", + "0xcefdc621065befffa8ef5d3e1dfc36e1288dc8099a64266058700e3d7172741a", + "0x3d58b89af7f4f1285943f5a2b7eae4276332dff2c80dbe68ca826760b93c405c", + "0xa4c9e36b4fcb64a9ff301002098926f1043a9ff9d4d2bc4cfe0dc5bf9b942850", + "0xb2c404ebf5a09ddfbacdf0c80d6b80db38191cd3f0025f795e6a41259000bc00", + "0xeea5837530e227fa21f2594b4904bd749d2b6000be1db6c288923f2ee5e019cb", + "0x50bb4b27f828cc0a193346732fdd68e4471440d73e8488c0796c0c203416b044", + "0xb63a60a49705b3acd522223a859a97cd15b927a0a032e008167ec520c80e968c", + "0xf26c2cbf974f105b6a7e44a4b64db7b5f47db2b07b0063c9dc0880b49499a30a", + "0xa7685c0521da320e3d6bab035bb6f21dce91a2f34947d41572f440d10ebda026", + "0x8836e7811447cb05257c3f0945cb9613cf98bd7146f06833c6271c4852a00720", + "0x466d2b4b32c49586d95d9d7e04af9cbe0d79989faf0c0d47ced2f44c0ed9eee0", + "0xc10c205911b2bbb126f68a0623be89cf15d5862256d73443efef334266087bc6", + "0x37f0b39ccfacb3a6f227aa7507b05dcc3e3a719a05972acc97e3a9b9cd07eee3", + "0xe19fbb4f4b12f7e7c0587000398b6db0a1b6bd707380528f91d10f2137a13454", + "0x6be9a029df58225a0458ff5016a9321eb8009ad41d96204277cc3a054655aedd", + "0x11f70bd87f0d076188444080ddb70b47439185ad9d5f5d6de5a8473c22de4562", + "0xff4461696ff8b0a2c675a0ce75456af609636f5d3e32e04e70cf2a11fd9bdfb6", + "0x9b6a9929b92a5aea4474ff2c0cb66bf5cde4757fee2a66472a8667320fd33787", + "0x3abf9f45f3520691cc23b8e057a9b11263f271017b2e0341f54a8c4d0c9c1d8c", + "0xa4c59800856a07512d976b09da6a455161f49a6c2489f31329b9b5d37097d22a", + "0xa16064ea2654c58d9e320e2f375db0baef5e797451c78755b886245aa19235d0", + "0x3d4a2c6730cdc3bcb2602403e9361d2b0ed9f13468469f094935762791639d8d", + "0x5ab3ef568d36131b116cd49af37dd7e1fc1da93c1c3305dbc7192396647a12af", + "0xf1e863f3748fb0eb0a436e4aa033150984bfe21500fbf91c27ecdf4a2c2cc246", + "0x1089b1d841b41e18c7d64ac3c67e75b7808603faebda33fd85805eb00d23df9f", + "0x988b647612aa96d59bc6567e2cd81eac3a5c330d1e10e6eba496729d375dbc92", + "0x487c44b0132c4aa36e09ecc66d24c27d6e692463cc8f2ed57299762c96c12a33", + "0x18777669b6f9c2e8332ab10ecd9a84df912451d08f01efb69208a4cdd1bca6a5", + "0xf39b81cda7bfd1677b6329a2ad4f0fac160a0d20f9a12a6fce6d7addb0f6d7dc", + "0xa69db29f1a70abaf8813cbc790ac8f5319abab611f963a3bc68cd143373168b2", + "0xd31dadc321c181d167c459f7d59d8f942d817c0f57fc19408551aaa672a6a8dd", + "0xf9c8d3effd661153f9acfc895b08288d340cf0cf76bce4b030b59254e68aeab6", + "0x2b7fca77a4ebd5ddb9f25263c1c25665629cfe2f8ebc2becda8cea08e3a2973b", + "0xa690d2ac4a2e72d25f2930b1485c992f9f2085bbc6e628219a819a785fbf869b", + "0x0368f7fc09a6d875e0b14145587a1578376c9ef2077ddc073c20ebd3886509d0", + "0x382ee33f0a8fb57969fdadd9f7245e0ca2f9d3ae3e22eb3b2a7dfcd3d752539f", + "0x327bc44d0a719d09a94aec702ae72698db44d8cf97729d8e2b29afd372711349", + "0x166b964691b3282d75e356e9cdfc11efb8219b30636748e35ad1f0a7343ea579", + "0xd887e5e907a374478243fb7b9168b7c677f482d5b56344120aa35d08640bac6f", + "0xba0c8f59d906b0b38e6c47c974e6c90bf2f5e8a5e246649d31774d34679fd0b7", + "0xac6f6e90e5d1f76896b86a42f8587926ff8e553de6fb898ab024f64556cca1ee", + "0x5ffebcf4f4705d29b9082f57aaa8623e00183fdbff871abe7a512e92fc78a8fe", + "0xcdb36f99df2fdb6ae1941b382369bb290579eeecd619b48982ccee70ae5489b1", + "0x3d979bed8bcaf6507625af5aac01bb4da36a59c7f4f5b0466c1e6e08d9884a45", + "0x4347a5957d0bbd586eb9181a55fbf05ee2f5f2742445de0f0e4499b14a07a3c8", + "0x66920125e5e2221790894e283d3d7043e2a7f515047ae7442ce5bb3df3139560", + "0x201eb8b43f60084e3bd57643baad36387b2f783e325fef4a1ff2831c6d744e9d", + "0x020b62654891b7ecb74f3cb05f16a73bd00b103edd47f9b55598d5e0422f9385", + "0x569164961ca5cf9f5b904f589d456904f072df70d893cf4d2b5c97b33594882b", + "0x353028a25f35cd7073d0bb90358c1f4a513ece465ee709beed4636219303e915", + "0x822e49678cdbea202b3bb076b29b1a21938d492ec10b2e1b50d74b4737a3d6ac", + "0x495e890b2a01c2424979f768c431e54bb800b9f7b294c2c4c9fe9b0a3d266956", + "0xa2cd47f93a1584310fa52cc5fb9a139ccbad46cbedc149a579fa4579b3d0c407", + "0x1762568b966f1ff6fc9af0df05a0a28af1168dd961ef00974111d13e5b218812", + "0x21e7d7479d449924a349466ec30c57a1afe24496c2cd8269dde1d3bb5d42f95e", + "0x3891646e0372d54f6b1e2ca773c281cac18ac3630c06acb84097965ac34eedf0", + "0x91c8d94f11e8a6b71e5ed2e30517797bb233e036a6476e75e8979886c41cef3a", + "0x868a5ce4c7dc8c7d4f3c1d6ce680e7d141a4da7ea9c1bea90201b7501f86046b", + "0xeecfba3bd2db274612bfe1196635a280e165e249fa0f28d2eb30a18584bfdffb", + "0xba0104317cf35e7f5949c465471ef1f46a037bc8969883407f295aaa3b628966", + "0x7dc9a1a6c3bc1f5f1584c2b2f23c67d0e186933acde193a2a3092eafebdcf190", + "0x0bfc506a829ae63e6c59c0376b8fb225c7bbcf9826846ef39624e5874b968015", + "0x46c1b6716777098d57da94227da6c945977b7460b514c5808c84c9dce9e9be90", + "0x83da006831e4b6ee30fa10ae6d4d5a03ccf7de0511ed104bb4dff0dc1ec9e800", + "0xb1f095b06b720f460afdfcb9ae0a283815092d789877be1677473777db7a14a6", + "0xf3278492785bb8ef5a52677393d868872a974c53e2aead397fefc563176b34b2", + "0x44ff2c1d83df22f595df941aea5dd548654418dbbcf0e92c37302b9c091705d4", + "0x04c221d1c1dc0c82463dc055904ce4c5fadb8f3ee413a325af47cf84c98c698e", + "0x0ae13fc2857ba766dda39387fcf00151840aba36764f4bba2aae05325eea3f12", + "0xaf58b87350fda0921974d538305ed94b2eb2215a66f8e29b1efe571e68f41a63", + "0x34faf24ec7d8e09e1fb3b883af59c8c797ed2f8aa6b3a776599a84a6360c431a", + "0x8602b8d5a1d3ad0cefbecf6605c39e39ad77f899bb5fe6b2fc2ccfee124bdd73", + "0x177c1b1597fc863ce600885908bb770fa071848baa1fba3457ae8a71c96815a8", + "0x81dc1d6e26757fcf10ee0db7c129bda7ec91149ba8496349041d3d98849af114", + "0x6809a5a28260486f81bb670de427bd271a3f350738d97fc7c78a48c179388460", + "0x0d038ee5f7a124f453a76ab31930188993332de250c7b3a128b11abb99439772", + "0x34e0f16f44685d4d1692f4b07e8e91137b207be861dd85956453b9c31fe774b7", + "0xb0b7a1d47f6710e928d1ebb3a690f9dff4f2eec1ae92b33429750b294843f4be", + "0x31133cfaa3f2cff8e8561efb2aeb3a152ab7164637ca400398c9ebb2221279c3", + "0xa8ff7a0cd3530718c55c2a4dc61db2ca431640904b36c64fa7ee496be5b9bc66", + "0x67842d44eba4c620efd82af3fc1f8d0f79a1a856f5cd6283860f87e4ce41b69d", + "0x602839f18d2b049a72ab5ea46e4a485ade14a2e693eb645aaf9da0b0e03ae8a8", + "0x226af1bf6dc5378e31afb51ac60cc9615d1d87ac27f11dcc667d348e9de9c50e", + "0x364305a020ce622012fde1ac9b1a750bb9f29390dd5e9d29a1d4da41f93b4c23", + "0x03ddad3fbe94e9d40a676fc36103bda3edae73630c13f73230039fede19fe6d9", + "0x6d5dfd2b4fc5b7308ce6875eb3a36a4a975e54a24112bf19580a88bb7622a5c7", + "0xc583d0cd46e095a4db6c9a5c2019c6c3ad17e06bba0e12b8c45d06d1e467098a", + "0xbbabc2d795dc43cd46442d05736d685e986b875ea6b674cbcc1a5febe924c48c", + "0x754061d21af86b622ee2e5dc4e240802ce8c0a2e20bea857383d0e84cb2647ca", + "0xf66880224776de781742ba6fad81d0361615bf4827fb6c75b2ae04d834f69b20", + "0xeff0195ff14aca0e97ffbaaaa2244e75fbd6051deb2c8c780db4f8b2690535aa", + "0x67573fe4d89c853ed0f24e11192ba413a73df2f572f09ba1e682160d744b3b89", + "0x08b058cb4ca7b6f9d05546b713a07452959e1a36d8cb5f63b4f6587d40b21b37", + "0x03bf21729cc2dc0f0e03c34c5802dbbff37815d6a03925d8f1af4ca0cabfe698", + "0xfd95219510d6b48316761ba894cc9f98f43149693170a7ad89407994fbbfadf1", + "0x515af672d106539845b2cec711d1dd96147ac5b5edfc251953ad8f0da4f8cf25", + "0xbd08a6a4ef66962af140424fb9578a5775479727252210d2bc842ade5bb53d49", + "0x9e35a18291218e35a2d1bb61bc794742f14b162cbbd4eac482af61ff5fb9e6ef", + "0xf31df1aa2be86bde1d4a36eae4569c220ee45e5f4a636a6aa6072229e8efe643", + "0x2bb168f9adcc3e90e26770728331c4f9c7e6337db82391a1da79ab0bb9893542", + "0x1899d7a292a254a381cb1f009b47ab4b5bbb991934f39aee902c0161fcf4a8e0", + "0x237ddc3c11ddb868857f0715c206a9a9cbb9c3933ff01963637d99aa5ca72ea2", + "0x1850e38072f39294bc0ed546b5e4a1f679e904935fd6fcba9d1b2ae21d48b0ef", + "0xc256b412d0b3e2fc4479ad7b3350ee41359afcbb89fc89d888ba8cec578d079f", + "0x4357827d938ea3e70f13cc6da5536e8f5e8c07a8e226268dffd58f67d9a1f50b", + "0x024549beb551819db404edbb2656bc4b55e13de785d7dd2bd1f5dfb8823e9c9f", + "0x755fea0f3704d89ac74aa499ee2f2f5f2d7863fb46fa96c6b3d3275fca940673", + "0x48c0796b3e39643d6d0fb1954388826bdec6d83b1622abe5b4705d022e7b9b3c", + "0xce8a6cf7c3b29c2a63dcca9f7fff65665928f2ab5c2e78441e577dbbb905dc20", + "0x7e38aec28582ec43382df201b4f8dc835ef5e084c9c3cf65e6cf844d68c110dd", + "0x971632eda188c2e21232fffc76c3a39252a9fdb9ac836f7df3ab627acf0a2ee7", + "0x9ce3e91300766aafad5e7d568d8dfa7eae55b17d76e74557a4ae0465fd4c00ba", + "0xa95baffcee55edc366ce48cedcd113a39faf70deddd4f33caabc161cfe50c4e3", + "0x1580c6468fe144eef18e10b54425f6d2d319002f21463e8d872489d16295de8a", + "0x246e9582ee228622c291e5be71f47649ff2e6aa07dd2ef645da0cfa909e465d3", + "0x9878993f902ef32fe7f6aef7d965fb193bb8d37da586911e10d1f4b579f51b57", + "0xc26df6409cef5c255f6658c75db58d37fba97e62cd5f11c31c1cbc5d7224c5f6", + "0xd5422e00ed41698912ce483ef0f9009d247c8f0242c0a4e3787bd7fcdc1d7abe", + "0xd0bacf252643d68f472ea15cbeca89ca579817b31ec89e55ee0ed5b51c7d1d1a", + "0x837523f8ac17c2e7ec4012b66ddf3c3aa381ee0295af4696c9aa054c9b191af9", + "0xa23ee20bd68da1a63c13bb397f20d5bc5e9cfc5137ec599d931b0d40416ccf64", + "0xeda58ccd1ee93175d1dc4696ff688cfdab02867a2d97e15a9dd4c767686a296a", + "0xcf7078a19b560d85d8450263cc02ed75ae384623bc0cbabf75ecc469a881a17f", + "0x19274bfd68900164028787c74a2187caf3c6c06eed7d184fca719876a8933780", + "0x625daebee2fb28f1bbd34d32a93e041545516336a63e129695d9e23926f57881", + "0x44668b169368dde70436250251bb6bbafd2a2a18ee7e8d187575362228a668cd", + "0x5d65670cd561841aad5819070d10db80b483cd2d0f22d11477025e4f5e0b095c", + "0x9468444b43e5e70b0697de3266c51a36fb1daeec60def4073956944fdca2647c", + "0xd83bc218ff5e2379b87edcc324ab4be23ed3150296bda71f1ce21560571948af", + "0x637d2bcd39313f4e70094ea398ac12b68bb54334fd5a7495b80cebc1446920e1", + "0x278b02cfc50104b7bb434cfb362a704a83f904a45575badb0b0887ca9e1ba11a", + "0x706b5016dd23569f8aec167c5b8969caedba0aa2deced8034f0ed76f2f73500e", + "0xa92df7057830a6b82728af538d9d96ece7205e4da6f725863016c32a5ee3090f", + "0x1a3af04dce84e32196624cd70a4c8dcf63371cfb473d063e71559246dfd43f89", + "0x13c8fc1bb046e9fc58cc71c5a4c7816ec773607b78e406c785265327c320849c", + "0x2fcf7899b3451ee2acda2cbfe5e5dbf63284b3b71949949db58623ae9caad188", + "0x9ea8ddcaf97ace976d7fff0aea40f4c9a66b15eed4144251e462a7091b2b2bdc", + "0x5813f15d2b0c73ea05b9773c1b62fc2a6d5547d25ead2b4152322f8f6701242c", + "0xf8c610cbbcf2d2612f5e5bdfdbcb7230457ee1a9018c2b185de9e1cda75f7660", + "0xdbf80564d8b6079fd5db3b0d33e0b61aa4420b04d0c7102b008abe923a0ea985", + "0x6072e8459300fc5fdaa3d9a1eaa11f2b7fd1236108e4d06898690140ff797ad0", + "0x72efc38c36b2899252aea9c2fa5bdf13325f46d55a8234183497f2465468f6d9", + "0xf2d7c41567a352230753531a48f002d4f9f578bc6a9c322a4f25eb1924e84859", + "0x102be8e4cd59dbe3b34ee259473d193d166bca1c4feab460f239bc66e8a5c7a3", + "0xb4b8c433d362b1d0cc2a0122a42abe7be9ed0e2cc861064517a152ac963706d9", + "0x32e662fab6e2482bc15ce27e6e7855d37aaede267519b49730c29b89f42df02e", + "0x73217234be9ea6a3d6d252557f55ad306c393e944fa9c2887687b9b5b1e8ffc7", + "0xadd94e1a9e4ae4ebeae15e30b39c5ce80ca6b19c4a4502f509710df82faecdac", + "0x22e6dfc8733014fbb442fa34af7b068e5128ff1a1a0b99e5d3442023df6f944a", + "0xb77c46f228a45c67cee97dac13d7eb317982a41b55f806af98bb5db15c106f8a", + "0x1880484801ece72c2665461a908088f80aa6001dd2e90f2d4d0baf4fa6270950", + "0x2319622fcd80875d2a569df620b8bf2a10b71429502511f7f9e38df1092785db", + "0xa3207b5d92da35df4370148ba35536cfe22af65d653724d2918d58ac43b6f11a", + "0x15f85bdedccb3f52c570bab149ee8bb6d715351ec6d840d029780d004d922ba8", + "0xd640efa11d869e8932435b758c51fec59459af9b33e1563661b865438c371f70", + "0x0047981c56641e94086b195d4315ebba4e3cca62711627c8283fdd60c52d21cd", + "0xcfe5bd85fc15a56def9c72b225e7d6c60e1f9a579d39699d7ff5ca7beef806c4", + "0x15748f2922c482336cedd30b4da8046be1ed8e98240b115165961e5ba5929f2f", + "0x6cad3238b8775f2d45e78c0d65cf0fb58b28c89f27a7cda9523b4836f9292f4c", + "0x40c50db47de1c068c1439ff344c998dd6a27bcdcb9d47fd3bba4bf6581f1f888", + "0x10cf3af0f3d5625916691b778ebe41a6e6dfd408acfef72e714b309663faa560", + "0x074e0f0ccb445318900040dae6cf52fd1b1ee5c5c1c3240f9c116c73dfd49557", + "0x91acfba546d13175a8352a4f5c7a1273b5ca3af2e2910565de65dcf17d91f057", + "0xe54e690cb86f7dd04ed75238dc60b5ff015cb8ead7129043932933941afc6c6c", + "0xf4f170c8ead2248857838e4636764c1d2c82bc1fddf17f854e7b505ecb0c645b", + "0xf538856cc2f3adcf074167cbebb5313cfd0ffa6eec2f0bc079b02f73fac99c3c", + "0x32eba67055cea82b5655a64e6096934165918abffb3e8d475af505a05be24525", + "0x27bf933754472bcf88854e5f6d72cf03022d30d57d7af06427209d6bc9f9689c", + "0x215e2cf4d592472dbe074d06a4969b1f86fd1db77928fba7db9cdc78daeb4486", + "0x16be6754d45698e83d7dcf6e43a4bd0ef6e1a26243576ad040066e28c7d9f4dd", + "0x4ed80c1086da6994fdc21f788e9d8daf6bb8e43fc687f085fcb2cf51ccb65fb0", + "0xbe30c14d97e921b949d63c65efdecb90ce429d142765dd883fd160199e0f2ab2", + "0xb7b53ec4421e622206a7e7d86bf7ff69202bc4965aabb7fc6d37242631946cc8", + "0x8760e41c2de8eb3c8a8b347d693f7a390d027f33e134b8d652667796e878781e", + "0x8415be47c2a670f1407d92e336b2a61575fd7fc52cef947b96f573c80ec6cc19", + "0x8bad9d36c71f12bc70710db8eed7ba4f2032ac600f48db99fa7ef62305501798", + "0x44ad472e222574ceca3549b035bdadde7a7553d0ba6fed64fb3f2ef25bee2467", + "0x31893c85e9b187f0d6ab1c515cac73695fb2d866801a7a23a5f0ef5541adb431", + "0x3da85fc197216c93c7890431394f27d7110226e7ee1328dee481b0b5b4df3b7e", + "0x04c3dec6d081ae61e23882e6d0cd7fcfb00d6f8c0a11f66f2db1825c47f65de2", + "0x32b74895a2c29bc6707acbaa0b1bcc2cff209ec54e577e7daa8182608c69b12c", + "0xc57f5bd3cd641b8a02cab4c5fa86a25f18b7587cb41d2a441ef732685cde2b5c", + "0xf56c09568746782df5b122b47c1cda63b37f5d99f044405b6fb92910998066b4", + "0x82a8c2085ebf2c52ad9efc10e7e2052c22c0356b8a218ae5fe3f34ab97cbc323", + "0xe465dde3dd215be27ac125d973ea01ed781451a8cb36dc0ddb542c6f855f9db7", + "0xb3f27aca38297c38b2fee6d4aac9d133bdbb151e9ae93be0732cc0fd92e75ae8", + "0xb0ef580f344d264959a2c8214dce5790b8733b24aa5e5c807130bd84112b6be3", + "0x6f7224af3ca178a8a34068e7f1ee57c124179cf2177937da4fb19791b334b3df", + "0x55366e41a09a57bc7dcd586d46c23a01c1cc7beb735da6ee7702d677e9ee20cd", + "0xb149944e2dafbf4c0b89212fdb7c20783c69c300a019875e69f308cf4387f32d", + "0x0774affc94ba0498ca1bc35e9d29e349b06cf1517b3e4e268ef96630d9a0dedf", + "0xcc1bb31038790e0eaaa302045f2f737476029b82761693bba89bc33bddbd50c5", + "0x168cb8965ecda3d43c1b065dfdc0e9f88cfd31997157946c88c49e8aaa0b8839", + "0x7eed57b259c435f966c96c9b9be3b5b8c6f68b5e5df4443a398ef94b5f0e4c62", + "0x69eeef43d686e59665d47169a0c5755c86702eb36ba610a5c9d11201ca1ae5af", + "0x5ca1fbe3a7a092f8d4a30331f5d474a07d37ea76e2b091e69bc55ecbf02add4c", + "0x7ab7c5be4479cd57d2c6326cd3d78be107cfea6f8cbc1e3f47a8f096d013933b", + "0x75b0d149a1458215b06addac0f4288ead6907893d15618e43c222c2fc568c2fa", + "0x39fa4737addfeb6a0f1023c59b526236c304e1acf9492b86887beec7f0a70b28", + "0x9958c7a3ba2a5147a74893065171bdc8e0501ed80defab68aae07eb96cc0ce6b", + "0x87e4d7be2f059642fa76e54a3d858ca320ef867b26fc871dacce25393663fbe1", + "0xbb454680a5cb8ce5152f1328512e0c3bd94a22b0e1f0ddc8d5f7b718100c604d", + "0x5ea70a803b59d2f6d5dc6a347d5a786a56ce00ba747a7ea19cfa2916296dae91", + "0x6d3967f0ba2ecd047a3c398842b5c21b68297f3f61a2af02ca28a8d4ddd56f7b", + "0x6a11ead051cfcdc027d8f19533f8ab5490b0572ace2a5b513cfb4923e145d5ac", + "0xc63a1babc775f8d3a9485ea60bd4fbce04f43b3bb5d6fbfdf4b28b1aeb1a6cda", + "0xe5812b7b8c906d153c9e150c77d0566d607047d6007284983dea82e669b475d1", + "0x13ac442986301f31adbebcf8ba502aee7d150ca1eca08d2697f35ca3a4494ef8", + "0x093432877ac80e21eefcb23fa80e1ebeadcd4cf5fd09fe379b52945095b511e1", + "0x6341292828cee710bf0b24e46346bdc0edaf47be83521cd26b0f4eec19b9070c", + "0xb8778b3108ce5e089d43cc7cbe44008fea88a12d68ea372b88a4be081d1b0197", + "0xa3057436b9020ae48075ad1821a7cef526fbdd5c3f1c800692d57f14238f9ba9", + "0xe3d7250e42dc9132ec2be0fc91b0fd4e4e22b06971db789c657755a889ac05ba", + "0xab44e92e47ff36ce7097c05596e631f17d21a906e78e2f9950791e895fbf58eb", + "0x57ad76040fb037ac5787e51550bd2f1a57acec5483366065aea511e77139ac81", + "0x8c04d903816fa2406231abeb630ceca92283516634e4d7273d95e381dd25ae4e", + "0x738a1f69ce9e3629a93cfb2920cbe09911449829e807626729408fa4875694e4", + "0x7eca20547b2d2fb42e3b9353b8750ae6d278dda9e519875a3e44bdc1b9f76ba8", + "0x9826dce58257f0439286c3d991eb1aad6099729cb2a61a250497c3eb20041c5b", + "0x2d05bbd1dcdfad8aaeaefd1e65d0eb6cd5270c42181e40689befd75cebd61380", + "0x338215886acaf0f7c9ad9cecf560b5309c000725428f17992e0dfc3db664b490", + "0x5f367a18499370b21cff74dc21862cfbe1ed044d3f56233532bd2d45afa0375e", + "0x1884c701d6edbe717863f8e261f1d025411bae7c1c389a38e174a0a225b838f0", + "0x76f8e7aab4a0de853fc682b49e52969fc6262c0103bedda43770b99a52f577b0", + "0xd29c4810593f34f445b84449fbe6b8fc2abf4692d2dcc3e2291533c56ec6e1b3", + "0x1925ae8525797cab4624c60cb231e6280d73c4e79f3258ef88f672ed7718c6df", + "0x17f278de6e4b88eaf44612a8e62a2552e033540b674ae72afcb4a5172ebf7ff5", + "0x098277e2dcbc79e776cfaa1cd4bc09106fb983a8d4220e8787005f6cc02421ca", + "0x833459947b135e0ef095fde4464b94758988df59aeb33818bc7b315e59f3534d", + "0x95084a4b22d8215a038cfe74fa8780b0d6fa9f47bf287400030b9e0425a03e41", + "0xfddb37f09a7286346c4473893462b467656e2d20cfb5682837c35ee808cd3c30", + "0xa7aa568b3fa0011b3029b82f67c11a3efaeb47adfe727daf9482e8bc959f6233", + "0xce8440b9bad30567f5d6c94f82b2598295b80d66bd8df4a14263b92c456c770f", + "0x40a24d2dca2339efaff29512b33d6993033ee3f9fa3672c1e3024d02d5bf2d32", + "0x5dad1885c423b705ca36ebb6db7ff4634b00715d8bc931a254233eac362d765b", + "0x62da2aac14322738e67aa873c55ff44ca3901c5ad3e48e3253bde958a108852d", + "0xf7a588e7ff319cef28776a3b80152a9adf4228d023d5521c539707379983d2c2", + "0x2cc000a45a4184e7bc255bbdc983d88893eeb098a70eb0346c8b20651f91f8d8", + "0x1b5cd4c9ffcb2d6fed1fd178290e686fbd28623d9eb4540d29109a29496a18fc", + "0x5be937b7a2b0b08151aaffbe25ae55ab5f3e53b7973227b42043ffce2c672abe", + "0xe48e66a417115e5556d70ca8a9f05c3748ed821d9a20c85882059539f65b3bf0", + "0x06b3da5138ce85b20abf8c6c1e5a854e961d7cf4e814d38d7052e790c4dbebfc", + "0xe684fec5b6d088e27a8f91dadc079c32a1141ff36de5f5af4f9004befefa26c8", + "0xb83ea55fe8ae83e7cf461579ede401c9416d3e59b847b8e604ddafba1a4240b5", + "0x009bde1d26e4784805b481d9d5abaa472a5e78e2dc337a4b34ad611fd9e6612c", + "0x8a940dcac2fad2117d6f7f25d4826cc475f33248ced3ad29fb2d71e8b3f99a15", + "0xa1032fd0f7ccf7da36421998d2787534f4977a45f7f1b2076ec12831a1423f07", + "0x5db1a8dec61488e594748a4cb723c898aab5ff5971b573949945d15ecd512f7d", + "0xcbb1a3ca875380260174afbbda6ca7110e09e591b906e2b9f87cfc13850f80ce", + "0x610ad6ff566026cdfe9975e185703d487d259c45053f241ae031bdbb3a473731", + "0xb4e34b40bbb06861c6f096bedb8034ffce1470134bb64dd163030825b02a77e5", + "0xdf37531d9fcfdfe4ea9cdc59ec9681e79d37eb4f0ce7977103720788b1ece4e4", + "0x6e525375d3d675e02a7cdd4a8766bec5ef10f2c10ef7bfd4e0924b510d46bfb5", + "0x6444890948a92467b523080f693d06fa836126d3ab2b3154abe36cd90ce4cfdf", + "0x354752f60d18a1c99051badc1924f1d57e5cb2a042fa925b507c4bf46ea9916d", + "0x9e5aa9880ac22b961a2db41257aa654e27e58de562babf60d8ef5507acae1a57", + "0x850443c9c452e97a47765c97346e15e00260547c97ac07aa45735650d32f61e5", + "0x657d82316fa6b019af0b870f19b2cf20c987883985bbae13b8e33a892fa5e2f9", + "0xdf537b87d47c52076f08a99fc99479ec8160676234ef103dae0e1f511f72d34b", + "0xd4dc2da214329f2b3cd037ad6d99ed08acbb619297df230291cb3ad1d4bebe2c", + "0x558cace99fe3084b926e4f2fd46173b7741b54a1f66376e245f7c6d087cdda01", + "0xe33ae855de442c9127ecbdb0ccfa4e0232356698b04d057251c51e3c6674c07a", + "0x4c68b742192a3e2a2ac7f7ae4f87f4365e9d271961ba1f3bc1dc916c94bee414", + "0xabf5db4a3c3f8a6b23cb85681fe6215ca48f8d1aea54d0f4384f011978fb4c2a", + "0xe11491348af49b5e3ffb94d15c0ce7e149dc622fe15decb62c76c13c4d9c7d1b", + "0xaf9f993b2365467cede05ddf795232f5dcc88e682d548194d47c858378cd4fcd", + "0xe9d2935b83b18fbf4f3c0a29417a903c621aaf868ece03724b316053d2dcd470", + "0x2cbc86ec733617ead05382fc827c07fba8c09c77b587e410b855dfe4b0522e0f", + "0x0f5ddb6c9899b02d7acd4fa90e93afe445fd3d115734946003ec0d3ce8ee12fa", + "0xa44917d4d02be5674b1090a03d6279cb556a85ba61e37ac289d50fd5b2295843", + "0xb7371b972b4216fe7b8d24d148f7f2327e743703b2ac127a0b98bcb46112b504", + "0x22e13f0566d6495800729bca103b5153eca79ce95386ac027c8a6abd3694bf2a", + "0xfd5e266f1cb74183ec0381128241557442be45440313b8393c6537051e453440", + "0xde1fde035cd181a03ccdc34ebd1f082a7c29d2c1ca26f46538259c6989afaf3c", + "0xa851f2197cc6b775f5d52a202a4123237d37ed1d7adf6615eaea0f1345a8017c", + "0x0710dc3e92f96a2962bb744e6fd402f142e821de94ce4071a1a17baf5b209935", + "0xa2c8e415a15bccf5f6800a44963352603c03931262983cb84dcd753bdb14cc1c", + "0x0d1cf66f6d28e5a47421669ac1c29d53d4b73abe94e34375eb02d22856bec32c", + "0xd2215f74460cc575e791692be4fa3005b2ea61cfe8f9878fe8b8179d1653d5e0", + "0x9611c0e50c96a8e299af373945a48b6ff4242f7f5ad7dea53faa5330c721d72c", + "0x5abd5b7c5e30daea6e77e6868114855b0b19dc0822016a8ac99d686f705aecce", + "0x2bab4a24eeacc509133bf7238cf7b3ab4d0cb01125989121704c5fa5bb4cd9b6", + "0xedc8a261a25eaf6229f0ab9858e7cd80909e345380172d9d4c6aa76a28a65788", + "0xf75dcbcbf79bafcd02ff04da481f3300428364d8454df917bf71d203e89ec69f", + "0xa305f948930ad9af77a913a2534f19157c0561e227e72b99462358784849edad", + "0x8294ec2bd34f53feb4e82dcb3ed73b6f68e112e0f0a4db956701927a9c1e330b", + "0x446cfa7b48902cb2a8a43a248b3f492fd5d1aad1255defda33c7ecb8bfcd4058", + "0xab4f8f3da0f1771b4509044a2fb1cac2b83b0897c74995914ca1329972f892e3", + "0x4086eb6c6b4a2fb93562ec44cc8a33f999bf6edb266070538f917d502e989f25", + "0x034e58040dc2acbbd32ed0d2259302f659c353f99819cfb3b1e0aafc857bab9b", + "0x9331ac143a8c54ec35334372397a547de14d56b6d35cc6ae3a78159322e7a34c", + "0xe0ccf0062f88cf6c57cb5503cef595905a1e174d3792b656e1759aa7a3c33d99", + "0x8f138b040b40fb55862855bddc3ec681d1acf4d6af0a0847bebd10bd28332419", + "0x9c9e8d99e1824c992d23bf8f7428a7fddb3b90ac60323c738f3a08214eee482c", + "0xe7383e8d9030a6ac3c735c2ec89d43123b58f6b68d4ba8fbcf9981c277b88fa0", + "0xa8f85d277bb0d7ba2fed3bf38cb55a2b51b1cc89dd6ee6f92bf122acf7516bec", + "0xee537fd9c66dda32bc4ff8f67346d062bd6fe2fed6c4afadddd0db2157330488", + "0xcc5cdf0a47943cd57b2e019492c4eec51721ab47a6eb346be8000d759b40c392", + "0xb34092691a4161e92ebbb43d49c66760fa9b1332c526138d3231efc119457e04", + "0xd2ef17e8c510cad529411f676686d0c26df8ed88389a2df09040f4cf26c2e20d", + "0x114d1fbb4866820216e22fd7f9b14d48690f13895e7e5edc90f48c7a80f2071b", + "0x1b31024b75dbdad6a47afe3c878f4004b1d24425e119b964c309b859b338516a", + "0x441a0ac8958d4954fed11b634980b140594564e7e48f11f5140bd75edbeeb888", + "0xe64adca964605310dd13a33d54c3f9c898e461d4207429344a62e762e001231a", + "0xab268ccd8e35969b9324726998fea7518769b5bef67bb9bc8c5ee6fbcda4f3b1", + "0xedd4fddf4050e42eca7e8652a44e7317cfa5bb7e16f1dc598936dbb102aa7aea", + "0xfba285197a757f007a05dcf5674c6c5af276066adf9984f1a432a8004f9db8b0", + "0xee105fbc2da76d20b4b837f4fc710a0f114cef1d0e3e1f7c89914cf5fd6d74e1", + "0xe8c12699297527745f2032de8ed697a5a8eb448143dfb4a0f1032f4fbd3f71d6", + "0xed81fc5f51862a45060de11829d098fd8d6808a224964cf4e2aa2f6b4a9d0da2", + "0xffb7ce3eefe3f01635c612fc7121c09d892a55bb094caf427b9f24a22f1ec7bd", + "0xf431d94aec15d0478a1ce110442481853f147fa66dc6ece6456177ef951a77e6", + "0x83d0d037e17453d3eb432be0a959321e3bce97d50ffcab80d4033fe2acc8439e", + "0x40a4748a23a4377d5321d3989d42591062d286d7bd76f97b971911429de31063", + "0xc0045fecf9dce91fad7458f37cb7dcfc330c68dac42103faeaecb1c4709d5146", + "0x073721ffad9e27488e3f665e3cd033b0dbea647b625b8215c488ebf9d537ee62", + "0x912accb3fea53cd5b968a7a2d049e1c8be092d5d83b491e05ef659e74be5c4c2", + "0x1d677caeef8202298203b762f5f4c76a9461b54afd34327e83eb4f8ea771dc07", + "0x1ea5ff0c9d3816acc0e6a58d31d069dd3f6ad8290033d7e3f6faa161cf680bc6", + "0x914e0243c9dc2a4c86ec1786f352948b414f898853c757fc8a9e2547389338a2", + "0xa9bb33b9715e507908c12e794e90a38a69f1e286c5276de04a1b8f8b1bdac80e", + "0xe26396538c813e30182de54cf5bba6ec10fb488c99550624bfe37a4714f50368", + "0x2f2044ffe652b78fc28cf7ce90bc561ffb746384d70c827e30b6bcf38deea8e6", + "0xf3bc4981934eef420717d55c6f7b6bc3630c8f2ed2bf5f234f8e984679dcaaeb", + "0x50aef67b732266498bcf703adbc485378c9ccf64a7790504ca6006495e966674", + "0x9142d6fbb3e073af8a8940b35b43f70ea124828b0c84d91c092e4272d4e79a08", + "0x81e91276d17b93d43d4547f8d94ae0c94c6feb08c304fdd7c07d9420c89080c6", + "0xd4dd999f943258ac385f42a1e298d21d81848c20c6333a0802f1e5e7a8a6940a", + "0xb7af557c6d8a1cafcde49bc210e4b5c29fc1f6428384c518bc9d6210d02044cc", + "0xfa2d5bbcb0e2b89b62a1bc4b92d17768f3271d7e001056189a7fc45e7a47a22f", + "0x0228ea0edbdf4e1a6855128ecb562920ea39deeb8e598bf3f9b011df59135db3", + "0x601f05fdeadcc6a0b3acb9a8785cca28ddc0f6e5b23599a6577f17e91461e6bf", + "0x397fddeaf0258d8f53883643fa1cc785f982b2f56c69a22544665f551e8c792c", + "0x6ba7cb3d901795e39a8cad511e53e7c5b504197b82c62ed87e0a3f36c466e895", + "0x1757d18057864f1325d39f1090cbb95da5e2779d5ea9eb2eb56b52e6ab1991fe", + "0x3a2877f6d8101b5bb0ff3149c5916bed7257e7946658bd6137c5948227869464", + "0x48b473b911cd32c779591541750ed2b498f743519f4993ffec5b77bd7c02256c", + "0x11651125c97c73e1b878905939188e72eaea78b80ef3288f6663e32412d7dd8a", + "0xda3ac7be1fa82824ad9436d018e5c71fdbd08b40d017b9faf89387e4f2f9544c", + "0xa3348b0c3ee2fd8e2db67a124ff57df52a7791c50ab358a6f4f4577abb333448", + "0x7cb267740c8f3a36909230fca4dd4d85485c7407519636ed1a0934282f0da779", + "0x31551ed3d5cac9749549b9b679f15eeaf9206821ce670d398cdea801f7428b63", + "0x3974d4e637bfe17b325d8f4fdf95ae036973c0d16b1e58687f47db0a8afc0542", + "0xc513a8c7c55768da196fa55ee1a7ce55801a61930f942b62d770966bb8bdcd91", + "0x966785a5d14a529d192556f45b83d65da3a266d48311fda61cfa146332b8ca06", + "0x752277b84c013c2ad094c4cc47e44166959570cd0f4c3a94eb6076208528f715", + "0x798359588635393987a98b42be25a3907d8d8dfff936df8f9bf67aee78934cd8", + "0x2d40857c4a86811b0b60a1a54b7e35e4168a1775757a520e3a758d13abcbb1c2", + "0xa6b8e3ae9dbdda6d77dbaf29450ede1290bac914cc43237133717371331b3b62", + "0xc0aee7d0e105a028d7dd5f9d9e96073ab4f056f4e8632fc9ad7ee7aceb5ddec0", + "0x2a01ecc7386ef1ab393eec1646d9c51430de8f4b36d22b37b0a2a587d48eb662", + "0x85bbcdc4cbcb78c4f31da419a633ff8469462b62e6024e6cc4b44289e0b4a551", + "0xf0dbf351133edd9e4f1ba96eac7b6d91d3f8d516d7f75f2933d983de1d295157", + "0xfb9c211988c24a41cca966e424f4cb7dfa4654b581eee849df1c7df5ee0ef183", + "0x6d3b6654e95219956d66264167f53ae191a3560dabbf8188e1c4a5a3eb34ba4a", + "0x85c5fbac9c3413667fbd1d4ba5d7f78ac52f06cf1fa7bbb1d0df5c897bd56f8f", + "0x105ca5dd8581bd80f614a957d5344ea3e96e4da95df9377887de3e9bf44e07e2", + "0x01c2c6a1d20dbc484d263cc426153736210bc6a083d21115a3e45394f3ffd09c", + "0x822333b5a990e397d0d807b0722e35fa0b4db79466171d2efd398a4662981c58", + "0x0eb05a126c482deb10ae14c9d2436e98da9de5df53fbe72c3a687ba61bcb68be", + "0x87338063a1851eb043271f59d0945beeefd995b748c9e150e52c06259a75a977", + "0xd28d462682ec3e50cb5c16ed5075500fabd2a39a763186373453201691175170", + "0x0f983048b4a88a229874468d39c1c69b53f4e1d499237332359932def0cdc732", + "0x1a0838de5cf725db81f48942352c306d077cc347fbe0852c8a89c4ee88f28ee8", + "0x2376f62e0ad0743951d40adca8c18d532273174f6099525cba72b74c7c5c8e66", + "0x4f2ddd5741f10788c2d5d10c54fc858c13b7ed76432a145ba3fb4f273fc20cd0", + "0x18b3ac2e545f648071264baa46911339d2242b4d50bd562f5b46d82d57a70744", + "0xe00167ef5c3c05dd6111690a36eaff169697045fd53601d0a53a232d45e5a95d", + "0xf3dbac78cd426767eeb640389c8f7b6a5c25b4a1f5198037bbc07ff6da82dae5", + "0x3c62e36defe281417705309af9da91fedfbf6c0ddb0beba67654f8a08c692b0d", + "0x8c0d6a22fe8ea166478d3b3615b2c2529a8a0edad6fc95eaf2d0b3fa381a1559", + "0x0fe2da5b7f32afe78dc7de4de7c47efcdd4d1f0bdc61dea90855273c347bf2d6", + "0x5210bc09c5bfad1c498ea141047b10cc45a24ef39ab935a6be92242ae135f451", + "0x12e0852be1e60ebc9b8859e8eb844b7fe9ecbba9e93d111f20f7434dc82c7744", + "0x7d4c8bbea58e58378147b90e2e610dbedbb8e7543ee84dbb62b3c84e1addc9af", + "0x13c64d29bf4172db0e0c23edd4471705ffebb95cae3b4cb028d70ed19f15180b", + "0xba7a7eb02098b987bcd8d35c3d3b9154757b18e8f2ff8014017416241dc34d2d", + "0xaa4e7bcf73ea0a0456c6f8a305d020126dde7da9f461571c5b28561ee21c8daa", + "0xe5decf74b06108d0dbbdba0f64a2e19e829ebadb5ee32ff8139e44c10c4228fd", + "0x6d324ce741bb58454944c8700d28f482f090d7385e27f8a173365b013593d456", + "0xc492562521bc7403634617a51d418fbcd7d9dc9f863468d31304b2f2244ffecb", + "0x0fb41131347c3601b364c778409ac8854ee8bba0abfd76b77a1ea65cb96e0a26", + "0x9ba1366ba7a987a343ae9c4c8b5319fa1fbb7501c11ffe19430c527519e890f2", + "0xac529e4a752576ed2da6e54ee3d8cfe29c5ec8f62f3cdddec60eb3cd50b8822d", + "0x1fe4dd4547a58d72a43349933507b3498e23257d72112498bda2e4e0ccc73892", + "0xc1e732399233447e00979580e7e0b14dea76ea5d5ab1406366cc9f93667e15a5", + "0xccc6c85d9166e610c8d468b44a58ef3a1dae284f7d977bd6d6a6676268e522de", + "0x2c3f4899b29f5198c80c2bf08fcd435316df0b2c81d14672bd1c14cde1932a05", + "0xd2d62d41a788c73936318e4d334ae432d0a333b792630889a3f6d1d07c48a5c3", + "0x6e8744b98e4f8aeae67bcf223df4c13608a6c08b28c7ce5e717ff6ac2d1d56fb", + "0x6636382c4f29d3cfa677e0f122c43b1d53f66b661fcd140e77f415674620aef4", + "0xef8568357ff4a797665f8f66fe8b5c01b20d79e105fe7f3bb6de68323c4af207", + "0x86edb9b2aa484d62077b317202a3250fe8320f7b21a6facda5d47e05eb13ca45", + "0x6cc90d7e5b029e51e3ab1122ce367ae99b873770019bf67e00995e87de271e77", + "0x78e6eb235d6a1c7f01de3b4343d646406a8aa19385bbd79b97996f040c3afac6", + "0xebdfd0c87593eaf2e9629fde2e32fbdd11dc141ae0c3a225301d0e5da0f05c3c", + "0x8929127f559ca876ee2519aadce0d1b9ae1c206f7bfa7ea8a8c9f0dc4fcbb6e4", + "0xf9aa168cb71fd53625d981111a8543d6afe0391b606140943496a01b6f900536", + "0x641d70c491be8ab721b8c93f373ac1e6be94aec2766366beb67e1623c6bcda06", + "0x9aba220f46418a12df22ca042a4c9e8780c95ea864e1a3e7b3d323cbbd255373", + "0xc6af4bc580895328b095beb0032936db174eb16173d8c06d15ea9e15907ee7af", + "0xa5600a88c4c0bf4080e7ab28de2e873dad4323ba8228f3e7ba3234219173c761", + "0xba35e36ad9bdba5a4702ce85d703d57747d520afe0a0f5ad9f27d9a0c5ccd92a", + "0x714e9c7062d53d33ad61ecdb4ca6905d2e4483df692d5bae61c103f5a84ed87d", + "0x0e4ffcd3dc9a03a877f46b444c165beb187957f940637d20eefe8e2eaa0bb824", + "0xf40e5117d6f83357880e0bea1dee521ece4a52ab91dc39091046f90b879366df", + "0x91526740f188b2651da771a6406faba39e8510529daeebfa4f88c6b84cc27be9", + "0xcf8f942ad03d2da068e55fd807dfb8b93261a3a8c374ede6aa005f8047406cef", + "0x5309c0fc99575e4ab359c320132c7a898c187ee84ddba5208736eb0f24125e13", + "0xe6706e0949fb6ed08df08db86636bcb6951b0344248ac9d1e2c6569101490a8f", + "0x59ed645c45d83d8ae8107b1b86d3a51454014b13e2be7bc552021dfa2ec6ddb6", + "0x0228e4b6d181940d617d047380f162077d902d31e2b3dd6a55c458c3bf34d7f4", + "0x62cc5d0b8953c54c66a6a9a48712659b2ad0221c287bbd40f5ee095a54cc536b", + "0x7537e9bca1116befd59f9a8c8aeef0d4b9639b50076b4091a34142c77116354c", + "0xa08b82dd5553afc8ed10ebfb5ae0e8e35c61ca1e66521e567b27c6e324a23e22", + "0xe641d9dc7e4c05165b6b5f5c2f4a40eda5007a14180b3b12a158c28d2c6b1f2c", + "0xd21f240ef671f220f9070b7c049c56a2cf638a9d1c82743482d2d1598f8bda9f", + "0x14a5327ef3ec51c8bc645245d015756a80ecbf3c91c28a8602118eaf242d7485", + "0xb58ff8d8a7dbb3fb76403173ea00bb42fa383db706830d7c286d3b419a356e0f", + "0x33cf59e31a3fe98aa593b0bbda2e24bea2b6f65bb47fe66c519fe8e9857458fa", + "0x3346f4456324cb3427224173dcf0f442e5974bba1c686049ad3991eb4c49b5f9", + "0xa9ae80845fcf699196feca67e7b29782757f288c42169bb84e153e5bf82fdd66", + "0x813a00f838efeebf7758295c1704ca75bb71e714cb750abe6ca1c5391f8db202", + "0x44cb5dd722c03a33e1e942b6f156530d6bb38fb8b600e7aa07c65ae3aa87f717", + "0x6dc9d126f75e46c5182b5251c15f6ca6823a0c66ee948c1829a9c2dbf0b6d542", + "0x06e80d8beef19c2b2a1f1b66a7ca278b2ce569c6c4fe36566edec378194d0114", + "0x50c4328b70841381fcf3ba7dc19c687a24ffeb5872f961da451601937de5dd9c", + "0x57b39385bdce3077aedd780d020ba5076252dc32315dc9a00ed865aa18060590", + "0x174632d70f7f7b8c7b86ac6dbb071a3915889a4651ef6e5641e49781bf5a2b64", + "0x34834239260d219d26ca672632078f8cefd3d33a601611cc57e00ca88781bb24", + "0x729cedd6fdf0b03650c85bd17e1ab19211c7523384fdba1a877f5120570e6024", + "0x675f603aeac26bea0bd037fecf4a0806237198caa381210c0e0128ce607177c1", + "0xa479b746e4a6ffcc1f3b88bf90516fc8f8fb1a8e31f3b31e6714fbfb2e5a9cfb", + "0x2c104945c0d3f571ca8bfb93e1e0b4df585bd2fb3989f17fdcba9989026e3c58", + "0xc9b1e2dae99e7eac132fade954dd7bc47a1753039a80d9cd18d9cbf432e27f0b", + "0xa0425e270254a3ae023d1b8e81d6a2a1590cf5bfeeb28f774d2ac8eba69d5722", + "0xf895a2262cdd77672a97fe91c9f206c23644985b45e647afc1d47dadf3c71e30", + "0x860afa88e1aea136ada99cb366fd6bb5809e42bd74f7038ff8b62dfa20a5fc20", + "0x110f0ab3233094d1ec1aaa4f3ca68c6e27f633eef369b2fbaacea2dd5b34c5ef", + "0xed2dfd8f149c9cd846086dc0f4809f96950e5ede52eddd5a1752b4142cd65e79", + "0x010245a296091d60e289029ed4cdfb66f873e753844389ccd9b4d0a4184c468e", + "0x38254ee9fe8c87f68d911adbe9c476daeea88408f6489ae935a91b7c3a4d85e4", + "0x1bd6151f34972ba9a2748c87bbbbb1a3cb1164df8a4f693ed4a1f915fa26c859", + "0x6948b912f5fb2e4a9cb521cf4d9f8c0b3f47a79c3a0b9d83c455c434537e2fcd", + "0xedb1bc8dddf0aa19d58242ef8299bb08089efd52aeead03721e0e4947095406a", + "0x9e908217a44cfa1e4ddd5dc1205b3c0a6308fc3a04f38c40a813be4d081cdf6e", + "0xd5d59b87e1cd4ab9370a4de389151a7a258f2a869d7406810541b403922f5652", + "0x622559841e49955a372296476d629085106f14c0ca14bd559118a34ede50b97f", + "0x0ecf488fcddeb001cc26803d0278fc74d26f8ca2aa8e20c8ce1127be4f479099", + "0x7ce89ac002d91331444842fdcce893ea3e5779c10a9292ab358ded887efb7a5d", + "0xc0c2f1348de385fa4f7fbaabb23b3ba4ce898c3ee5c5c5b2157be4366f85820b", + "0x9a1a5c308e035b8b7991dde5073d5fdecf0d5507629e08702686edeea98fa364", + "0x737e490a2fccacfa704b95558ada42405821d8c5032a8cf24f5fc37edc09f94c", + "0x886be102be8cc3737ddbfef4f92727cc1427a3d60a53811ed1e19931310cbd5d", + "0x3c492b8a2f16bd52b73afaeecc152d3a3f8229fb2d5287af48fdd2eddcde1549", + "0xa3fc3357fa519de234a26b6c57c67711dba351734d73b2853cafd6e8129e3d3e", + "0x544740adc17bfe63b1bf793a13c24e63afe1b7df2f3e3189948533f50b3f14b9", + "0xd8b50e55187f4b801b76dd3db972472f6517ed96bebcab615de444bbda67f6d4", + "0xc714652b3356f455bfe89e8454aaa5d89aa9ce7826bcdc686574bb531cd297b7", + "0x71ac4fdb56337d0bc61fdca8ee115d251bfcad4ed417aa0d84abe6f0fe8792a0", + "0xf5a76ff97c97e0a906d73e082bc33a5e8ae0e12c3a4365860661c872a9063170", + "0xfe917cb962caf142bda8f618392bfe561e3986c1e7b91cdc6ad88bbb517807b6", + "0xe4e239fb05a29afca7ca572c6aeab9a2761e6fc41b8e98c1e273b06b680e1569", + "0x2924c6c6d7dbbc17d4b17f9a1d5a61c0d66461c43fab399b177049f1e1f05c8c", + "0x320664e374f377619ebd8038141299f06022eaf7897992b82826686dfdf66e1c", + "0x4cbcd97f73e21628712db82e06c467be1356da1d2db23c3c3cf532a71365be69", + "0xd5ca98b2840dd31d44e79b11b099e341b107aaeec813c6157b544576e54a19ef", + "0x2e214329436c5255eeb30c45ed79b365b2bccab12275200090b85fe3db449181", + "0x621e8b6c98ebe0dd7ae36d74d43d3c51944f91a89a54a6e36571c41668f5dd56", + "0x92901e96dd053e9a639ed809384b64a83e39d232df8a6c1efb71048082d7b06a", + "0x39fd20cbd2fe8f1a8ad8831123c0a75892ea35e044855d11d4c967efdfa542b4", + "0xbea2ac993e2b3b58e71d7a472e996ea838954193f608c41ad72d138aff11b58f", + "0xb0ef450d86609684e82d3e4cfd92f77de59f15f945d622159332c00c68d934c7", + "0x6b4a9caa8b3c94ae03b0aa5243b55529a4972814e556f1256d735c0ce9e4ff9b", + "0x2e50844ea0a1b65fd7130aa536abd3b195d0a6a4db97901e4bea1aba9077020b", + "0xf3750735dea99b0dd04aa35cee2ee667bcf6bc8fe06d2e42e76d3caaafb772d2", + "0x140c293a5b13029aad1f29558c4872938574a04a96428315d923f894514ad203", + "0x798d6e1c9a49011a1f2b73406b26e613e1e4a6f2c97db7b7a86b91ecf9733edb", + "0x5ada66647e13802bfa9e504e0497b1c1b3d8358265dbdcdd72584217505989c3", + "0xddf2b1a80ea26dea6bb7955bcd7a464387800a306e160a0521139499a6150fdb", + "0xaca3cae742dbf1fd84863701348984d475646396f6b26045027f0d68cec2821f", + "0xd26594a7361bc341f87c063ce38bf6ac3fb1300fab162d33e4da631eee08c73c", + "0x19434484cb740f59d6efb2ee26796f68d8b92fa1b59858985431cae7625fc08b", + "0x8f98964b095bd61f539134b01911fdcc32db43c8f17878da1d709f5daf575807", + "0x343fdce2e4a6202dd40dc7b2006b0e0ac194dec303ced05c9b59ffcaf0a508f3", + "0x4ca7e49efbfbde184718197a53ba5aced8e69721d45a99a55d868ffb50de443f", + "0x3da44ae694e27906a4a7262037052c0d5f73c88bb2eef8347086060f73f42eb4", + "0x50139d2cd0369919a9622041a36790dcc5217995c1bf54efb8c481b0cce28a6d", + "0xffd685868a479c273c192af982bc407b394f3a25bb39e039c349869260f3778d", + "0xd78a07f2942046c1c5c3d564f241ec3ece5fd51a086392e64fb32cb8b0e0c963", + "0xd6f345c8b7441cdd92ac4e46645d11fb653b9511c34d3ffb90b5f67f5e8efdbd", + "0x4740d4776029c2a9d99ec92de0221d2696c716336667c37dc27dbad1bc0316d2", + "0x1badca46f7bb7cae33729b28092bfdf393e4cdbbcd9876ad04bb9cfeb745b031", + "0xa4496232bf9144dc6bbf29d1d6236f8bd9c7ae72128a636864d3037dc9849e54", + "0x7d22257c940d601dbf2b55ead7baf2222b6f4791ef47cb3488c779d5ca1d15dc", + "0x81ba1c018ac24826e173dce1c5094aea984babd5bd1fd0745e9ee7a78bf96850", + "0xdd378d2011886419f67b09b38b635b926bb2a6375eba843a12e27d9b73246479", + "0x2d657bff6a184518e527595abbecdcea61c76a16919876a33cd72595d61af953", + "0x367f64fbc27e1da5cb6ef8685bee3286201f8e6b692b438bdba46d21782e6ef3", + "0x54a4f5fe2a43200f144f6b505f4e19d80c680720f70db4feead1c1cfd9e7b977", + "0xf592cf2c002f770868e3d34a2f0ef09a7b4eba12e6151fe3d5f82d2c73bd68ee", + "0x3b70fbcc1260cb638ca544383ab6bae51f3aafb496d2fa0c3bf3f480b3a44058", + "0x1f023a8d026c0f868c2f97f45c4d98bc4245985edd8592082522c3d1b55f1192", + "0x0efd126c05874a463b8796196dd0c36b6e401243bc4c22a89efd12da426be5f4", + "0x510b019fdb2711c840a14afc025f2f7e7879bcec5d72bf5706a570e2b4cff628", + "0x28fac9dfd8108f4396e3a3882f2091ccc9664a6b0cd4bc417afb8844e155747f", + "0x4e689ae3aafde1ee7695ca3a183ced0bb9b732cd16dc7ff8f2ce4a7f8c8b5217", + "0xb90327006e5acc15026e194ffb37bbcab1d2184a757490f063c3a8e5b08cd66c", + "0xc5a2db9e04c8dd52e7a28a6854008665748c22e578b5b3e3e7c6589366009a7b", + "0x7d38d634f112af7f4c1f80e8e07697e4fbddc46e7a4e92c13cc3f11edcd147d5", + "0x16f6386525283c8c660dfe59d15f8d30a414674637dcba541cb40c6e1442d976", + "0x60fcc887e79ed0e42ade74faa9fcf5880c0066ec28b8f90db3511ba2709bf029", + "0x0c281aa4eb88cfd8ad7fd1561b1333f572a410841808d6c666abb13dddd81700", + "0x66e68320e5eef0618ec3726582c89da5add9b92e5ac80d87030f895caddca802", + "0x8f3f6b4100f26fe8f4e655b8e3a57847e11d52446516d3ea20fd6d5bd98ba929", + "0x8b853388aea88e3a32b015f581e6dcf5fd3d188e6e8df23f10dd6b131e2b31a6", + "0x2b29344920bb4655c6893d88207403cae08d4b011f5042e98fb7da33194ecbf4", + "0xa9b5a03bd709a3b21592e735c54fc3ffe57d67ad654d744e699724a189ffd51c", + "0xba8ec96193ce8c0789c59ba47502f8339b3848d528fe6a981f31a8d66ca4bf38", + "0x17efa720b35d2e86d660a54af0d3c1f622687cb696c3188c8058fc48a60dc70a", + "0x133fd2e51726316284a6607700039a4085605ae74f5cde77b6c935407a7ee0e8", + "0xd94e6246970cc4029cf19b46e3cee3966d83a890a024a80b2cd703015c639ae5", + "0x298e2c96e68c1e3392ea7ab98463767036e94f5c92311ea276da96010f6a5bb4", + "0x82c0e4283e1786430fc19cc6376177bc6335f39469d8fc3ea55fa9be1a236643", + "0xa5871b65dae2b29a9c0ef15ebd82af4bb33d7773a353280a61eccc8fe064cb40", + "0x71a62b717e6ebeb1474ec19c5c6c4fbe57b36ff6a41526559d20361e9897e434", + "0xf18f61214a4453d9c62fca6763719824cdecbb411629f1f03a7df49f5af145e0", + "0x56289e2a11c4379523d16278e02061dd23b8cd33466bba995074666a498fb3db", + "0x7d0aee20ffe0ac892b2b876c47afc54d9eedada7603ca9bf2c4fe808ebc215b6", + "0x48c3df7c623bdd3066bfa7227773bbb3874ccfe26867d12ab763c51472ee4377", + "0x12785217e389720dec72591a87205217d44b21c231e86e21de2fc2f23a775649", + "0x01a66e60e8baddf09e05e5cc325e76b18c4e9a44884e676294402e5bc889b885", + "0x10eebfcf5d37b585fb4e1e9886e666f4c725b029bd6fd126504e9be812d9383b", + "0x69e92d7d094de590c0233981dd0e339de4c610295f330d03f20d074ac05bad4d", + "0x45f8aaf7fa27cee793328f52e22936381c294e7838ce1e9d6c939cd26c17b526", + "0xde7674f2b9ee8b950b472f9ebac020e433f0ecb1bcbda86564b9c7015f08c759", + "0xb05ed6f85e1f525f187b0f1cd8bde03cd6e938e749fd58195e767e6c815316db", + "0x9c97a833aaff82d76f7443121e602137f95d24b80d849b057ccfe92e5dd3a57f", + "0x3758cf8cede19946d1f4e88e5e783ed6018e5c1cbab76ef234a5200ae3c60f79", + "0xc34e627258d51650c1b48124bbeae30e1b526f8af4e33df42465dc2386b78dd5", + "0x0d82c51cb233258e4223de1bb8a0abebb7d076c5473ee8b070a743c5536c19b8", + "0x49c10d720e3114b8edde45aba56a208a42018d576b26d3577b3be37aa54d1785", + "0x18f8b91980fd2dca97c30708ea89c8714b21749572c3778539a5f147628aaad9", + "0x538db37001bec83ea4c3f382b1e16c65fc25af40f654861fea3ca885ef401263", + "0x2384aca4bc5a4a18c680b86d3157beb306ecad3af2417f446612b5fe3c791b68", + "0x2023b51ea9bd5d3c4c816cd8806a3c75b04aa3e5484b4d1120471b076b4aafea", + "0xa1fdd4371a736bd1bf1fbdeb6734f74b6952f83c38b0c651d047e03c312f8cd8", + "0x5b98f41f67ed99462aa5c96b3907ffe9c7f3eb3cd8c78ee1b78ecd545ced6a97", + "0x700cfb4a3a6f623452df57ecc11363db17e0035ca2cbb7ab031c0f6efed1cf9f", + "0xfc867a0c628b69b5c7b3f253a4682229561049eec6a5ec3045d6ffd80e8c33c0", + "0x79174d473127421fd3432ef6f91e91bd22543451e4ae3b44cca4500a69316c33", + "0xcc21927d610901a8eaeafd8a5e87b2f6a20eb92be5b9b785131f825dfa474924", + "0xe1da7fd7781a1ae76be9b4124b82001e70a68769cdcb92a9206a53b2ce90bda9", + "0xdb68ff36807be8e17e257f0cc4b32fd3b557bffe7e557bbc7375d8cd47878170", + "0x81f74add5811c9066ebd534686e3ab186d5c3b6367285b94ffff7a6abdccd626", + "0x33ca011f1f3eae276db88fb06077602ec6b4e70d2f7c2280b5158f63a4bddb1d", + "0x9d783c782deb704a67a6b7a2962c5fec3a99cfe315eb3049047704fa144a852f", + "0x15ec4bfd7117b8b72fc4fe798533f479e5b6903b8f7b099b245e474af4abed2f", + "0x52356d7e7edc0797881c6f42633baf48f8acf1898bfe0ba0638717d951d5cdfe", + "0x35318ea6845e8cd0b6761c3bb13f1d87068d2a4777138604e30c66cfe2fc711d", + "0x47ce8273ba3f55cd8d32d7dfc186f1650aa22d114245c2dec74c823513c0c828", + "0xd2138635604712074d13235dd770739739aad4dc4ed418599eec7d1abc738517", + "0x9ad15b0c8e1e6c1ed87214c6a4c54b8daf74b167ce93c582b5c5ad7719a1cb11", + "0xd73c66d303fcb4105c74f0adac81800864007ca3341257d3d76ecf05759e967d", + "0x2bedaa6a4c025ff820f29beff395f3f1f26ec9f470837ccc6cc471d084f609b5", + "0xb4e2f728eb97ab993dfbbea35e29e0ceb99ce591bc626c107a837c79b4ed95e7", + "0xfa43a1421ce5f343597224fba3c31f19af39e6c1e135baf05d336f8ed1055995", + "0x5f0c9d2a5e420765eaacf5095a130246c1073a8fd78d3320e0d3cb6ba8e6e265", + "0xc4c90af43f85d0d3625e1e6ec544ad01df4c475d438dc863715873ac176e1c6e", + "0x2da14c0c5f7965208e329fd5a0a3e2537a1c865b7cb962318a269ff1592c3700", + "0x00240650509697255e91d096e982b7336421e847712e939711d96121173cee0b", + "0xcd393aea05c418c429a621c8850b76b4953bb6b8d606468ffd4ce420aba5bb9f", + "0x5e7458cadcf01712bdc686bbaa341a268ee71491cb77e78b1d78b2233337f3a3", + "0x0041d6e1ae80922d738dbaffd55cd704794aaa029428d0128bfda4bc715ff53e", + "0x6aa0c978a70b2fac14d3a24853e5c67ffd3f9edaa9a0d864cfb1c0ae5df0a271", + "0xa4e0dd95f5a6ff34a40ed96f07c61b4e9b38f65bfb03ddc966ce3a7189a396fd", + "0x1b911bb521476809ca6a74323f90a22d6a3545a6a30b25e2f5123b1477803263", + "0xf2c560ad82099e25cd1f813ff9e71fa4d26e514c2b4966a7dfcb79f82e0cf6f4", + "0x47307f5fc4a9f9df585f7d45134925ad67b6ef360b8338a3f454d6fbe72f623a", + "0x0c3e231bf6f751ec47fd1984c3780d91dff083bc4ed26ad238d12755f6456c64", + "0x99841f294a068e8ee052cbf88178ad3370fcc03e5c4694160943fbb53ff705c9", + "0xf417c7885b3050f1e70bba87b9fd15028a635f5184dd21517fce71d011f9b0f4", + "0x90f57193d20072c05b8192daf3a804d66748752cdcd61f4f25df9c6625527aab", + "0x20aa12aa1d19407786366ed495e35487501d17254d2885b8dbafb53d363db19b", + "0x65988938ba691e71ce936c5fa49ec59313ddd015060e94010470da1293de26fa", + "0xa34042586d7a1367fe46e859b9031a240b0b9ee850441148bea126867f27b0f1", + "0x1bc03f654a55bf9cf655422c62364d1c4c17b6724880c3814fd170b11a19d2d2", + "0x324c7b0bc16f98f333c098b503b8647661f28e2b3a81dbf0974871b886558a47", + "0x3b47cb8e7d6bc7d764533bbd15f55177a8c39b222f12ebcc52795e07d4ea406d", + "0xb250591c4ee58e504eabc80a28797c53274970b5e8ba7713f6d0682a36277bb1", + "0x275ebfc34c7c3d113f4b6e2a45fc58681fd6743ff9c20c3b49f84db6485a2fcd", + "0x11f885a8c5b9206908666a0909c027ca4bd37f1b908d36b62902db2a15415c49", + "0x22df0be48619e2481c101d709153f367b4bc2c3092161c57ba2ba6dc10c1d445", + "0xf1a18e095b62eac62f1977370a84fb34d3f70c11006d8f58393a0845a14a80cd", + "0xfd6ef13901363f6ef37ef4292481661b3cd6e34b6a125cde14051683d380d934", + "0xae38cfcf5222b3e6f02d66f400ab5c0ae6c6ac0e74b0860c19705857ecb74422", + "0x327f21a766133e9aade0e7ea2a53c948b1f53f3f56114f2672b75f2811587468", + "0x4cc927c4e98bdc17eff3270c75318fe41159c4f4a88965ca503f68448d7cd3a5", + "0xbb6142a5b2a660bfd8a2268f72bc5695175b7695394eb406114fd72ebfbb53d1", + "0xdc35b33b0b921f6132db04c3277ccdaa3a43f6db624625f7df110f7b33833699", + "0x5fd7db146b910e83e9369e02735f078bfb90e74e3c3f7b36dae1a8e474bd77e5", + "0xd10eb1f57c834673dc249dee99a9c84479d00f0d78cae8c7d028e77c6ca6418b", + "0x3a161ba6dddb388d570d8540665e9ba0ec61bf673a79ad17824fababf63db1b1", + "0x77a659c382591f9f2bfa96f5bed62f0a0a73395cba338c6c78547eb75632f9d6", + "0xf3461aafc3ff131d14974fce54a604c0ce2376599b794dbf69702495556865e7", + "0x73b26d53dbcf7e5262a19c1b35c4ccc6705d564d7c79f3235a15538c7bcf979c", + "0x0ec8615f52b1dae627b50f3d9dc0e4abbd8e3cde93faa44a527d52d8b1622157", + "0xb401a94d93bd2220aff7afdb8c215e50b0ab03bec3ed8756badf2fa70bf0e742", + "0x156823eedc15c0f08050d44e46ed4baa66105a15745fea0a258e16cc300ce844", + "0x3a46da8139ffa06ebf4613032c88cecbc0324afdd3247cddf8bd6b78cb860d1a", + "0xaa6d40608918fe77a2f069f4d35d59678e4b91188eea4925da9c6fb625f38eb5", + "0x16121688cd8688837a9d9f25bb1ad1b0431c3de7e6b6124846c634d697320fd7", + "0x3a0dd532f5c33630da14adb2c7adb27f102f9e808f396b1968e1521f4a60c8c4", + "0x0fd09fc7407e85d6b9b49ed30bee7aafbd097231bdde27e56cd4850f0ed3a6a7", + "0x53daaa6d45b0c14d423f2183b6021dab672555386da73d5499024917c6859075", + "0x6920c2a773f2a363cb1375a39ef27317193089ea96b56b7e76712f2ccb60f700", + "0xfab431f3cc7ec8be4800825bd15e0373ceaec4c01148700f3fba370be9c079bf", + "0x2efdd2464fe13bb5081efcdc615f172392b2a2d177dde4c9ab012f2f05dd99b1", + "0x175bec92215c2cea0715895184c9a3c0b14e04a030f853090775112cebf0b894", + "0x89a4e51e72d0e93f4fffb21215145799e9d51821df48771b468646a64f675a9e", + "0xa2722217cfa0c1ec9b24fc7fd4fd607fccd98398a93e25122a71715c7d254335", + "0xa53e885baae4fa91d3763472c65aabe0f2b3e91553e1c50c0b49c951921ce1fa", + "0xebeb3598ad70dcb33c715ad4acc6c2a3b4a42266aabe21d61d892f13ed8eb04a", + "0x07ce04f9d80425ba88ad77d54117352dbe48dca24a281cdf7af3c6fc7d253e6f", + "0x9f6958ead3da59b875f411794e5990d6d32a360d1fc2d341866f34acd68e217d", + "0x90fe18774189bf1c7f02317b8dc2149eed9d2b1e77d3bd1e77f1c25ef3bdf116", + "0xdc5ee72976598dd75ead3d6502b982256ab7eecd48a9ed993dfd8355c6ba44f1", + "0xabeaa4ae625c77492efe5df4fea447f7501b2ec397214588fceb01700acb5634", + "0x5ec1bb01d8f46abae5d96b5eb232ad1e85612da11926afb92c91f2502d9f366d", + "0xafd989ff958f59b849da95b5b648b81a31a9caae71454bf7216b7ec052fa5f88", + "0x5ed6bf9e8743af877a4e835cc87612c4a87fa0f2e26e290e2303ec579db9301d", + "0xe97ec4be637d014ca32051814c80b2e61615b13801914e95688ed99620bff562", + "0x1485f0c3b8679b617a504234df01324bc4ba365c7f1b40269f77166677c27e12", + "0xe0a89dc14d0cdc06d0476c2919bac794001e44c5e55b3e0a12f15e3a7fb16754", + "0xd3c9acdcbaa62bc54fae5cb3d7281b2ce1300bdc4d377d26e6d4487278f51e8a", + "0x566d18af24c7a2f5e2e32ebf23313e6d3cbebb68ab1a53c84d5965c552e50036", + "0x9ed8594ff2559859eabdf899134499f7157d96865b3b045ae0eff9a3350c0d46", + "0x822fcac36bf5eee82fdf05e9a6c02bff26f5cd0bc97fe70687d22cd26d0be49c", + "0xca309c245efbed11b629982871ad5ed025ee6e6c478aef60a4b8630df70a92f8", + "0x688a6c288545e473984a97c99960369b3933520904fa739385e9647ebeb86f1b", + "0x29e28aeeb39c0d21c34e46de8f3efb13822144e05f1b21a9463a392395e93989", + "0xe32448f7fc79f2f6187772680c249d1cfa3f882e11e6ca69174e28c791bd18a4", + "0xaa45ff54759469bc525b03cf9e27dddd697f62857eaef0f9872a7a0679c3e974", + "0xc8351a55f33e5940c98b7e5e444a5fb68dcfdfd75a6fc675c11bfe41a064d579", + "0xf0170f74b18e3f7d513b5481596b2ab8655ffe39a10ba5afd9f9fc61011633eb", + "0x50f4546bd0082e000bd6093171bb6f29d27fa7a332eec2d7e6a1150054ecd3ea", + "0x064c90a8b5897680d0f8ec367ebaff5b5590a6c8035d9dbee4eb60ca4695beb4", + "0x64bc40c4a38d666ece23d5eba1219a2c0157172986536ae90900c95af184b648", + "0x4f320f7a7a39b6c45e452d7acc325902aa85709ced8abc202ad232da4ec6c13b", + "0x67f2a145aec2dc2caeab5d79a0cb4c83935016cc6f928593a25145826b611997", + "0x2f333103dbe553d535f4af826fa77fd14a28b520b7ce97d56ff732f88f537b4b", + "0xbd1dd1b229388876a3d233b971be1b845cb4d0328a3a04ad463030b4fce9231d", + "0xc0c56a7d611caa102501cf33276de9dab564f3375b937b927652e00369e2ceed", + "0xa31a192203272ab8458b1600707ba34ecf0e7560597ee950d802fd154e76a768", + "0x6ac291592e0fe690540a575347c49084cc35ae15e7c4e449e8940a146f8ba3c4", + "0x5940ecc0772a5f50e0658d0d7143e6b521594db929d17f61d1fe528b1df730ad", + "0x4b5691ccf4bd1c7d8f2b191e944483fc465a2f2dde369514b34f8f9c9e434a01", + "0x16b25b151bc12fd5977b933d8a3377a489dd8f0461e64a47687898a773c77f20", + "0xd384457a25f319cb7bacb397b5319aa529be8bea06f1509fdac9874e78ba861b", + "0xac60876991bc94461a9c2be409aef1f718ba0b0513850e4ef10d4634063ac0c3", + "0xfa1f3184b40f94aac9312bf28c7b39788aa3237ce83e0bc37b3485b32725a7e2", + "0x223a4367ade0b54123485c17d3247694014ef1bdd6abb07ef8d8e2d51232ea30", + "0x97082d02631bfb41d3a1ff6798232c25af97fa7c5709d7d8bf4acd8fd63c4732", + "0x811c4bda6373861ca9c5763f3554511b8e548060eaf85751e33b59a802eaf5df", + "0xacdc1bad19ab696da1f1d5e5f886d7447026b7064305e891554e761014e2fd88", + "0x7401fc27f15e7c0ea20b1b9a1ae544c3699e745f6e3ca4ddeef1fe679c44489f", + "0xcde817f35190f307d2c0ca44938c09b3b32bcf8bfcebd735f11436ca591832f2", + "0xcd19165c7094cc5136ad322e6001ec9432c5fd711f0e2d90a62d1e4836b87665", + "0x6e0877ce7bebdcaeba3a02c4cf8907321936a615cb18f6c2ee97c09c6ac347e6", + "0x0cac8a08eca7c02653c1d206c030f7fdff11a4c5e55cade5fa2bc18dc0e887b3", + "0x2ecd8743ddd0cd49e09abf6cfcab07be796b4d23b6d30bb2dbe5713e6503a747", + "0x1cae360658075a6df6393ac64052a3e08c7927545d3ca0cb4505a13e7af455cc", + "0xe677d583f98feebd9f9458d335c5279523d571cf09e058a67ada994cf5589d46", + "0x5f38e724a449498653b84fe543d4fe39685b8727fd50d056257213d18e9773c5", + "0xcf689c21c0ba342d3a04bd7b9e30b1f2aa2674f24c04ea3a5e514b23f26d9809", + "0xa0a7041d550c8faed75086ebd163c22ab07daa109f11dd7506e265f46384945d", + "0xa5f9f06132b381b56dcf1e81282d9f5d3923669be49ab83fefe8b8950c297336", + "0x53da9f5721eb225a18be4646a2bf8571371e37b94097c90df8173173f0a7d3d7", + "0xdf100ec998f16f4f327898c575e5843ddc9ecf34c18262edb47635a326747a16", + "0xe81ec26647f2bafa5a8c09ec8f8ed305d34f3986a4bfc1ddca643afa14437c94", + "0x057ad3f4770577d7fd55131e2de708e3eb5d67ace0cba78da7434b898229f1ff", + "0x19b3c95cc31a706589edfd9a12578cadb9eeda36b5a1d2385fcacc2ed0005b67", + "0x80e5289d8caaca811ab36d6618b94bacb69b5f5347fff77afef39f7363d0c9b1", + "0x2226edc9a5efd0cfe25fb4790c42b69bfbfa39ec3ab53dc56909066d1168d7f7", + "0xc590de9c6fd97bc21d10b3ddbc78afd54a256ae449ac182e006d406f428d1461", + "0xf4e386022c5615e48469a48e977249cdb303c1f86f49ea8591a81c9e896d3c3b", + "0x48639f90e80386c36c368e334344f7f238b8fed60f0cc7fba20b3515f7f45b06", + "0x1961769fa7ec8ef3bef7a6c6deabefd0b488232b6be9ded1f96268d5fdf6317f", + "0x324c3f11f795649fde866ae0d520f8ad39fa49079c3423a3709dd6d5deb27247", + "0xd80a996baecd938ed9be4520956f64766fd02378d77964ac132c623a3992bf14", + "0x884f3f41474e3cbb7a2c2f53b837cce04d3ac9daaf67ca0d66472092c5d119ed", + "0x07058371d60f70b1285764a1c928fc9df4f3edb62052432e130e44e073f1406f", + "0x2dfd131b1a8198d4e220beb0fda7c1d46cef1e805d8725b411ceedd164e0c3c0", + "0xe68f638aeae7640574529a5e97683a4867ae0e196fca26dd1f8d8e5593ef5f34", + "0xd6caf26d56ad241a8f6d75cf2ac5d6bbf9d3fb51d3e3ec803a46f549d2f81a52", + "0x0da60900dd96c8de775f959933ad9a981850b8a657a5c7210467b320648afcdd", + "0x27018311d9d37a86848a63925b8450e94489376d69dcbaa5115bfdb4b632f02f", + "0x799475d2902522548d70dd3aca483f7d4fc0f10c231eab99a957513151b9e5c5", + "0x8450e5ed3c6542a84f24d059815ab2801520338cab8a4c724c389f81b9a48c8c", + "0xb8b14f256ee4709b4cccebf5c92363e38cbeaeb33f475288b4be6d604c37e7fd", + "0xe77160f18d9f35c0eac84d6982f76fcaa0840963cd4857092ad792e97859eda5", + "0x18d950173c69fcb5f6af3ed8eef4a8ad440116370a23f0ec61f2f7ccd6ea7ab2", + "0x2a8654fba53186e80f517eff2ce5028045b05f9c48a52eabf2f3a5ab8bcc5d8c", + "0x37fb81acb856f00d3ddd7fe81dfd6a43fe681fc423838de2c36a0d049526a63e", + "0xf36bd3e791a8e6f4bd03b31f395f2553d066b71cb2ce8e52d54d22e01c610322", + "0x947bbd31c3936349cc8d2b436ea5bf8047eb27d0f6d1f97a939cf8e3b0e827af", + "0x88e96e0cfdf872b0ed80ff5d236239ade2ee1899f14749ee318702e64cdeecf0", + "0x9f5d8c1a9a9d13c55cd7b67d7684f9a13201e4a496755523c7a3174761ded218", + "0x54eb1f1a845f9088796700a7a55a6d67ace166d6302ca04daae46712aae16436", + "0xdc6f813b93d9c34b5b5180e86995dc05fb42a2eae376b651b94b9b082d140c66", + "0xcaca89cbe0ee7510856f3fb5f769cfea32bdaa87b6b0e26399c321032139c2c8", + "0xce9197e02b371ac46515b38e9b5a636748e387ffbef3378dd040d2fb6976d63b", + "0xa8ef7678f9ff77fca135578a245207ab9194fa042d10f919e7dd20f510135a74", + "0xbc4f120d9263347c2044c39f023db3187f67ab280c34cdd0122c4ecdbd0e4385", + "0x7687e41b877f292264a91822fce428277ba03e03adc70093befa80b50f7807cb", + "0x81866b3506cb5bc37ec81e67fe3addcb64edc316ae26c475b9e21b0a65a94290", + "0x716cf82a12a42d063f6842e242cda1aa0d9d74f23ddd143839d288bd1617286d", + "0x7dc12ee53f5a49f79e2924bcfa1cf7818dd88759c5efe7a9651fa69263616b21", + "0x614232e17ef92a7f9c89f5e78ad62ce49a93b437558db18ca886a8b36a3189ad", + "0xe36103e1e3e126b22032c4ab12cb403f35512d366453596c9668346ed01ad3f7", + "0x8fa21a83cc80c5e2ee866efa8b5989cb8b2f2287e6705236536ee248b0479b78", + "0x97d661894d612e6eddeac30be0a772602b9190973ace034cfc5964a53eaf736d", + "0xe769cedca7578fc82e9470187f0f88dea43476eb5003e37ca4e21a80c95a82c3", + "0xe02d2399fb8d4685367ce58a68a3db8201cf23386da5fbeef92a776dc3423e2b", + "0x7ddf559b53316e8d2f58a124a70bad3ee879b89a00548f547992cc6275b5c803", + "0x4912a5c438f59aa05ffc36fddc76086c92b365c78f0c5916dbfa0ec65e02db1d", + "0xb586935e1047cace6f3b4647c782eb47aeb5c61729b34f5459bce4204f3ec848", + "0xb7698e03148d6a932acffa6f6669fe9a3fc0a8fa2a15edf784dd24b5efbf9f1d", + "0x96515e929198dbe753a55aa2d4aa38f7cfcd69dd9312bff9d1fc91ee3e370076", + "0x5323b183935a92f89e64c3a687d12b3d5a0bb62f4d07896aa356030dcf3feda8", + "0x02f22800aa645f1b95193d62f8682341db4800c50590836ae1fc921451cfa715", + "0xb261dd5ef124828e1bd2890eb2d36b23cebacab01e9fc887777ec0fba9c66502", + "0x4753ae8b7a4c608412d9588ee6337ac66f8c3b21f6eddaaf456ca66af9b709e2", + "0x6a56d18237b87100070550c81da1b9e50b8d0246eb913b9f7437d70e284aadba", + "0x8da854ba7019cb5b415f154fc6b1969eef8e8dd5917de450634739a5716d0c13", + "0xc5788a0ab6667e5cfd9235db80a06d544206b199163ab891510f431d91cc82c6", + "0x8c9ddf8df394eb56f6e786b83b42a2cc8c5ea56fb383adb05dc384910afa6541", + "0x1317a456c57b03976ee6f2d309b9fe6dab74dca3b12b28d78d42c07e045d1b3c", + "0x0995b493a37c1960adea2e3dc36e3c57cb25d8fdf6d0786c888403086780e830", + "0xd749350530d8ebe7f82b4cd25b0bb1239140e139e7254b0ecf37f39318fd2313", + "0xcb87b63fa4d1bd218a38e5baf6dbfa12436e7d8c316d9452ee810ed3cc70bbc3", + "0xfa986131b54aebbe78b101f694ab9a7bf79faa6fc45d0b2292fdc8c6c0a8885c", + "0x1c4fdd8f4ad92ece5875659365805cd3489c50309b3bc45cec1df3eafc30d01b", + "0x3c880bbc2215b382f901929eed04bf3e3f96a8781d5d4fc8989048761584a51c", + "0xb1d443907c57a3f851d2b3cca04021fcba230cc8c3f35d6334d371a0e350a289", + "0x71270b940b7432650f0de583d45a7720fce15271e7a63d9064e15e44f5b18d05", + "0xd2a8315d47b59bd439dcb081d5943201fdfee308c0c552b7dcbe9ad7d44574c5", + "0x32029794bb14db3e89f6fed62b9343bd42d830830c5684d6ba7bfccc3a7b6df0", + "0xace45773b00830898d835285eff473eb9033a87eed7fa0570e4c862c8cc20c55", + "0x84b74333278946ca3bf406fe0f09196e2ab47a154e0f0a2339f454d518e12b15", + "0x529cf77dde438de6344c8cbb72af28de48a27fccdf37fc45743c2c985c07131b", + "0x339d576831888ba0549aa3df8ccd920696b4192a9808d96ce6320ae493590b11", + "0xe186ea23c78f994ef89bd9e34a5ea4a8e241c402b7ea24f371725b872871f271", + "0xf6c267f5226cc8683ac5bb829091dc7779ab75e1b6154741ff5b6e293d5e2637", + "0xdffe36a5005ec87e406f77521ab5c1b6a119c18d3b9367ac008b26420c41e218", + "0x772d3a44cc81401947da04c788feba1f71104318a09f48167c9b0eedafd22bd8", + "0x1b11a8e56f3dbd9f5f6e1b140d344e9ac88e74434833d950fdc36c13042e6774", + "0xf177698078b949794472fa3926290e8e88943a40e3745f1776b824b9e1fdf1bd", + "0xec7994d5a084d6080adf6dbefd72962d2bf6902dc725298818b2e8bdf7253889", + "0x64836e7eada0dbc4c4c03d4eb50bc515f42cce3664ddbacb0faca32928edd450", + "0x0bd7094a9a85a95c07180b28155d46e4ce7f78d364cf95e23e60f69d06c580e1", + "0x9584a8dc9c1d8a595c90affe49bda0439a57eb4a42554f0fb7e91986ab8ce4ef", + "0x2a74def2c04fe2c1d7a2d678afa7cd8c45ac2edf49405e87f7ea1af4fa7311c6", + "0x9651635ca0e77dee73040c0140f985d8ebc83b11270dedf8648485a66022799a", + "0x579301d81539e2e300d145410e0de62a5dc6972198104eb9fd89de1daefb42cb", + "0xc5320402e0cc91963a188d3549f72923b7b14d005b9daf04dda00288451b0410", + "0x50effe6d12b9670c12c48126b750ae93737fa62e1cdb8b5db0c411d425d6bd11", + "0xabc0d26dbdd1af42fbafdd2268dc5dc5822a36c7f533000e923e16c6338efd55", + "0x1e6074b31f69e4a3334a193a2eff087e37d2eace05dd4357f3f131536b9b73f3", + "0xe494fedc0c4cd61f6925a612b956ebaac323f9b7283d95e25b025acb61196d15", + "0x1e3619933ea9c21c8eceff398c6e4fd7657a0853029f4dedf636a19414f52c5d", + "0x46083f05a38abfba2412ca39a67bcf0ecec5b399ffb54b475c7ee6226184d607", + "0xb3c83f0c92f9fa125989ceb5d3c35b36f54984b38990102667465b94c9aaceb0", + "0xca854349b4089956620c8aeb78d57b3a842a79f48be552eee754e57aff069447", + "0x66df1ebe61bf315325e4cd57860edd66de7fd29d28e31478c0674f39826296f4", + "0xd6e2263b68687572b96bea41102391b07041addff6277e3289090a68c2be9db5", + "0xf04ed69d60ed490cca80ec947142009a836615e3e2f0f7e255d290800f8bb2b3", + "0x3e3e032874ecec0b6f0813b85341a6db1578c31c21795ccbf6075f13c167f8ad", + "0x7c2ff9c5b9d0faded53c9e5f1a79dd9d852aa8b341b2d93d30e30a768fcb33b0", + "0xbb3076671e556c63fba928aa5e6b1618ac42e8fecbcc015481e56c90b7a60a11", + "0x748a87ed5b8aabb3ba1d11ef5e2953c02475b90f2110d89c3c323dc95427cfff", + "0xe6a9730d572a14fd40624895c78f87a074614fcb360e9e75fb4f2d0134d21b55", + "0x0836f3c358654d7155286cb6ae798d2638159086138da8f7d22c0a2da0b5c13c", + "0xd32d93e5e977a5f47b0c9c1a6d70b10ac7246d668dd6ce50f0d0371399d6cda2", + "0x2d9103e0673448f5f44825212123f3dab11551eeefa7463744f6a44298f7f73e", + "0x8c8bd0c257d6bed8a56844b94b85747b7dc4366747ebeb571d86a71cc6c6226f", + "0xfad8aac6d3b3db3406d63025e93cba332577a20a41ed7fceea11c4e630a02d59", + "0xa2349202534200b3ee681d6aa94f27e30a768bddc98d91f694f60d2048ddb237", + "0x2b6f00a0eb155cc416b5b8965d4ce057ed3f883e5457d86c3c53ba6b21c163c3", + "0x35afac6d926cf3817420d94f499468ccce25df0558120d677dd3d9c8242fed73", + "0x227cba2d2140ce9783b00b8ab6c05f3dd1e6284cc2b8bfaeb2af8bc33992e58a", + "0x0b1aa7176b1845c4dd1aa7f1523b8eb6bfc4a7abbe797ce1a1d0ea4b5524a8d3", + "0xe4f8057609ecafac162c8d354117cb18eeffadbaf7e4065998bd449abf77a3ac", + "0xea901da589b2cb35c63538d4b7acea05d6023e95992e3e47aad2399c968890fa", + "0x00ce261ec4874e9540f0d1f9ce0f243cbf9b9beb9f325063fe87fdf8b59ce194", + "0xdb745a01e6fb05417bb104d46882b1ecf06629692a8de559c9ce2f3acd647773", + "0x6844bba98f0979c453c1c50e718d14cc2e6a324629d4e67863a5c0ff07ce9d4d", + "0x218fcbbd249210967e9f783fd42f83d700368482d3617745965374b3fabe5ce1", + "0x48d40ea82b90c36122a4e990252661c57914a9c549cd986941f852349095edb8", + "0x5594fe86ddfd410eb331be7e907c6766d62b9a35d5826b4f98ef68336c9b9679", + "0xe0580bd0257db1fc17c2ea660935704d8486d6ce20a430e587f7ef86b8654957", + "0xf31c8e1b7f22d40d59a17269890c6b693b218355a712a27a050a061206b0725f", + "0xf94c97dced64198be4aa3abd31825e94e96678c2a52dedcaf316a41c3e3c1f01", + "0x3d77ad982ca6664e1f9d93bdbaf1b180933ff3a9d614e2f699eb954a4b5197f7", + "0x5ebaaec5f7fc3d6569a1366c89e89b797c2cc8f9b4087e2611d129a55e7116a5", + "0x83a7ff0290a45501b41e43c9f577c56e062037cfef3dea74d6109231d36afb37", + "0x1bac4fe25b709b3a83378833eee03e9066cb484074f4d4882b755b6fb05dc4e4", + "0x66b3d0a4a7d78426be7887466ef7ce079b80d3affde2db50c87c99d4219d56f3", + "0x2543340b078a3655db46be25179bddc49eaa143ec8e9872bcdc5c9435c9b83c9", + "0xf43b64a55a52f2c10144d2250555a625199c2a6932c2f2ff16924d4a9417510f", + "0x6925988fb1fdac057d35f22965d13407d8671dd3a387e570622e123e8f753856", + "0x55914fdb8cff4e1bdedeb4c02c0e01aec796b3952ff52a71bba9c5f1a7beb20f", + "0x23d0efbc845b55569655be03d39e7905f3b956b5f15f26e60bb328c411cb9b34", + "0x423de8d294b815d90eea6680f070c16c2a579b99bc76d39c71135bd82cbea6a3", + "0xd76401af8f9a6ed3bb6e5d0c417092b1cd604a25ef02160ade826ab2ef7f780d", + "0x88f5a0e60d2063eda6b4358fc0c9178a9a88a1660764c40d3a45c55ce0ec5052", + "0x959a94ace1e540a6cb76c1fb947c0bbe7dcbf61d20453b7b6a4924167fbe0ec3", + "0xfe63d9383953aa92f0ffaf33f2e7b9ef1d3b9a6ec2ee84d1888063003df5a06f", + "0x1ab3155eab555662deb0a32281ccb4c0ef7eb459c527855a8bc474e32f622125", + "0xecc04b160e55646cc8a0a9e7fce977f520a247abf10041eea11339f94aa7d139", + "0x9da3c577c80d3994fecaa85e310560beb46f9dc721c9e3d718faedcd033dfeaf", + "0x24740c1a315a3b21d7f7cad009b2eb4a8cc762a9fed1d5bf745f56d7b5ddbed4", + "0xb83f667a9bda1cbda9845aabbc1437602546306f45d152654eca455dbfed5a3a", + "0xab6bd1581eeba92ca236f3cf93df9921988b1ab78d8f4df9d53f6277a3ccd187", + "0xb63d09403e09bc71034a1603773a76ceef03889819245a60bfc6bbe0dd9a0608", + "0x3bd76d1978ea62bbbcdfde8121699e77c85cac8e949ed31c27dfb7759617c9f3", + "0xf7d54008d907b43d092876532cd3c103ea605dcc0f2ba2d5fd2205c7c83a1970", + "0x79513dd1195d59e4150d3dc9e790a185ebe7460b29d8aecd8efc6186c877e8a0", + "0x7d54834be4a3603bd97aeb869dfe01a39c00c40c32c7fcf00ecee004a02d0a1a", + "0xdf0b8086fb03f91f3c34f67dd3ea882f164e12f5e221dd98c19492c7e3eb8069", + "0x297de6f06409bd584dfc90361008bf6d6b70e80d8139c59acbca30621cb58983", + "0xcd92adad2c886927c2d7bbb97de2e825b44035b76652d38f8b65f8f129cd6373", + "0x3089320a0520d99b5b2713cead5ffff8e2973e728a0bb07826056d0eb81685b6", + "0x5c356eb5f5b76086ad067163b786b12d712b0070b7c978d506fad9d156ad4bad", + "0x6c6410f0db415a45896fb586d0091ae8f1d39b2a7ba0d50158d4eebb768f34ae", + "0x98ec58e589128e67b2e2f54b9d31410a5e58b3011af5b714cb165502c7258848", + "0x6b17e6e4727a125dd7a50dcfc0e0872406b1407b0aaa85cd94cca4b82b6cb259", + "0x4b2a063385641bcabd4db8ad9776eeb0bae8f6e58e282dcc0743405d00b900ad", + "0x08a9315f27b4f2c6598c876719e8e03c80eebda328b41a87226ad524dacfd08e", + "0x8e9f98b469ff1ac5d621c43af92d565dfceb9d3f303867a00fd2b90d38f82cc5", + "0x7a882646f7e1254d30efe815f854843179b68a5704241661f59fcaccf91ded81", + "0x7e186b5b12c90465c9ef5545a8cbcb739ec243882eb137d4122adcf9c5bc0068", + "0x744db11418c4a09df3cb0440220be57a25777da16ad03e5bbc510a7024fc21c5", + "0x34eff628fc36c98bd8fffc016332e8edfdfda4a8858a8e0a506c140e724c6df8", + "0x521bbeefbf43f36c9ce2eaa96ef5c2f207d24823e1aebd187491e742991ac681", + "0xcbde5c3ffd332ce5d71351a42dc4c8da0ab5b6277b5556baf4d9a2c8a690e9a0", + "0xf63fd48aabbbabd3b034d0e33c5f60a1508b0aefb86e5addf184430176002eb7", + "0xc9f1e8591d0ed73f90b87423017231d1eeac58d23cc5b9c14eb64375bf5bea49", + "0xf442c525a178c90f027b4a4270f058ad9943d0503c13258e461ed5f67f50af20", + "0x080f5eb361b6eacd879cfa4d203ff37f6a19d6538989778cc88528e502eda4f0", + "0xc9b948589e2b88fc62222e9adb955c6f967f830b1b6ca92a7c5f1111df3cf81b", + "0x2863cbfd039581f7ebc42f4fa830b310de61ff4d8e34df424f8f7e391a92bf9b", + "0x159fc109a73a5bd3d212a01a349225d702a1e908bde32cdfda72551ceeeac3ec", + "0x3370f2085322baf577f9cf5007ee56bbcb3182b38b1d694ccf9a5a3495c93c04", + "0x3268e3d17cb5d6f9821c24ce90b1f34cf368537e185824c83d0392962379bcf2", + "0x59518a23166a7698f8a7553c8d40df799e2efee655c4ebe2f76774c4aca857fe", + "0x0635e5686bceea79d6967886d9f185a8d403acfe5b79e81b9830c087ef48d1e3", + "0x1fd1e7d8eaa7ee9813fce5d97ae10715c8c2fe4c85b00856dd1c1c78320dec7e", + "0x8835d80653087658a28729d412e56a9629f10a13b1acc9308334eab3b3886407", + "0x16256e81fa4eaded804ecdb281b1d0c691c498c67af3e18b02019a8a1ff7758f", + "0x55baa9e1107349fc83eb3c031aabb68635a6488d39a6ae4aadc462519461553f", + "0x8fa2c71112dd7d8dd9ef08cad2047e1f031e4111488810c8577dfc9943037d49", + "0xc331f22f5fd9bc7bc7b76206fea55b51eca4c34b6da68b410c7352192332747f", + "0x5d2e6d6069558ec09e84748df1b182260c801868b0c4ddb679ae897a50dacfec", + "0x2d287a33e009b5d3ca71c9d5266190a82fb09ee6b126baba59b1a64549430430", + "0x0d1c095874e6a8ca1370fd1c864047afb8e6efb474c9ad5bdbda7cba059c2421", + "0xe119577fc647873ca247ac9a00a2a8b2277c06ee65f0cef4f1d05d805b89bb8a", + "0x7fd2314fbb3e7fcc1f4fd76f28c596a985475720f8398bb5bad19b48d52b4225", + "0xdf02655f44640661efa92d1b21d0d955519079202b5035ece0f7789e132d679b", + "0x2de834a8808b280e337a685ec1c4de5f78b59195e24e01cde477029b3ead2463", + "0x10c057c46018deac1ea2eae9fbb9fb2dbedaef928071e0caf775451ee19cf53d", + "0x779fc29860c80cadd138cd7b2426b6fdd049887e553e087cd7bf510182e81d1c", + "0xcd0e637b81b5016cf2eaefdf7d6749ffc7f0b8bf779d4224d9c240ae1a71840c", + "0xd30b33eb452747c8c60e4b54dbea70cb877b6e22de8b3f3ec09d595535ed840b", + "0x175a78e1cc4b213754a39ae443c7b2c8eadaa116d6c67f3e6a97389c565540b0", + "0xc1e16a12889e2296ea070aadd91fc5ecc98ca4b5d53e9742849d14cdd806b37b", + "0xe2207b0b692e47e4a85d2276f81a357efd361ab2530526751f0133ba9f4c4884", + "0x779eca736bea6d352e8ddfea350b77e0a4859baa94f78a927e809906fd77c60a", + "0xf935a52482ad0c87c62cb51b51403a884e85c2a2f8212b84a2e22d8df9199a87", + "0x2b513c9e1315311d87a25244de96b8c5cf0f0b8a80f7981fcc100095bfb6d67b", + "0x88de30f17c648c18e4fc3c30aebbf7d1650e60b4234b605d38b109466e1096e1", + "0x4e4d3d5520b010a53d4a465b67712b5193038295c098f86b4f9c451af9e0de7e", + "0x134d537ff6a032817683fbaede098e97b559d5514260d43023aea1c941079d0a", + "0x98fa2baf172fe81f0069caac70e7202023bf2e82c252d2f0bcc55a4381496e53", + "0x9613601d7d59e509d1222b0dab7019e482a3313bf78c1964b8474f2a7358f7c5", + "0xd05a735a8009ddcad4c18af9711865471985694b9521b8376bc86534d0ffa082", + "0x6208fd8677b90d906923a7a7f7f961ec8bb96e990cc1497c2d41cfd5e88082e8", + "0x354d2f387d5b8d054ae782f4e6a7d0868d13dfd517920863a62d0620205524b0", + "0xc7c629f65f738fc1268ba90e40c5cb5f1cbdfcf2c6290a1f1a4e0456ed9cd8d6", + "0x7383eae04af05a1c36dbfe7da690335462e8b7f5bfc540cf989df4052c683f74", + "0x636b0bb8ca3a92ae305deaabdd289532f07c464e8acd72c62e15e5359e6e72f8", + "0x285add310a26fd2bcc3dff4635780a39107f0a243c3f4d3534f5bd3e5fe462e9", + "0x48ada3a8c1bea56a98bf1a80865e9392c96bb7b341f1fc4729b9e7784410ebd7", + "0xa13d36e9610dc253ec0f26c6ca2cd9c852b89bd56f3865658f14db9d463229a4", + "0xeea9c59fb007f5309144729db11c6e9d34d99d90e4eb229412beb7bb90b97a9e", + "0x90e0156a646f7bf812a261dea828f4421894cc7ee05e429d25542b1c94214c47", + "0x16f037491f75416b47aabbf5c358b11f7b3a3132c51798b1a7a857744279c1b6", + "0x1cb566fa5346fd873f40dbcb0bc62f27fca84c59b0c95ac189cfee6f7534bea2", + "0x40d56ed4b55f9b5af1ddbc37e3f05b1726a1a45180de9586ecb4494f9d8de4f7", + "0xb1f842490a7868dbaa4fb4194b1eec4f40ba7b827f9ea05b20bc95dd05ea2672", + "0xa936743db160a02e597c7d68ccadec39dd0db248edd7b4c7f8a01092630255e4", + "0x4c81c96af8c0ce017d451bfcb30b6ff394a1441b19afe1a5fab7a9efdae062b8", + "0xa38b13296162abf0e99f698ef44eef8d20a03923f015a0e6061ef9be546a603e", + "0x33e6b2727feffdb80e87adefca9841acd281df86d070546ba18828641d3db1a0", + "0x9afda031431bb9c346fc25732c9c8e7d0cc143618f9c0d965c8c20c34b5f7723", + "0x3ec682c2fbb2e04bb2dce10dde5d5eae62e077f053baaaf0893a63464a1a72cd", + "0xc1aef244ea8dd34fb09cdd8f3553d56b0ceb7e3c34a3d15ef81d09d68ee68582", + "0x9e86c6d5b5e038f1ca0518bb620540fbf96a5b4809fe6e5bd52cd0a5113818e5", + "0x4a3133c3d66c9e07e258e5f020804906489515c1cf93dc51dece2fb291911b63", + "0x276b8789f9031760ca0f25b2dc02d53e8e41b8a746625eab037d2cc845136660", + "0xfbe5fb0df4fd662e8e9980419d14d98ff245372dd099c7f7a3a22d6542fef725", + "0xbba0cb6edb8b80f2278ea20b3d64d95efb0312dd403b0be5f802d04bceea4d04", + "0xe448d4b38358b30ff9d832e423ef5cc01f1215608fe5da861ea746fc2e14de3c", + "0xce8ecc2eb01ab76be2623f137e4bee8f1176df79b7e0192ab2f2aaae6d8d54df", + "0x0ce8ec30156a92d15541fcba6b1a1e1ada40a354bc22e89f6448c067bf0cd931", + "0x392ee448b621deb4e41074c6d9f8118e0138ea3e6e11f443905d1f5d73ff01fb", + "0x51ce5b31e76e8958551dba2ca2fbfd4d303f73c120da3f57f752a5476b9b7fbb", + "0x808dde22e9bdc91d445144dbbd4e8562a207373f480ae2abed8ccd53ba150ce5", + "0x178f7c318fb26212f893a6f5a5c9a5351425071fd1b2fb24fb4e37edf2f88dff", + "0x4be78b31282b509219f2655ef55599a0cc8e234e0f4d358e558ce980b64ba719", + "0x120c97497f334f7aa6970ae431077a2cde40187c09445a6d3b876705da0fb627", + "0x7a7f1470fad9081521ad6e616574688646ddbccf1920da26efe40cef10efea41", + "0x2fd4034ba1bd578e4192eb748f08ccc155ee431d7c031d1ec4f55dc920c1c073", + "0x16ae0dd227934e2b31b129bbad1353f64e6a943beefd663114752f7b8ac92e5f", + "0xf8c3b267799dbf7db0856dd883433ed312b3e96e2e56d584f8fb281b67027a57", + "0x4d4e8d1efa9d9639409add386a3cd900f8d990b5963622f77818062cf830b9a8", + "0x94171849d290da38937d3edf62b03c283487a945f01b5b447f2045c027aa84ee", + "0x227a8d310be11cdfbb995daed7159a365833976a9018c3529e29196df2d3d499", + "0x88ac01be3414c36feefa384c45c6cdbbe577b03aec9d0914faed9d61e136553a", + "0xaf928a230f65a5deff2486d4ea85ce7427a845d79567985833e60fd578e33f7e", + "0x670a40ec0528aeb59ba8de22286d2ced85ee517104513e012140c67e30a41bdf", + "0xdc196c567f3bd0f881bc1b4bef64bae82513d18d1449e227729a8bbac7696d2d", + "0x089c062d9cc3c7fd73d54ae00bdf31a11345fbd5999cc0dcf53380327289eeaa", + "0x7efcac0a990e76e14d8a45c815cb8f481f652a1562dcaf0e3f6d9dbe64313417", + "0x5d9861d3f0c7674e495064eeaa3fd295047f50a602d83a71eabea19c8c254871", + "0xb4cfd44325a862702357d4bbbc532cab62dd3700441d8747dc60e06fc9408f94", + "0x014fdfdb6ac6b54cb73186d8b65f4ee02e671ee7595b19f6f63d6078efae093a", + "0x297975ed0031481291b1fd4a8e3ab47c7349330932c70c21d95c9388e0539d12", + "0xbe3a1b59baa5d74cdadbedccdd91ca3b3c0cedab2038d44bd423788ca1c182ae", + "0xbb3f052b341421d7bc5747db9bb8d940e05abdddde96f04494cd87f29700d3dc", + "0x785a13b4ce82e947062c82306d52cd1cb76b58d734c765f647a001b28a48eb39", + "0x3cff39331696d06e23819c4db74cb62cd9270637e23c3da6102ad08538ea4dc3", + "0x37e02a6145071b2e078399bd73f03cb00314a667f8d905002a4e8d950234d52e", + "0x1d807c49e203b9089e9bbd1957f64765547c7e9500729eae1e25055d465034c1", + "0xc013da3132a45311ba0fab6370a95afb5d7b3e355869c8c483d2b469563b96f8", + "0x811294751d2b47bfb9205794862849d699b2c0375c852db048004f1d830d84a6", + "0x634cc0c17304d217a956a59092b59bd63a0167153a6492c2b9d21270db1b77a1", + "0x21a5526f41b58d6e7901466414f929b04bb516e42ee757ee37fbc904906b6a72", + "0x292de492e8cae7cf2e772b83a41be4322b9d205c48cdc2d50f24b054607d0cac", + "0x1c58ee7c5b89c5c2a8dea324c6078e428dbfdd9bee79da718ad1495480c84e07", + "0xfb85ca6b1a35f3612e189f20b06a57c29f4a0df0315e2d9e94c63616d4dfe86e", + "0xd498858b6a48e4f49acc4476fddc2c37c3c9e1b93b8beeea1c87cf107e869168", + "0x2680e765cb30d6dac4180581295bc34ce80ade40108c28e39cb670583b3e595a", + "0x04d03aae345a5104b93925c23c5b52e7358e4cfad560f9150edb3db03c2c30c7", + "0x49201ae472d9d3f81b3ffc767afdf14ae7a972ecd7c84833c326e218edd00a1b", + "0x8236629bb0dc8fc18a472ee0185496c4735e46ffd883098ae72c4e6ef2e32e9a", + "0x285bc9d5debbf00aafc5d4954bb1ef6bdd0a459edd05509d53e0fd5e38a78e91", + "0x0b8a0124f0e935c5d04601da9a97a27e43b7ea26e53231815f6962287bb154cd", + "0x0d86453af764eac132f014e647d8ba2f9c191047cd2c5f4dd85c50b74eb1f822", + "0xafb502813718166ed82edabf35e67561c1e00f874e1dc8719dcc2a52aa192e8f", + "0xb610447a2358ff9b21d478cf9585e0918cd3fb96c7b12f2fd4c9b1dff70a0b61", + "0xbb4febbc182694570ffb00e6d2a3409364d55a42758b5000810d9f7aaf8bc852", + "0x900486570135463874a70b3323f968b006919495f746c3fb82b698ebb6e8e096", + "0x85c89b8f43cdcb66f80e05574f3b124053432d6294cc3bb5c04f93911eedd24a", + "0xfbf18bbd1383f069aea86abd5295b8fef09e14f168ccd9cca0554a30de19aeb8", + "0x2e406176988503e773898a24533d60fd3e7d73b6b82aff7737ceaaa03b53d68b", + "0xad45d831b30dbbb9a989952df3161e6e6b0a7e38560e71b2a7981cb5f824af53", + "0xdb66129a21200bb209d36e2f74563f85c408a7b078e9c258e53baecd23c8b96b", + "0x81d84a48ada6f36da06f9d8d695596bd282c7186fd44c71b4e2f600acc8ecf99", + "0x20d0bf7335c2d2796aa4a19d1399e667e5cc020f3794fc7d78bd2c09b5ce42f4", + "0x83a110cbe07e1d92dff4b5917feedf355776397d00b0b3352ecd35d47654cbca", + "0xc86fd6f5eb6776af0059adac65bcccefdd5e7752f01251f41357737336183497", + "0x4a6b6eba450b952bc640aa412a8448d5c72c4f92d7120ea8579c372eb48a6e10", + "0xb1b5a85b4328ca46cc6c74c1f2596802a1a7b3b385713386ce083d86ec270e6c", + "0xbe44ee6e203014bd050af42d21db67036ed70792da3f7030f8ea534a018cf81a", + "0x4f258d3b9010ec7e236e9ef9c24ceee6ba9a3066f6dadf793be5252789095f8e", + "0xbaabd8139472cb5328758d94c6411a4a60f4a59c845db2f0760314e72752e206", + "0xf6776f194ee4058cbe6ebffe42b4ddf45bfa23fb829350d24b7d4d73e6caea87", + "0xa60f6806d15a6b0874082899bc9708361bf5f3de14a2b679dede3e1d39ff9b3b", + "0x6b20155891d7806b200383fefe09b4c1370287c2cb8a3ad7d0a9fd61af8d25cf", + "0x2e9a5cceb65da442c21c53fa4dc1046df92f28527373309d1fed96c81fee7a5f", + "0xea6ca8f3c8eea90d0ebc67c9ff9494e0a2cb335161cbf6b71a42aefe114568ef", + "0xd8c77912ffb53b786a3f4db74ce760e4387786751192aaee3f83555ff48e905f", + "0x72ddacd29845ac3a12c3b6e180a7bb6444317f7f39199927631ea415e861ef7e", + "0x2c31f7bca9c48072329d4cb80d0953f05557fa7c0fcaa0b288d3dda9f469dd0f", + "0xd174ca87babb31f0b6fea49f3d20fe125dcb3fe839b2e29ec8fef98529c2315c", + "0x840fe20b599ed7df3be476fb9a8f9de32f023ed4921402c484241aa7156e3fa1", + "0xc9debbe3b035dde24d56624f8ffada46aa8d626e09b22268f6356f0b07e0e785", + "0x34ac8624754a8e6860d2b11c812df0cc46ae7ce339f3ae3a4cc84627f0be3bda", + "0x791a479cb8cba8c155d2c1b3d63c40d9b6627a685ef91113b213af6bd86d9f98", + "0xf4c1bd25abd2d9da11174f2c14167409516edd6ce03a8fe46631f550eee3beec", + "0x0b4eff752d6dabcf06b1358ef065808874661f9d00f824a4520fe983001970fc", + "0xff3c8613c356afde099ad64ac7ae2606aa705947d01a93bc15f367e6f50e961f", + "0xe1a57988f11641067554e3524cd582c22f21366c6453f92b4afc78b677f7307c", + "0x8dadd27b9fff4250680542be9e70e438c270d7aa2d46d4573917e6287ea98c13", + "0x2d36dff4f7b85553f5312abf1562b95d0097fdcca074a11579e7099252bfd151", + "0xb025cb9880f19898f84ad239ffe8fe74f8cd5a877fe80322f9c71b129a0d2dc1", + "0x18bf666198ae6e175f3a2eab9b2269ec483a1bd7fdf11cbab7fb04b44626138f", + "0xa0ffb7bdeab13f4621d537161dfea5a9040080ac5b68042672ebbaa54553ab0e", + "0x4079d5ce5a7d2ea4927f668e5361cb40417ac97e5aab93af59dc00dbe54e4bca", + "0x960d1b8c10cb0b06269fe4de6da11bddac599f0f2fa4a0bfab4350b45566bec1", + "0x9f1df0d9ff559c6a0f7b4882bd999e81452d3616a2c88408b4ac7619ea9e6f75", + "0x23b54b7d42f584c94d52e062ab8121431bfadec4f86d8a2d22ec602ac227f35d", + "0x081a1d7a2a73520732accdea78e50b668a35d6f95e47b76783c20b3f1bf0b73f", + "0xe243c106993a0d8ab3562487c40b8f09ea132522197cb3aec18dc25590dc1fb9", + "0x8645ac74e43700f6a109bb588fcce590ecd4841bdff10248042c66af15f9b9b5", + "0x487c9153e640c8020d2a3760138bcae06789af8cc6c4cae84d0ad23101f1a3a6", + "0x23edb3ac0232ddce2eb9baf3ecc18473ab052789e81346e78070c86d17ed1319", + "0x3bbdc974e6338c14adb99b000e89bcfc5c8484ead66d2ed7f3cba9a8b9d22ca0", + "0xe40bc74ea28d493bac0451136760f5f278c81c9add6bab5aa920ee6d26f5d405", + "0x3401f10f3f2ef765c002d8579015c83f89d3e6e3028d2b130640dbcc1413244e", + "0x096821a8413c25b926cd490b3104c19cd49d9669526ef41170ff78484936b46a", + "0xb095434423f947e291b14047ac0b108ae496824d7b23dff2a6b260812174e7c9", + "0x2c0ce8228b73f73420551e5403df80864ba5fcc2967f765fb5997b75189c5bcd", + "0xd845cdd8829f8112832c14bc7594c674ce9c8fd80249d26aa923d45236db78b3", + "0x0a72e9057c608c172c79f33e0f9cba99e6df7831d19e2ac1c1aa13c5187f737b", + "0x4c5a58b608efb8e327878a0fa50d7c634dce0454bf9fb58485841609f94b51a3", + "0x8d05937937481229096b5e2976dd9d3ab69c7d43487326490a7b832241621e94", + "0x2987e03fc5c04ba3d8bed77a61f769b59bfa8cf999835f41593384cffca7179c", + "0xcf19c98cd8e422c135218a1ff989a7585e3334c21f3b234f409b6ed5c646453b", + "0xd639bec7c5195590ac729e9cf24afbf53221894447ecb89e4ea1955d4fdc8601", + "0xacda6e9b56dfda83c60b08da0c18dc2fe32d7e6dab49cbf4615b99451ae62046", + "0xe13153c678cc3fb40b2652f775b62c7430d3e89070688667a935d297a8d0aa29", + "0x3a91dbacce556df098fb774fdd1c7af83b2d039937ee049add2da4fbe209ca8f", + "0x4a0ae992a83a927d15b981b6e76b5d2d983c1ceeb3275d44c373ce4d4f1fa1b0", + "0x88263cb31879e52bff3a493eea8aa71da372e01cec12cd6bd5fa7628118aa374", + "0x3e6ed3b03c26922d2af22a76bec556a7524d45c34af9d298d5b020e23f45c1cd", + "0x70761882d731078b786b8130ba3c7ba239e6c5733d88802dc7831742b4d9a4c2", + "0x33c1b535043a2186684513235d375a32b591e2c97c8e3d0ff1c6d26da48c7053", + "0x8579955c7b3043ba6f100a9e9ce8734f71c0af5db8c900918a47ec20041ec67f", + "0xe0f1fa1ed83f92f45fc869e151169224bcb434cee80fd2ff2ce5d31c38815b42", + "0x75a3b6e6889b2df99c2e665c45fc2ffe375319ab62f6f5b5a4394439fd8e489f", + "0xcef8a2ac91dd057bc0b8f504e8691012ea85bac34f5e738880fefe439e39a3ca", + "0xe561554d697d920f55ac0b7e3a196ca10053bf4bbe0aeff68c3b4cfa7c7e2861", + "0x9ffed87c538452b2b3d47b6fa8074204e4575c8d82c6b7ca1114bb19112afc7d", + "0x06e75927cbe19ca1d3f31caba612827a3d2a92a6adc7f2bfca2c75d2a79675f0", + "0x42c85a3be0d0d0a336aa2ac16208358b787607c772204381e46b720434a69754", + "0xd6edc57be5749122a0c5a7c26ebdc31b6a63e7088baee223c8eddc51d7940732", + "0xb3d67b9471fb81dd522397f316d54a7745169bf306a52836b9291849f3d0cb91", + "0x99a83843cf51a4e1fcb5893bf6d6ba9d87c19d9184e4277fa435f8444bf693cc", + "0x2e0ae385b8825723a48619d1f89f3b0f555b7695006f1b4d871467a2de24c495", + "0x1e315fba0c10d666311f80678c828815a8a41a6e408d7e837cd9a443d6ff7c1e", + "0x3dd2cde674514201e4be5b25ef48b5f9f3cdf861eb08aefc341cac3077770e5e", + "0x4e8a9a9006e7436f52b50a52e3a0602ea0a153ef9c77b275801cfc9e6fa2f094", + "0xb354c15507905272615df0c723db0318b5061e0f11616fac681c38cbf15bb2f1", + "0xfb7c1beebeca99e7a1d54573d9c703582e45498a8f60e81278f274ed8012c575", + "0x85bde8dcec7e07672c0fd30ebf0ee3ad9a2b2c3af0d78445e5da169b5735d9d6", + "0xaaf66b90f404bf7b564cd2087463d6d53fdf0cbc92cb25e7cf3e68e88345ba3b", + "0x86973fa1315893c9c13d4e505d975a3e2755e83455ce68a079f094dd738883f2", + "0x3751485305de7cffe22a88ed1182f12562b8e193797472a13ecada07931f69f4", + "0x777c03e8df360eeb9d46c29735854c0d2244ee3519995defd0e5100e5b9bc2aa", + "0x5e413d2190bf6e8cc50c6f694043a082f19329f3e16c42ceb5aa10626a2647ec", + "0x28493ef2638d0ffe5d23b5d2c537402c12e2cc9f32b4d54df4df3e6458e2c73e", + "0x751d74f8676064034675a35b43637f57e0b71a05fffceb38fc23b2225c87c7eb", + "0xb9b01d77dc65bf9dc2d0fdaf1cd8e2e62ab11d779cf92af2156d86f2b85093a3", + "0x68cf82640bd8a91e4031ae80d0403b7c5575ebd2afa52992aa0085cbbdbc4556", + "0x546ce299dcb49e9e52536a4d323d22a7df6088fcd66827134ee896359d0ec8e6", + "0xa62215385b9c4c8a827a9a3ddcf1ec81992c315942ad432441e8a2581f93cbdf", + "0x80cbaec6b36c0d4c0fefe2546e8e2732357fe9f342dc3576fe10fc0651682d43", + "0x0d2e2028db783f2dcfa250d21a086efffb9dd20faa792c910db09d08305bd614", + "0xa80eb50c830e572cb0b385ee60cc853687a799ef9917f015d9276ff17232bdc2", + "0x9ea5ee31eb8f619711ee68c5121f79f21433a5b27df4276ae70a8ef8cf59321b", + "0xaf32b4d28ba2b72535b1135f88a2c663f6e05ee75e68bf72b7cb89b9e6a1ce2b", + "0xad9dc9484bcbb35305f9d833b18407ba0a179ddb9734da0d9ec6fefd09793934", + "0x26bc2f2a9a0720afdb6ffd2b0bad7a7923ae7d4cd8b782028a29c98e70feec67", + "0xd9bec69dd0e15c398180803aff643f062809bbcc1047813736265989528401e9", + "0xdb68ac2dfbaf0e41c51b5993dd392c874a95f74052ca6c5dbcb1d837071145e0", + "0xee10af7d43f066b9760a594733cc7f5c436b1522c6d6aed9ca635640ca22a3b5", + "0x715b57f2947f0d519eb3add78706a79b0f53391dbcd25bdef495d88e84e75fdf", + "0x9793c6c09a760e123307b016abdf7868404b807a0eba3b6e1632099a5e260daa", + "0x0a5fe9741012580d76f01c46c25d398056809936d04a8a823cfdec5b4a2f3f51", + "0x78355c7195f8c0ad85f11a49780e39f5796f16fbac9e9696709b4fdc30d114c5", + "0xa976e7e1b2e07b28f1e64d9c43eef004db2ca1c171f52ad8e7283f5b6e6404d0", + "0xf13365edde87f538fc7a2f47b63960f3f96c31ed4afc6efff15f9889177f80bb", + "0xbb775187173189454a48476be176af0e63fca6a2051b3955367b1a4cc3d06756", + "0x034f3c3608df522aae461c92d6bd44f2c78511c8d227d5621b77a6e800df16e8", + "0xfddcc97c9cf345d316796374247083aaf88e57b4d79bc12330ad91c09b89967d", + "0xe6b7f99734aa1d71a0f5227276c2519bd3cff6f885dbf665e003aafc44f82efe", + "0xf72ef40f1fbaeb1f8f7252cb9e3d8500233621cc52f8ac7c2da972939ac05df1", + "0x14aaf3e7e8bbc4e4bb03ea77bf9a7339f1aa3e14fb47de2a613544a15ce4f5ce", + "0x5d36bb8da064439fe62f174feb046cfa649581ec62b57ee4766078365e5a1101", + "0x59d425d65ca54a8bda490197a0b5c2986a05fe867e8e6fd7328cd7a2890f9fd1", + "0x7794385506c2728d337252da606fa36cd2e376f1234319aed3710ddb83813667", + "0x2253d36af5047f099fffa05e7c3428eac497d9f58bc0f7648c4a4a67d8431446", + "0x3c4cde142e2999805f73a31ebc727463fb2d6a4856f08d0d5e1b18fb7b4d4012", + "0x3c474f091f821a43de6893701ae6b64cb563cc534e77e37c807ca49e989dbe2d", + "0xe091d786f8120745f3e64ec6c348de63dd957721e18c0eacd0c0bdef88d23275", + "0x5f590d01392c5f7ecbe6e15aea521a2c66ba1cfb8abf8cda3c97cf7d682b2a73", + "0xae78570c52af5e92888c824310ebcde696110f4f15b0558898c6a6dcaa24bf21", + "0x5216d9142156f47c9a843eacf17879db6af63ea3854a8a60536cdd392af0976c", + "0xca1f30891ff7b07434c17aed25fa0914f31c4836081d4a0768a4becdc4252abc", + "0xd4d52addfebca904b159822bb931277e5a5d65f643a5d6f7a1b437bce18d600b", + "0x2825cd7139acdb68bd26732526d0d3492e23cfc3f01975da8ffb77104c0e7276", + "0xcacb2cea1c95e015aa4f24e603f4bd45fcce7f517de04765c7603893df3cd414", + "0x48ae2fd38a5dee21cc3d5343d294c98939613850a5fa29793afba7afb3e27bb0", + "0xa766d4aa99b3531691800bc50710583b30d56188dc2e7aaa21f8b7b04d384194", + "0xebd837cf641641448e26b9c843eeefbf8ed5c7b2d6d248c2235f0beb1a03a62c", + "0x4cfcac8b87501530fdc1974e1a3475158b9bd94781e4c1c7b9b79e3d6cd0fd58", + "0x0141e2a0645ff64e6fb897cf1d8811441d6a493a4a141f78d381ac7077c6f300", + "0x5b31ff870de5d6ebefb0f2af2020c6c7003b836eaaf3ed5a4a26580028ba3c9a", + "0x88ac8d91208776bb65fca7190bb04ab602bec63c6afdced103e1daa469cce472", + "0x8c8b4eb60e39b878baef56d0dc7d41a8f4cf66de52a12e0aa106fa94dde422f0", + "0xc3c8d7bf89fa058c0d6f16b71e7369570d35d606ba5009efa7c7e437e08e7102", + "0x482d7c904a5e241565b5639ec8244f8dbeac5136bfed0228aaf1f06fa6b4220d", + "0xc9f98228fb37671700c26fe452a8845608379519a432adc1e78713dd769fbf10", + "0xeb1a0bb1342fcdadf3584c2b74ea112da596dfa47248e26f33ae5a27fa95e5bf", + "0x35346b4fa100997ce77156bef44c1d3f5130b90f64caf27f6ff12f1dc58fc4a7", + "0x5d824dc5470d16f9aa41f47155cacf7e702af9b44d740ee1d0261555d6811863", + "0xec346bd53134fe3f738cb50933011647c5b6df720444f66e64026a41e23b6aea", + "0x9acd1477e640458fb6caff6757ab71aae5d7570e967cff4497aec375fce9fd97", + "0x722ed5a84dbfc6451098c52fa92728c780ffbcb5e418450dbb9223b12a5f9ff7", + "0x28dcfb919f6196e3c8b66d57b55b4f34d8cb492f1ab09bcc529ca9c7c2af7f47", + "0x01a2c0fdf0cdc7dd3fca9639b81ff5881fd0b8a00ddc526ab06792ed2108e702", + "0x8a6ffd2ca824daa3c35e3824e05b59736945c8ce276a984fd1bea3392cca6117", + "0xaef051cc3272685541a3558c7f28f95fd0a5a18317f976660490fa6232c68c48", + "0xc85f6cdb9e88596fdcef26b1a0dd05c79fd46b86ec4bd7e3b8e0dd70d7b82b73", + "0x43ecd57fdc41aca3e9ed269c9fbdada6f99ea44214b715ab98e9ba12eef4ee87", + "0x3bd51b99013ad059ada422ca89eeb933d139f4d6649f3fb1be9874a0bcaa8d49", + "0xc208aba7ef37c2041461791082f3e94c49458b56cc3d8a03456b774334618a7c", + "0x8e4cfa48f76771e1c78f66b74e09779cab6119787d926e0fb8f24403b9afd0bb", + "0x4c5e341d3e63e823fd1793bf06060686a7bc90ed822dd2dd911a74ce0e51cc1b", + "0x48d74326fb143d440ed93fac9e435b04c842bb5cad8f4dbeb60ae87a93b0081d", + "0xf09070f07fb592179a4c62b89aa4bb593acf8ee0aa380dea4299cb12f7808cd2", + "0x22242c9c4db12db498f7d48498ab09d0bb31c7d08cc6ac7da7732b94aeb0dba6", + "0xb041deb4fa8d3d7d6b10089e20caff3ce6635e6d1a435089ae63d68876570795", + "0xdbec67b9ab57ba048a0348294fa53e207da00e4de15112a17c4b9461c2d750e2", + "0x55fff2668c741f23d00d90451b46f630cbc13678b3c951b609035c2a02caf444", + "0x2fabf7baec6c1e59134195b0294fd3cad1746c1e9fbd6bc6b1f79d5123f4773b", + "0x592ffdfb6e29e9f5cd3dce577936e9779a9d7f4c38b3520af33ec1a0646fbaf5", + "0x00336bd60c7a0170a47dbf7b7fdb16710832fea201dceef3fff084e6bb0f8143", + "0xb4514807ad9a1dac18097cfe884a13d28c32f2b0d4b405c3408ebee5483f769b", + "0xb9f0dfab2e7882c6e926018a1303dcc2a9654cb9f6fae00ec77370fa8af5df3d", + "0x46221fc915373b15e2ccae1313a1a6875f1e2e6c361c6d8698a453a396906f35", + "0x391ca6036970491c453064583fd79a8255785ef2142f9168aa0f94211f383d4c", + "0xbf6d4769fc2810e16d813583adf0d66a4956f73de8f1620086d0410be662e56d", + "0xd4fd2317ae216172c3469316196c58a0a4f633bb42ad03b8f467edeed8d12f5f", + "0xec6d629e41b86d2bd8a4128e4eb3d26797ab7587b182bb1beca683a5fd983305", + "0xc5d926a2eccbc20c9bdbade8d35ab7042987dfe3c62cf307303bf51c16be5c6c", + "0x305cc472758be26505d76805fd960b5b02f3de28efbfd4b9c83db26b3f910c05", + "0x930b163bd6ac776e8db0794f70feabdc9bc67c21a8943e7b222c6b2bbbe31bae", + "0x372a2fe03336884404cfb40e8e759308ccf28ffb65e3a5eed2578e37346249d6", + "0xb57bacf3713b201a168868c4067c576b8ed3bda36f78df61f5e313c2f504bea0", + "0xdab0c2c9d06a6df1d8603ef841afca3a32f2953ad01a35b20cf3c3badb296b89", + "0xf230fd7c79e7b023ecea7cc9a05c2415678bb7235d26213673cfcec491f6929b", + "0x42935fc47326da47b474a8efdd4105f7895f7eb13e3737f7382194ae5aa8c6e2", + "0x94de6df3869fbd253dbf67ae8344b6d4282191332d0d7ce307fe8415f2d1f6bc", + "0xf845167df0e80fd59c22fb19cc6f9b4e7b4efa54f3a303a7af33672c742e375c", + "0x65839c30d5cf6af04d291d788244518acd39694caa7bde1e57456b016c1fa6d1", + "0x14136b74b5c98e1e7021556d6a843589f5b3b08614b1494d9e18f395a79e44e0", + "0x2b8d405d4b9bb01c3d7a9d70d948c6ad72d4b7350edae87822d591bccce59126", + "0x4611d0d46762b13574ef0d568b70fdfbe4761ea60830e61472a332108791ffbb", + "0x61f43aea5e19774a0f39c1895c570f620ba66a8712ff2affe380fd9c07ab0fc8", + "0x3055d2965c5ea9e4e5637b6218160ab50d7fdc9890b3354697791cac808503b4", + "0x7768d5ab816b51586f5e2209e7045a4952e33c44fe58fdaa398da0eb7d1952f1", + "0xc49817f37cecbaa3042d5042e2d3803916a8760124802dd7d3c97121d119076b", + "0xcc453596fd297ab6b2e64345d65bfa4d1a23c9b34385cdfc45c543a2dcb9f138", + "0x2483d202cabf64c161156594bb490fc7a5d869165c3a98d3f88fe9aa4497579c", + "0x093511b175f651bc2f1d0cf326d7f88943aa7a5b71b479dbd1cbb39a6e662d56", + "0x6b1b90b116d51da26b7ea14f3c8043b8e5325fba25aa9166c6c41a7a4f02d56d", + "0xd334e05fc09cbe24151f80e3a91fa7a5ba89813fef6f8dd8e8ec2569fb54af84", + "0xa609f6beb9cb2d46c612a3915bc6ad751136cedf32d471e03f506e5e3ba51c16", + "0x06d2e3e1f15f447c00b771f467fb4b8f5d436295b7efa26331e8807d8f92bcfb", + "0xf95d73ea1bb1c395aad300d1749d7bac2ca362f319f4d314d30df501281a8929", + "0xe19b1d58aa0141108b75ae8d3a0be9c4c34cbc74099e434b55920a2450264962", + "0x357925f1277a2054b367fcb8b10fe552edb0d754b11f05aa1082f0062954d36b", + "0x469096cb8b0629d4399b32e905d5e5d89598fcb147afbf8559e90f2ba3e96c05", + "0xa6146c04a40f4b0389d94bb7659bc3c835bba4a3bde6fc3c9b290aef4056c55c", + "0xc4c427601db3607f0f1b3dba57d6ab976b8ab3dca58272ea41bf1a54f99ad41f", + "0xf9538383d8b839b610af32376efbd57225697c3db2591890481ef4f805fffb46", + "0x67551b523c899b931747da5ecfd71154c04438540feb8522f15a690c042ec91b", + "0x3944a31953513d1b3be87cc06ef34a1722c322180c5e6d37346e0494e59ba8de", + "0xc1e5e75224e1a467a8d92f6297f5d8f6d698a01935634dc697eb424a465c6ad5", + "0x9c075186833a8672848253a1fd9eac8c5592e00e8df73330aebd34355bb50c58", + "0x92f57cc00bb709fe2a1f13328ed9961a1c4dd6a530121a61ac0a557fb6e1519b", + "0x1dfe008dea7dfccd0b06ce894588d484eb9665685af16bf4a735ece4c158dcbc", + "0xfc18c1a0473914e8d990d7c0275f6b042fbd8582941a38282a46dc8fe094bb77", + "0xcb57037e8030188b657c06afa3e1d3ff32dba791bd43d4bbdfbdb543b286f8f2", + "0x8b2a6c6dfb162efb31fb0f33f07b8cbd2becbf20677bd33396450fafae3a26fc", + "0x0cf11dc8487b0b98ffe77fce9217d42ace4bafad9d99be942688cf2d0edd8e94", + "0x06e91274fb302712e4e290d71b212af6b45e30e5447a067e7863d6ac5a3bfd51", + "0x3ba37e1ef209ceebd6d30711ec71b7fec01ae7be6e288797f4c0f1ad2fe70973", + "0x436b290584638df1704334a9648fb6b47eb9b25879694899e1d967a717a625e6", + "0xd43fcbe13c7bf940891ed6b11a9ed418d7fc5f75bc188bc2bf40cbbbc61469fc", + "0xcff09b9296fc26ac777b7c98a288459dfa3b776bc9fb5c918c7ef60246abd51b", + "0x8f630295f8c4e957f8fd6ca3858eb55fe2ab22bb38a038fc811d854f66e72a19", + "0x94aee2ae42056d450388815ef6b7f9f40880ad6f588264a8f12c060def1ea66b", + "0x4f20a66b26b3fa3b262ec4ac456d95235962a77db322e3c7467cfe9067dfd03b", + "0xac17cb461673fd48493eabf8e55b771048262e996b90f598abb9165360f3a1af", + "0x6e3e093a89c35a8b1237cdba259643c1c76ddf61b1074717036415585ff4292a", + "0x8500d3cdb6abe321e110612e4e36c95b75ec7149dfc129744f3a45e5924e77f8", + "0x4630cf54c6b0fceffef32eceaf4e809faefd8a0c3c112cecbdd134e3275301bb", + "0xa8ea612f339ae14aaad431d3337aebe42376a7ea2b7cdcef94f95d44fc52030c", + "0x5b8e7eb4b4e5b91bc62dfdb1c3432e56732c971f89f1f018ad23ae8c41b69bcf", + "0x1263da20c42d79055acb1f5308c4acb2d3a68c11a6ae9bd99dbfcaf3758f1671", + "0x6f5178a438cfc8826061aed58e9f588639b71175bdbb72144cb211729a9f3973", + "0xed7ef57fef2ae1c45c1f9b9a54a0bf1708fc3e9ecfe7d15a63c41cd54903f9ec", + "0xebd9c34e329cafcfb6e87f175d288ff253d10a05178c33e1ee1ce7f603b7dac1", + "0xf09200e262dbe196ff9b0779e3b36c76fbeb75dac7252e1ba2bda55ec5fdd1fe", + "0x6f74cfed2a134cf89c0396d6086ba6ada8f3785678ad3ec752dbba0744fa7e79", + "0xfaf3e5b482c5986622d7d2f770128d077cf4ab754c1fd54d941f259f95c1569e", + "0x261f85b71278f04361f4f9475104edfa525d3995fbf8d2f0dc8ce12abe045ede", + "0xc91a97c8ec8054f881401bab88d370069fb1cd8543fbffacb8e9d0d629ef3cf3", + "0xb8499a11eba349c34adedccc7c24fc46d122be14bce3a060835f83d6cf308df9", + "0x5f1954ec361b3130468efd891ebffe2280062d9bded7166e0ce46779b9e5dd94", + "0x9669c977e4d3dd1b7b2f7fecc5b9022ef0e6b0cb91aa960095e533c4974c6e0a", + "0x913e96593efe469aee9b400b912867d5c68723aafa7723fecaced53cd41d2eb2", + "0x3dac68eee8d9b456cec3969335268a68b299e482ad1aa69de8ef08cba20e97dc", + "0xcacebf04dd00f9b760a2c62b40a8552a5d26cf3bdce8036e7fc987b6b96d09d4", + "0xb71cc9a651187bd2353fcb817e7d20b1bfba335b730e80407c5b2a04eb895bc6", + "0xd5cfb6162824dbcde3073e457bd7534c9d600590478b9585c14b793fa4461ffa", + "0x0ede2f6404c402854bd3cf7e1d84a7534ee6c6783994464b5762b6614e4dddb4", + "0x5678d6e146156199bc96854e362ec5ee6dc43d399193237e3a41fc2e812420ad", + "0x07ac6c48b46c690acd0d688d332c05bc59a4b251ce5920454c93ccd7411aa108", + "0x5f0a1c25c2baa80cf10d50ca77ec6d57ea567c0cdbeb6f7110e1d5d058782d88", + "0x65a08b755bd9a50420400893ea15564d2e4b99e5230ac372de3f9a9ed6532d62", + "0x452e00c918601b598365ab4d5abb7b6a165c49009eb596d8465704e1bfdaab7a", + "0x309633e68ac55ad4fc694247dd77108f063476318e8fb8e754cea28aff298854", + "0xf60a075542a759f1a8dfdaf636c1d32024f9e0bb275a96a74e902f9cb371faf5", + "0xe305b2f277f28fa45411bff5f67920677d637b6faf3a20a4ef44a3fffa33ddc4", + "0xea656763f68a7417fd0ecc2581c4435ca232581550813beceb91ed8a8b7bd3f1", + "0x6308cabb1d38a3454d2d5390ab960ca7fc540cac9db3012dcbd5937fa2b0bf6d", + "0x93c2bac52a21756acfbd8ad98de4dfe8dfd7993b615445b1e93fe61d33017030", + "0x4f21909aa6b08f18d2781e611bf6f7bc2bf0ef6759332e440b8ecb8678fdb824", + "0xbf1004547057e6f03d6c62266c82834a99004c0660b4626e2bbfd0899c331c08", + "0x0506e714291453e65445be66893d9ee0e8d801fdb3d7ac3cd1f972a69a0d465f", + "0xf4b0af957d5fe7f0305e318032ba0603a597c0732a719fd70a8f5fdaba3d80af", + "0x101e4227eaf2fe7627979d1808dc933fb2587f0afe0ecb90ddf4fc5770144f3b", + "0x15a3ffdf3dec5212e70c58f7cd19e9f2940322f9259d0ef9304a35afd0bd63ff", + "0x5f152d0309c692ca3665f3a0b3355cc93678013085dbd6429c05feff2b6c952f", + "0x62f6de1419226568517f3764c3e38c08cfad70e64cb3c23850ffb05453cde733", + "0x306e0de747fda628ea337c1c391ee67e55f7ea71f1a63a331d2692f5a4382a81", + "0x96c17a364fc2970e8f6efcd966327974024af8c5b89cb9d192bcd82232706fb5", + "0xd21c1422186e36c27f3258273775ec2d04c824e270ac05eab5d2a0c3dfa97c07", + "0x991de4e854c221a97f828931bc13b34ab606d9a72641c3b607a6e24024eb3d72", + "0x1b74894b37980886422c4630e3594b904b4325cc7277ffdd5044493e960f92ed", + "0x84c2ca4577eaa4267f2c94011b361970d1c61a5899659e6fd1db82c5b5ad81ed", + "0xb93d2d38ae591ea7044f80569bd189edf0351187d95d8687cf815ee18b939825", + "0xd8f0f01b1766f82b92a3d9052fe59ee0b6c2f4524e922da06a899dc567cf1c99", + "0x569a00dcb3a8009494fd131a5eb305816de955cee87078554cbfaf8cdfb916bd", + "0x0cb273868cd4198bc214ee75ffb72ba718510c86c1c2ab27531a4cbf89eb6804", + "0xacf93df591c13a5eb1196f796abde9773c1f3ed8c038d7abffb60a2b88e34d1c", + "0x79f9054a67cd634f87ab39dc6eafa2c6550fdcfddf7a3b5bf988af830fd3f955", + "0x10feb160fbc702fc71f72462ee3400ec6826960e4b6bba44d32b076a74611139", + "0x643da7110cbb31b5ae35eca8e87db960f16ede27ee3a1f3d4dfb13e8409d1627", + "0x7fe4dbeb8359e39abc086c33ee4586b656d632c78c1be26b0bf5faa0672e8c7c", + "0xafec661f0e1034c812e8806c6f0bcdeebdade830507992b3e78d3dcb1e225fea", + "0x51e1831a54209618a591f33acd4c4950affd45ca319d64e0723ade5f55abccf7", + "0x7e233f1cc682e043de5e13315af2ecd3018e53160069ba6c9cbbdfed019fc2cd", + "0xb6e5b2658901535be2d67988030edc839dec7243fd38afc60558cf1e32b3db8f", + "0xc4b89b29e3a69b41d0e2ae87c522970b08019eaa1fbdeaa01fdb85909a3c5430", + "0xe9368745c3de598dc8ddb57cc8041a01a22fc8d88b78666a2c2bbdf1f9567116", + "0x9b8d7d08f3d3ffbe0e248d6b95ccd56c11e548493297a9924e85d73d1094e980", + "0x533068158d8cfbd68cc5491d77ce2c9250e1dc11a8bb1a4c12bffce5e0b4cc31", + "0x64d8b22cc811c692b8d37b73420cca8787894ac139d666ee0c6ba0f03fc7430a", + "0x4ece0ea5cd5114dbdada995eefb64ad7401c9d2d140bf006dfc643524fdcb4f4", + "0x162bc41a00d8900302e3f66e8b8b51b7da67fa80fac4b4f659f5589a82245f91", + "0x24ae5cfd0940cbc97d7731789208e82316058ba361f446dc3b7e7e79acb13168", + "0x08e164b85c6ccd1b1af0c3e484d6aa846dd5d6b83f976b49c0d71af2e8d31d8c", + "0x1e83bbe17a4549d5547858a6bd46a7713a10b26f663b7a28ed62f2d0fb6847c8", + "0xd8e4ac9bc8941fbfb0fde3c96d0edf3778752dadab693089808ef6a96c2488fc", + "0x46fc5330767f766b24ff279e140102700cdbbef0934c187f64c6a7f599bd890f", + "0x9e89a00723bffc37c39bf6f4e0749857adb917c9b238a2175f87f18e0d21bfa4", + "0x3765aaf7042da10f0399831ee925de6d9c0d16f2d6742b9ccad9d6f712d2acf2", + "0x257abdf53c6328a4bd02a98a3aed9fe6b50c3208cf3ffb96d44cce340ffb442b", + "0xb0e57f5609bd4591675519c817035a4a2768aaf2a626553f5de47d9d3842c54e", + "0xeefaf8f7c679a5c13a00b17c720d28e07bae6c270882cf73dab49d2b5479f52d", + "0x450e856135b9c6ebf94690c84ffde8001a53d2a95fd81b97e83ba04d32a410c8", + "0x499cfd8e7be5d4cc0120d3d962e88909b440f2dce9f5127551ca706cd9e15ebf", + "0xe4afbe8fb968fa5af0ebb7590e222a8a14c318d1c93d86a48da6e34afdbd6368", + "0x2a5b704e752de0165065411f6d2e342c8e46a6bc8d34575ec544dc1819a706d2", + "0xa978cbfb4273decb51b790a9a272dc87c8b009b4dbdada11b7fd2285683773e9", + "0xecec18dc43c4c5437047b582ca1a066c01cae1d14276acf8dcb98e72d9b31324", + "0x90e384fdbd6b8ea92bb2f81b876a46cc3c7ee22d4936fcc5e8b9f71742bc0192", + "0xf7022f52e1f5adaa4d5ad8deaf97f4b2d81c795ce1c7ff13dbd71fd0c32437b8", + "0x8c92a72dab7442e314e5a5451395b73e2a1a873c1c55d693e22d26d0ed770b38", + "0x13b0fb006674f0b7294f43f3887228681c9a6cca52ce1dafe8de76922521cec2", + "0x3acfe0e780efcd53f65e5d32e5d6ea29aef6415ea3a37788e0ab0f7466f15856", + "0x0e0cb0a3ac26069edcb7cf6e653d259ec9f2d874da6828a3597177aaa9e82f9b", + "0x2df0ae1ab0b2a88b5bd286535ab0a8ece8d1f3c29a8cfb0ae85495a53c644170", + "0xcb86140e07cdca36c4e037fb0b99237fe8e72f751e3f2719e1f43921356b607b", + "0xfff9889a31f2a2b44760f3829899667af69585201d7b765f615833f1b2dca694", + "0x700a67d053ddce1d0e5a9e7448f0c391136d52d43fb1e0faa5dc54bd79118db7", + "0x8758f8c56ea886be6cc78e5cd5c280d1fd5e1139bdd162be56b318fe46a8d07c", + "0xf966043555634d31e59d799e600be8be02377e2bf42ab24061baf15b2a56ce1a", + "0xcf44bbbca2f98248e0805f076a49ffa476d58a514b2b70c802187ac8abaf3e2d", + "0x7872b5e1a05b1f1cb17e0d4338531827c44610819a9e48a99716655211fb474f", + "0x8186554bd80a15d9f1edc2a5b9e610a26fe5244b6ea693a6881a0cf286716007", + "0x2830829b2c31777faa1042eb0b62677cb220bf659410ce80e3540c44ffd97e90", + "0xafc4eca78202305051023598f34bed7c3c3e83b6bbf0e6b39eee00c45a7bb4b8", + "0xe13df7c854f2157f17464fbe8604bb3e4d9ec0705a6bd1f1afce9f41d2598a48", + "0x1aa8ce96b29676ad954ca70ff4375f7d67f1a3a6d429d965ebab99b157ac4392", + "0x4e7f5504da4c885b6c7bc581400ca452375a54ec6ed88e211f7fa0deb1489de0", + "0x3f383b4b20c8bb1fff667e1dd5dff06248d0fa27861ddc09cc41e7908fc611f5", + "0xa6d44cbcd3a828615eee0684f850072b73fba21b0721530c5c8b3571c8814c2c", + "0xbf92598b603dad4744f283597f5d956f511b803a9d6b5cf1d9f21823bc95726e", + "0x773882ba681b897a924fd4a6d5d209179f7e3a36a1d150686f8f54a6521a359e", + "0x18f672b07717fdf58faecbd9f9fb69ce47deeb122b463a7e3e7c0ea07a4ecaef", + "0x155b2825aee1fdc7d744f407fd1877c5d7f585c7cdcf36ffadd26bb39e30026e", + "0x1d975781d1ba4c69351e06dad154133597f13e84514388f4e66e76ae7312c020", + "0x5b718fbc26a49a91441aeb13f188d7cf72ddd8a143a40e9549d40ac87a7a00e1", + "0xaf032f1c92e0799a3f5cc71256432a8af880043f9cea82462303fd77633ce2be", + "0xc2edb3c5b4fba3dec8f58d3d85f84a850d44d8fcfad8c55af47039c1f346582f", + "0x8e521d788b06b57007d1898415db38044b6d59c3a42dffc49d9832e734ebab2a", + "0xae2714720be1d097efdb6ab34f079150416d64696f97482122095578ec5b8181", + "0x4dfb56ea238ff6a3af4cefa2b452b8453f8696e682e2434eb9e229724edf6c54", + "0x8d23ed085c0a39795a54a66634b3c4c8ed65756bd3e4b82da187fa27a381afbe", + "0x6200e80d8a92b770ceb321cd8f7044b6f8696107f6edc66678322ea42f9752f5", + "0x504bc182d1eaae3a33cace4897e5f2f4d1f2202c94904daf11aafb01ef2cad05", + "0xb47ef77cda47c591b7c7f1fa8def27b00e76a9f24e666004280ccd736c0d7c99", + "0xb2e344472883bc17578279c73f195121c40195fe1a942fb5fde8ef329d2e4905", + "0x687dc02a60ee1bc67ed37dd0adaca50938246bcf91bd13a09283d9444bd994a5", + "0x0707ab86620eef5a21042a7e621704e5c390a1097544cfe9935ca164eb9cb8ea", + "0xa9c59531eed1de34be5daf10c5c8d9592ad75190519302ec84f4b655e11222c7", + "0x66227f5222dda891441d231160805c7196cbed6e73d052b5e54174b032ea06d8", + "0x8286f16ebe212ece9e48ecee45059b71acd98e9d5a7b778fdeb8d34209c9be1d", + "0x8228458fc9da8cf2ade9aa404587374f444f2daaaa6214d83b28da7b95b7f45a", + "0xfe91fd08c1feaa0b8a576a4e90a2c0b8e886ec60ac681f8def4ac829d4e8cf8d", + "0xc070eb0d52404f55854a470a174f04b78be389bf7f0b0e053e62aaa949f039e7", + "0xd7eacdcc19598fe065a360287bd0011fcf79c9e2697e527e904a8d57d08fa6ce", + "0xc673f5b46e09ad70807a81dd5efce6e2f9505431448342ab92fd5390a300c46d", + "0x6a72a8ac1f2e3fd5045e610ec6b58f5175b17740a3ac3effe173e3f4ff42ed55", + "0x223c4826b5e5121f223187c112556c8ce88a18f4d83fa6db2c0060c9c6e214e0", + "0xc86ea3c14d8ccdb5bfd6bcd9b707b9860142fe1decaf9a479d7d2350a05bcc73", + "0x858433aaf19c57481c650d0e2d003985b733f1ec9910ba08390662b87b589728", + "0xda8aad01c29edb9666e8d0d135440039fb8ee7c1df5a21d42ec35209214d1635", + "0x8e7507e28cc24c7e24679d38935bcacefbdf15e324f8ce8ee4221ee1e3c8a596", + "0x6f32daf51c4ae2da271d12be5c90645fd78bb944684a6ef311648541d7cfe229", + "0xd07ce931097db44c793a541d04040ddb91d719f4af449e784785187464919f3b", + "0x5bc104e0af83531ebd803993ce3f4033d208e93db51d71bc3b0e26353c2315f6", + "0x8b0083be4612494c786c8ee169dfe7c723f043c20cb2e2a080331d5c55630270", + "0x5e872e0f0d4640dda435b41f9cbc991f58d818acd65e54c71b3b485b112f09fc", + "0xf61e2366d8da1bba2dda295db6c0dcfe476863b1772867b4c16178fcedb78219", + "0x52a479a084a4b67d7427873f057b831495ec758b5070e2b0fea844f8bb6a7684", + "0x69509f7b16db6893b3e4305f887c4ca2e06158bd950c8aa2aa6c8ec4b4817dfb", + "0x4a324d75cd9187ca7cc3809246c8ba1087a97c18d4d66f56e18729ace31bae4d", + "0xb53db61922c96f04f7cd90f70cad632001d24a9e8fbed52d863c7b2a67e00ac6", + "0x810e37709895682dd59c4411df0f191a584a1ee87c2acd5c2a8568dbe326fc84", + "0xebe5808fad5351f848d7381ec3059dd2b212a89982da4fa7ae75b117411665e5", + "0x96c0a380426c1c494a35339576b03f3b01b95d5cab5ae1744a372d2f3874b6db", + "0x294d93896cc6fdef10876cb078468d4328b42a06ec0225c043beb36380f5384b", + "0x5e6a98f7b4b7319725173e10d7e17718152a5a1bd3df87de8a35f340684fbc10", + "0x85b8ee8e6c20298d74be9d3fd436461d701c98fdf223820f8c2840c93d5970ef", + "0x809a3d7c3240610e42afa20f7d398066cde718af78bb8aa4f1d1a3d70d5a7a10", + "0x762dc449b3bdf200e8c324aaa8b988969a559c2efcffb79ef4a0d6c0ab7f566a", + "0xfab687fd056a27f0367c7f2fbbd6992cbbf6c32632c2ab528cb7a183836b29bb", + "0xb876163b4be077cf4213f87ad7516bc3a0d774df9033aab8eb53bc8072cb23ec", + "0x696c84137dd405b6e4adf5b672f2b3176032ce1387d3eaf8547e046700264594", + "0x3bee4c6a62955204680d326017aed22d99aedcd6d60563bd219eb1831d25b9e0", + "0xfb44f1a0edec0b27a34b840dc33af5c319d26250dd9cc816b574ccbb18ff3704", + "0x8153bc277e115832e651df495c71ca49774c59d9da1da3275d778c757c53561d", + "0x61e1fed63ec1532b8352e35d7d711bd384733392740ae5d291bd0df267639d1b", + "0x3919238e97e0ce570a152e068698ac033c6d7dd822a2afeb70370f78b9dfaac2", + "0x035d09dbad5ea4209374bd9fbcd5777b67c011b79311873d09e5c360ba217de3", + "0x25eb0a397dcd9e821b583eb9efbb22e3f03e59d0d2eafc6e00a676dc6fb3b027", + "0xa4020f840eaa378a9eaaa76368fb1d72432b0a4f77948f51c005785176e5a49e", + "0xa3d4c7782746280db3454d93bb31b04debf6c8457366920129285a1dd36651d2", + "0x209dd696a449df11439dbfd24e7f1bb3c83ae21838217c40c6b2137f84ff87de", + "0xd355663091a861e258c4c31a967d10e67890051129ab757090a0e076021b25fc", + "0xc6afa3ebb0dc975f30e0ba6777b836eec6aa65c97b809f883a13609576116b32", + "0x4d1b540814d5850ff4d40f1a779ec63389af1613fb6c7366e8a9f5f7239ddb3c", + "0xaf18d49069e398afdadcd723f91e0540b344fe28122f23c56260395ab58d273e", + "0x095aa9902e14bd2fbf05273c589a62570d7b998664e32fdfd7d4a11f66f29285", + "0x8356ec8e3cafc2e295712871dfd3302f533aa2479e8eb42ac8538e5758e568b5", + "0xfa866dc0af4257792a8e95eb1a96ccd368050bf18bfa578625d8574032364de7", + "0x70b2675325a63dc806be12554ef69a75853ea0f70aab96d409f426f97a79aba2", + "0x440e600b09699e46bdecbfe001c5b847a3e22a8ebfeeb4e33ecac75122f00d87", + "0x56b54c39aa348e615e39c5c550eff6c4a8acdabef7292aa8ee95057aeaae9d4d", + "0xc511be0eea15958482b3c46ba98d1e895cc345707f95390fac0a74fcb9bd6c48", + "0x9e8243f4130e6c3e3e785137b201a7f0cd857fb09af71c9c8530ac7b0024ac3b", + "0x5592ef8860216fffd737aec819f41d96d12f85f0fbe26964aa009f6b92287d5b", + "0xd7e0f36c8ce3ce2bdfff4a1f673dd98744b7e2d48c40b6e7555b4116dcfd74ef", + "0x94e69bc17c6343a3e8fc1b45278fcf6e2544ef1054aa797eaaee4f8baec9d169", + "0x362f35607f97e8e0c50bf9c57b8c652f2b30a5480a5157045ad121ff895bcae6", + "0x86c6027b0f5cde42100c514b95fcfb2e9f5edd9ce7c62f921a4828483d03ae87", + "0xb89f1f22b9b17bd09cdcf03e62e3c160deadd742a881ec083a62e7e10db0385e", + "0xb700f3578bb91cb058701a8853ed9f6a8396879ba80799ae0c35ceb0f965a973", + "0xb0dc2e7503b4029bb9ab0347e8e5abe9fd04d098adbee7d593414a94c5f901ea", + "0xedfbce63a4961a1158122632e865f5f651d320f84624ee04e2c765bc0c1807c7", + "0x42891ef7a80ba193985f575f33a1da7dbb78830be0f5a53c7aad93c0f10eeb57", + "0x23a0dfb3bad707b1dfe0d7050055ee8ce5dcf264d0a38ae9ab227ff3ae4588e4", + "0x0e7cff0d9ce0fc2c64c8d6401b70b7015cf53566399165d6380df6695dca9e74", + "0x23ec98f7a736d324dba82e53d2aaa691f318e910872ebbd3ecaa71341b6fabe0", + "0x6e90e17d2133d1911bc600477e07a5d8a5d31bbc5cd8cda31b362e977186a48a", + "0xce8b453aeba2d613a5dc5046d7468dff1f85919377aecbd72df35943f932773e", + "0xf87488e91cbf94d7f1a761f587022c8c00972c83cdc518edcc0790f7d40fee12", + "0xb65ae804e900a5aaf50630ee79869cd2996d7df232b28b7d734da2c075e9d6c4", + "0x807d68c8698473a157c6a3516717843219fffc38fd4c4c4bc846f2c0f4492a2d", + "0x3ac397a59b700f014b9355650f38f8564526891a222cc18f86a7424881a8cb33", + "0x1aa73a2fed6221cd3aa7bcda7a5bd828e49fa674f6566db269bafa636e8cc05a", + "0x1cefe4fa3303cc0d851072f73d5654f2606a7d56f5cd07128ee86cea83904258", + "0xd3eac32136d014a62b4d3d375a1c8f1e6ffde565a7e768491e340a047f496bbf", + "0xe73da5b02c18c17f0e3f562dcb3b78f037c73f48bc3b3cc1e4dfb1fdde2dfd65", + "0x008c64f863150004bc967c81f758875b6eb3ffd842bc55814bbf24de212c5f92", + "0x08eff98d854092598fa2c1f3e506f25e5c9379ddfcac58e3b6a3d9c46aa15c01", + "0x33d85013df6d9e7939fc3cda5e22fd1237fd4bfc4888e9408d90bd868f9a2075", + "0xe3b87027095659a2aeef4afbcbe85e9c01fde26c44d2942de1b90cb1df7ab5ec", + "0x8e3fd12a77c51db4bb2ab5b6d064b644100d070aad502ed6ed4f1e2e7b28c739", + "0xdd3c1baaa39747281f4583a6c5f6168ec077be038614e1e7db7b0a99bae0cacf", + "0x601541a2c4205333513c44fa3e4d7b5479f22aab9f7404b3c808cdfcfab9b59e", + "0xb870390d66e857738c4fa89f961e9cbc990978d6c432da83e5f4710b6668ca4b", + "0x276260a0d4292c0c98be81c955f9269540e70eb29e11e7c3d71e3120414d9b9b", + "0x8d6e418613c9aed2844ce918f5f1ecada6d6999850b8af54de9f2a429c4e16e6", + "0x69d8282913da797d60654405b10304be8b258414c9bfbdc9285bb22cbdc08bf8", + "0x62a79f1644f5f8a2fa7cc6037a51db0361f7d38e4d31cd1b5f7c4c6f039a12cd", + "0x5f474630283e59f08b54a34ae79d05906b9a36dd557a9482693487a0a16b3738", + "0x9acc2696e216f3c71e15b3b54da15cdc815f9e1bb6ec49fbc55317609cdfa048", + "0x34b011047fc40b0d91399f7658957432d6a4304bbf4b0d118109a957a9d7641d", + "0xed6efcb9fbe8104fb448b43f52172c003976053c5222d05b196468bad20f2207", + "0xe93a9282b5b60b4c39f83a0d9185a0f1236403b32b881b1c580bb64927db8a71", + "0x028bb763f359297fab1883ced661945136d57321de8309c276d420c432b00aea", + "0xb9d7ad2bae24a572d4676ba631a600bc9237e650ca68392b322cd39cfac72fce", + "0x4c5e80932ea4bdf36cd139edb80df1d86615cc6c8c49e50d1f2f545ee50c7ac6", + "0xaf88970dfa14d773bf458d877d6de86ad12d1b3459e6719f96245998f5f0a869", + "0x81f9c47488704f0c7ccb98d109709ac9043031a54235a414a708a83405ea5075", + "0xa9653349ea7784d31c3bed83c7d9befb6dc95d2cd9a050a3942ab08f70e7c7d1", + "0x381b1f3d5b5d9bb4d0908e4b9e2d77e37f5807063fbc07098170296c81e60530", + "0x2ab4727173d5b72f301e4abecb294afc0373cb496d10c9185daabe299ac33808", + "0x8077691afc316fcead340f0cde02f9894b01d2ce2ed3c2c61040bd4a811945fd", + "0xc5caedfe8c0902875901d0c88289a41627c4dce39c858d2684312c79a1bfd016", + "0x9289665d06e4c66f5f4ed67551096af4b5600a108d385c83f10b78e4dd14cbbe", + "0xf864f6a2af3cf00c9ebb9123b2f80d22cc54b7950dcf2d88f2f39c0bf3b20ed0", + "0x19a114d93ea5af84cd463a3ccd9fad00580a7e2f270b5ec33f48fd2c7c0fd34c", + "0xa94877f1a29cdb06801953239495f019ba0fb441d3ad7f325c45fde3627f76f0", + "0xd861516ac767989484ef24125ad140753815dc2ece88fafabb697d75d7f970c3", + "0xd449406fed463a2e0c5ad1fb0f2d39fe3ed0fa54d7400a4b591dffda1bc065d0", + "0xf83dbd12153ab153b60f07ca939987d34bbcac2d27fb201ca7bfefff006fe3f4", + "0xc1647a539fa5d5449bfebdc65e6720d7a45582611335b0a6a7d2b2ca5dfe4ac3", + "0x28bfea7d73e6b54d46b442a618fd779c7e6eba738a4126047ab8713c32350784", + "0xa4ea2ec68c625d2c2b2315a2b2f00dd6ac096c38ffb120ef1d984215d38cc227", + "0x289e1bf62095b9abae57cd1a25ded67915eef19f17e69f3400655cedfca02b34", + "0xd758a7987a0de2752de13447b51cc139afc8e3e323ff53c2507b0fcbf2f48c5b", + "0xefdb319709ae48843b2faad99f369a0df8722490c66606b1be29aa2d4a1300b4", + "0x8fa7c94f1bc6708eac291532a43a298d62a794c8936b35fcd0b7b965ec353b70", + "0x503aa3b92cdbd053fc07955814b08034636da4cabf9f0f1dbda835ca50924b1c", + "0xeba1194cfafb652c4e54105df31cd9fba4bd6f78b93f8bd77abd0674e54c74a4", + "0x3513cef9a6942fa03a752252d83b23faead28664fea69079cddb8950f92deb8a", + "0xc7fa037ff3a1931364120a1a149deca14dc0a8c6116caadd0428ed6ff3456599", + "0x0ff323dcd5f1747cef47c51c882ff5a1fb3ac4f4d4153937b0a01523d224582a", + "0xe26bb506a9c32f4583cdc5083d6c21cfe6e9888feb25c97b2fa03bad32c29260", + "0x6c94f6f128a09c0160be7030e4365a2bc1cc96740ab3adc4f51109b073113979", + "0x28c011d833bc1f1f0b0d941c83dadca9b77dfe6422d8943cce62ec9d4e4f6a46", + "0xebc1d22bc681b90db746ee1ef3711625bfa8fd2e46f3d78d8e34f394cba82a30", + "0xdf77b7792f4647f1f1945562c76ab6bcabdd537fc72d96522fbc2326553ddd82", + "0x6ae7f75d0d5c050e8bd7e977d88cbf656ceb9ab284570d24ccf2a62758fe1b7d", + "0xbe4715a2535fa8e97f496ff58aed1532c5eaca7c598fbcb1a24359ac03df74c6", + "0x42cd7047b8910f3a9803ed5469896f559af19c62637878fc5b891e80b9e3338f", + "0x57349fda57731d4920fcb8af080299ad39bc15f3abfc106b59da4c5812ecfc30", + "0xe154f091b2d481a663055ec70befbd158c769f02d3159f4dd9d740ebdd8b2bab", + "0x3e9ba425b7615ebf892966b5bc793268d195998eadabf6d07202e08f636e9079", + "0x3918f586cd522297adf1bd4c5b9c80d367fce2ae60ddda99e2b384bab85a0a32", + "0xcb50302386a1791ddbcb24d480f651dfa5191355e3bb9f208deee98651367ff0", + "0x6fc1184ed1d0bfc055063bc1f0b8e9cdc2d85f935aa99cdd21b5899cb3cba4c3", + "0xbbfac9586bf90a8cf57e4e9b33ed90920e0a8019b5d32a8de2710ce15567c619", + "0x3dd4c8fcda4b6603a3dcd493cb69ae4ad82dbc47fe1d4739dab38cfab5665aaf", + "0xcf5ce6574ea4eeb44867eea94caa94b06619ac94ea0be8c0ad3b943890eca7b9", + "0x5c7690978805008a6dc8e174258c02cfda96a93a805034769462e09cbf328475", + "0x9d3b108af5e8d58bf37ae01d7244e0d63bff672f1a39388981882d4555a22d66", + "0x86411119c187822c30c389bf55168669fa376af27cef0c5f6a13beb8fb0ffd47", + "0x5ce48efac655826285aaaf0971af0815e2b70ca2076621466512bbd82fdc3633", + "0x027bd90cdcf301a2cd61fc03a5efdcec2ecd05ddfce07a280f8243f9a5ed8d06", + "0x8493a789615b031b2b9d175733daf757b5af984c9b9ac42a1ecf5aa8bc21fad0", + "0x9c0e223bfacdbbdcf7c15fa637a4f37739593a325f03f7091e9d81e2c46e4eff", + "0x97f1ce59ed26cf6d5b3152ca0885a0b6efaf1d9d5cb11687465164c983f84fa6", + "0xa5691a8b7cf190591279dc12118e656bfd4d27367fd75275f37295f928c0fac3", + "0x8e550bed46703565f8e1c626e124eaa00c9d45a061bbdf90b551d2f61d9c5993", + "0x8de080d5346f88f6cf270f9b7b8b616621e70519220608b98915f4d5cc7535fb", + "0xc2d6f352913329f1bdb7c03491c6bf179f26bdf9b854de4f33aba1b1c83cb5d5", + "0x33e7950b16e1e6670f22bb368578ae5d1683b77e917cfd947a0ff038b7e25655", + "0x04867bba207dea1f9a73f1e5c64ec4bbef5778a975a77fc31268da7ab9d8aca9", + "0xcd537f2f44974d4180ca1b0e99f60fab666b9ca664ef0502d21837f9c9055b43", + "0xd049ae333217add87cc817eabbd69c54408867fd146376d205914814d1317045", + "0xc6c369dbd46980a97530375e8f833a8b3b98a095799017ac9cd51d1386542f31", + "0x810cf7f6a541b0442c9b154b0677a5c97243d31624406eb69bfd22139dc3877e", + "0x441af4816e8ef7950eaa7e57398972383d698499921b281dfb3911aba6708042", + "0x59da35645312c725c216395c71bbfe68a5735951a264531febf3c1c05f17d080", + "0x747861e371e70fb175ace1ee283fc039cae79b6e47dff28740ea493099e1896e", + "0x0e1b72fbe97458dcc5aa51287e260020a039c4feceb7f6cb400518883f2b273e", + "0xc097e173ddc16b689f5861b3a0447175798dd4e670f0b6e3b84bc7723a864f1d", + "0x7aa248c1bff11f71dea22a50068b751c87c36216af3cb4acb1c53767cdf1b1c2", + "0x8fc6d1e127da7bbb182d03136e9f3f30d6368860011f4a223fec9e403db9a3e0", + "0xbe1d47d1d86426f3e9c0ceb18b4d9b257bbe1c1ab074d7152d3c716a5a53f7f1", + "0xf6df1b4706a45a6ebdd524743afb1c0ef9a7153ec8904839392029cf2508f01d", + "0xa1c2b3783858c3b6c5a769365c105d6b17b010e437a4b979cc3dd91f7e3c7da6", + "0xadeb1983bd7f1f0cf5231eb377e5238fab5a0fe3ae53155b6abade9684cadde4", + "0xbe1f1a7f91d3ad2ab109d937affdf0ea1b064e0ebe97d721a5e74142db17e2da", + "0xd1f62f4f823c975d8e0af8764023ff2aff3d1784a925d2c6890a5e0bdc079f5d", + "0xfed0d14e4e7e33cc9573eaf30b2bbe3dcfca4f9c9625e3068619395a039c170f", + "0x19ea5e8b214af0db110ac956abf9ea3faa07fad3ba2a66938dc67ad0851df386", + "0x048211bba23acf0ff35d251aa28d0898c3e3fab8ba1244a39b226bf42c697d5d", + "0xed4c730b647d9e80ccda65e849ce6138dcd407dae5ec676159161ea9ac102bf7", + "0x61e5673fb52e500cb929d7f75564615b5a0cbe10653b07e7e6f2d3d90fd3c575", + "0xecc9caaf9a59e4cce17846dde3804b6fd0ff9f9fafed0d61fc3874f00dd09d13", + "0xf43427f6cea3677ee1344d795f2ff715b043bedd452929ecb289f0c48d2003c9", + "0x523998202f4c464391d934526e4ad99dc244e9c6a895504ecd14f4231d295912", + "0x2c7bb7ae6df6a952c22f842ac70cd2b29156c36b02f2b437eef2dfa3b6feabe2", + "0x91d14664e464f57aa40e55f678cfae3af49fb10ed55c5f196b12c572e4b9976e", + "0xf8ed5bcc93e216dda3be24bc5382f67e68e8809243861c217856355060d7da2c", + "0x7803b38a90e04eac61153f7c822cb6af8e7cd98291dad3ccc14744e755efb67f", + "0x781992b75ee77116781cf7bcd61e450a72817839e697423f1a9aae9682fe4738", + "0xd1abb07d480615328e28cfec056492ffb296f36032f9f1d1ddc6f385fa482549", + "0xb9acb0d9c6e01f968aad1e290c9a77003e0f1b34a5def89500fc940b7e6b12c7", + "0x67b36ebe804125111aa2f2dae76da0f2795f896285a7b385c44751746e97b5c3", + "0x82fefe873a43b7034c219b89c66a94d6ae8b0093b7ecda92ea1b5b50ce4366c0", + "0x3fbe9003a9dd8c74320e8416859e67e328340e8da93a0e54cc18929d4b24dc9b", + "0x8fe618c3e86943a7f72e617bd997b0fdc8524524427abf275b6b9ea54f6f7ec6", + "0xe178bf9d9d233d6cdca97172432d119fc541b315966df1e8821a2129f5289f8c", + "0x2a168e954dd003e486f19880a62f92939990599b3f08093fa6cca264495f608d", + "0x59943f8b6c84f013953423f42ef3346c3d73dbc3bfa6e4e92fc3aadb93b8a560", + "0x3bd6b7167468a3c233c0b0d98b4a257e1b5fe03e1443456845cea2e028d55543", + "0x19bb9799bcd9777503499b82e15235fd423c0b5ab10e4ddea4d38f3b03616f57", + "0x99db2e3bfcfc1b2e6d3789f6d096ef98399f3bcda95771597e2c1938fcd5c8f3", + "0xe6a856110670c4ed025bdf5f74cf78ce650ccd6c4c8d97a41fe9de261e4465dc", + "0x2c954cc10d8bc1ef96b410913e6f399d690a1366ae318edfe11a3c3993a9dfc8", + "0x3d241473acb34c1d49e7699945e3df7cee540a8d4012ff5153cd5f24d902517b", + "0xbd714d228874fe6db16a381f357579f4a195c9b8c3d179c0836b64d400ec7b84", + "0x19487449cb0d4fac2dffec1e5215b484460e7ec2aa3dfacde0f9a75fc6c25dd3", + "0x7d78dff475cc21ec598f60b07132a406602fecb07a20f0a05e5e191f567f5d69", + "0xc2ef8cf3377f6236a199df46e2ee588904b7ace767142558a2e3a6c443b3647e", + "0xd116802563f75e96ff31db2945364f6166bc22e575c4532f2e07d90634f358b5", + "0x543619a770f6891ffaad27269b67980b5f8dcb5d59a88d09c811657e83ae8afe", + "0x9bee7966a663e83e51e2efe5a8cb8cdac5d61e53a5361bd6bc61ed678ac5bea6", + "0x9e757e3fd5852e540ea25609e6b60bb2e96df0a5f7883332c4a66fb4b0fe8455", + "0x959e0db21faaee74f395a412b4003f93cb88e86d042b9467946be57a019b4762", + "0x73492e6638e41f0275dc3d9b5e27aa388cf791bb51809f5e04e099c925d22f7c", + "0x4bd480abeebc278a607f30cc6447b45996e56a6856acc73237222cf93e1c6af5", + "0x1794ffd94d132b53a763103a5cf9da60d7761626cb4f89de3bd2e660f37de743", + "0x75f70fcc0334d4cda13b112af1c54fdca53a3e7e527cf57080f09a23b948d2e2", + "0x4f3ba83a307dcd1c365e55e598e79a1875cd1260fb1d6074dbc744f31d93c491", + "0x8b5e157186a03eed24494f3f979ddf388ca78f215bcfee57e28641b5e1918e86", + "0xabc53be846c9bc9868cfaeb4b37954fbde431c6681cfee3a4ed9f52abe5d1d3c", + "0xbe5b880cf0f23e914742886cdfd54d7a0ec3415bbc611daa9ab3d84073907446", + "0xd33463579981cd5cef2995b295de918ca78c48bbfa193fa0e089d7e683ffb080", + "0x2d6924b27bf798beb2ce44bf7b723aa2845d4d2547e683353f3def36433ad494", + "0xff8214e91f281f27a82e9e1c47d234c695e747981b0307c7a1db4928903589b8", + "0x51425b92d09f5d2e780adb9119416a2daabd4e85bf73af09f6d6e7d194a5052e", + "0xcf710f90648c84e34470eec84064ec033a92c9849c8e6d979c81a7717cfa0bb7", + "0xf5ec96d9475fe607e8bc864a0f631bf0c5c4a9ed27a796628feeca53a48a3067", + "0x94ca7098c6dfdb1370a662ef00c3f97d876451f6cfdbebabf8f3874a29351be5", + "0x7a1c16a114d05917f5c9d7356321000342a20f1882c3dd4f1cf704807d421364", + "0x5fdd5519bd66a039b815685b22088df7fe617785cb56d9ee8fc6ce13de3b3fea", + "0xe560d7ca65de1801190892d6177ddd7ee2cafbbc37d5949a782f3e770923f4b3", + "0x6ec993904bf3449a4e48fe6d2bb247deb30e0be3bb42dfa914a1e4c6f1f9eca2", + "0xd91124606366981882dee578d2e81346407f6e110524479327c5e91fe6952009", + "0x17905812680b532cf67670fa3e2fe4a4092012ad442497897247ad41832510bb", + "0x1de2192f5c44cf841f2ff372d0c166d9ed1d2705699cc71136cda77c1f738d35", + "0x88c3d1eed26539e4c85e3bfcb6c5537210e9e3aad17bd150afafc0f2e0ab84b8", + "0x80981825dce8c2318115adfbc85b5d65b0c2b76838263dc2bab9b30f164901a6", + "0xfb643b763b0c5abd1dc9c0fa55a253707b2a08f5a1598e77244d57268a8f1460", + "0xa8a867913188a19c63c37793bb188f3b259653ce8953653d036bc13aaddf5ff4", + "0x150959da82a74f61e9c9baa00b37b62f68f37e85a0d8e35e1775e442a7e74a8f", + "0x7c9e0bbaa688a8802f5d0814634a4476b2d9478d043653560ce0d58fbcf32fed", + "0xd5b3c69e54a993345cb0ffd2440c8b2ede8b8ae5286cbfc94db34d9f5884eda8", + "0xae8fd50c936e1378f605b1894681216d9dc58949158bb2d062d917c940e9eb2b", + "0xcc31bea0808307872e488b219e5eb340925c30fcc806fcb6c20246b76bc03132", + "0x28ac828a7c5e7d7efad90b56a8b1d31ecc9646030085cbe5ecb3459081d8eca0", + "0x634b4b41b913290a3236e4d6f74a42718bb9b0671dd43b1a8df7197924c1c3d9", + "0xd68c7893744dbb35fe1329efe52cd244c1fcf46ba7d521f13aa477865b547ef8", + "0xd289a6b6dfb1e652a75d6f2c327d9799d071c9704f26a61d79483150c0cc5f83", + "0x15c71bf57ba9a557e72036e299522e262b0147130b1e852544da5869939ce710", + "0x346874a533c5ba84f34783851a1321a49811a69f5d9827271a8b4aa8395831b8", + "0xa0aad2269968115829a4e9b44b5fa70bb2b443b235fedbc3ecd69b2a1e4fe909", + "0xd76bccab043657810fdddb8e574f3953995ecc3472fffe80418ecf5ff24cbe92", + "0x96eeca708f3e5dbdc8b4d860ac16919ad237a1bbbbf72e274c1398864fb1decd", + "0x91a590d4df693b1e10da142b4f4cb5e2278867cd99086d576d2a12cc2a321508", + "0x8b316c0ac11cfc98291b9158f6526674f267f132548814a32bb2bf90dbf43a48", + "0xd65801099ff48405e7291ca0e8a78f5808276f599c027d026927635742c700ff", + "0x8f78c3ad1edf3098a7f9d140fb346a39c3203dc1d57f6b3b6d20e17aca62f1d4", + "0x6d61c7105e64b6e3d87ef44dc9c78b49a269f4081e6eb96c8527b9cc2e2c8283", + "0xdb34b74e5757ec02998a4a42342ed8d3165b6d7a7fe4d7ca50c13dc848c544a6", + "0xda68d958ed483283907bc933194ef9d7091536c0bd22774e57ad2547edc3c7aa", + "0x0f1b4ea3706fd970630f50be032716bd20e9b71bf352fbcd7c39bcd271af861e", + "0xa683b8eacd8a2563fe7d3ad66d89c09cd71c0971cf1c70c44f934270ca05b1a6", + "0xb042af60e82a2c2a14222a0dd2927f8176dd3a78039e1d85aa30428305767ddc", + "0xf9beb3dc984f5eead664ddf69234ebdd3937d4582ee9913f8dee2ff1dd130ebe", + "0xbd9dc06d2afd009a2d71f8a49bf74a9738e64d970a68a4a06d3f5fb6294c411b", + "0xab6dab64ebbe71901fddd997d49eca5a2c98cc92c5da075032791976eec48101", + "0x18b58d9b0ff2a869eade11bda261f36dedfb472638f623940d12ef11748200bf", + "0x76ae7ae4edbe78fe7231cd9b2b83868fed14c8380828b81b7dd227eca350f453", + "0xcc01a5484c1a4ef48e36632e9352a78298aa68a4ecdc4b0030a10831d7db2cd1", + "0x3ef664e5e10480aee767512029de91d404c619663200e80ef0babdb49f9d2c52", + "0x8cbdd1e59ea7ebb8a9cc92aa1c6130e5896e6bf9531f6a972368eecffa6bc2f2", + "0xc9ea29ec44cb17617509520f01b325763ff64a20d793e7e3e8d4bea8bc6a5cd8", + "0x031201468d786dc77c950a82f3f21168caab48cd635c6a1a3970420be63baf2c", + "0x32b2ca1f79fa9938c990705c03cdca59f52844ecd78e1dd4323c1ab4018a0cd5", + "0x44a80f7e3f4fdb598f2f47b39e905cedf8bdcd30cdef3422b0464281f9bfd2c9", + "0x87d78bf9baf7c3ad7e6bf9b8a704b866dfd4d748515f22e89d5b7d6ef47917eb", + "0x7c4d6e802cbfb1090e084c0cb9ac454afdc020f54088bd23fbaa44fd4763ab7b", + "0x9a6999158b47c4fa21ea35ede47dda2dacfc4e7eb00479345da3cc30a3af3a2f", + "0x46633d9f9ceff7188ce879ad53e8282047bcaa573d37ae909d1ec621716b26c2", + "0xd43b92936e8e77f55ecc5c14773aa20dc8af6fc190d373e743d9abd96f5216fd", + "0xa0e858197e25537e7008c857a2d4a6a0c43433241c86d05e32a5419131511c0a", + "0x7557e453192fd27b4bc78d0584947f4dbe887697debc98be5a61dbc6319a1f00", + "0xbcebff3c0aa7f23773d8a6a0c98dde40331598f3223337fe230c6f859a903ae1", + "0x01e388d96fbaaaa06e8f9a3d4060fdfa1f8f98cb7b853e4c09a38b7bf69954d5", + "0xca3a0d892e1a7c5a1157db5be5ea2b8d8217f04cb80f633f507ea6a416548f74", + "0x13fc9a373776e25a5ea3cb4c1b2b79f98697b4b834bd1aa1e2cfa78ce4fd5f15", + "0xa409ccc7e9809c2dff62c55a9caebdac25bb028a274a664ed609c4b2271d92ce", + "0x585487fc12f4655ace9f7b1b75fb5dc2a6f0634af572aaf79c6639cb15941c82", + "0x11ffa50f73c26f115ae6d3165d3567da7af4e0add61c406a63a415a476edbeb2", + "0x4d2b71ed5745d894ad3647bd1a20fe1cc23578433d838a920678c0500c799d24", + "0x2669485c840a7a5955588ab792ed8675bbb577c2b8c09ac6e395f2a0182660bd", + "0xe325016877bf83a5ee16f4c70be2cb3f2a2d6394e7fb23bca9d29c19337e8a5d", + "0xef6cae05a97c2246637e8b0c5e460484be6b34ca00a7419fb1f535e34a30bf12", + "0x49e0c2e848d1df1c449898d0d91f6cb4282bf5f47197d6309cb11d48ca1a1b6d", + "0xf684537853d9e283fc27d3f3dce04e18399160e98fb6f73dbb15582bf90de12b", + "0x747f37edd0eb9793e4431a7371a29c66c75b406ed7d389fbb8acd979b7119bf4", + "0xbbd9a03d812b704eec318304d1eccbde91b611f860666dbcc7bdf95993615599", + "0xc4d15b09bca904820b6e3fc32d202c998359e88e841c8013061a460fcdfe3ed6", + "0xcf5f3e9acf516a0a15a7ff5d3525b99a25023210b55d6ec0d317a2a33a957ef3", + "0xd57125f6e6430af8ad2c897342ae1e5f786c9105eab2ef9fd80cf3cb0129f303", + "0x9ba72c65a322ef28f9cd764f2c601c6bb00eee058e53d08807a1998b74fa3a37", + "0x19a36e3a30dc94118a828514de0f24cf93fd77ddefea714c3059456a5c40fcb8", + "0x7c202ce18beab82c74e600b3e770ee7db9504df70b562883b548ba11ddfeb005", + "0x5731c39fca68192cfe0948bd1717e3f82a9be6415bd99e7eda199940679ef6e4", + "0x9e07be93a805684e07e86b01ada2890e0059312f6a3fdc7883738480e3d91375", + "0xbe406b87234bb2ca495dd919ec153d21413c9bcb3abe16e880737f1dfaf781ce", + "0x3f6d44d381bf06c2eedafb32f35d2ef58eb245f3cefe30761a385926278fb60a", + "0x41e88a138102c1961f5b8798b52547883892179c9bfe342215ec9e0ca5b06ce0", + "0x0f787aa2ef26994504c395fe9f57394fc022dbb247a6b35a4c7ec42380ef2c40", + "0x517d02432c04303da718ec38f6945936411d1b27028002b6257001fb942a7cbb", + "0xbde87f292905cec630e6f368a9219504ba1645f64e8560c27767c6b2ddd999de", + "0x817ae79f4374b8e30749c313d23e4880c19e7098c4676b69ec2ba50b3a6fd158", + "0x650058f9cd8e840bc193d8e639363f094b0b01420213019eb4c4c0a23be969a2", + "0xfae84492e613662223ec78e0590f62eac593ab5fbf74cd355885db5e9b7507cb", + "0x5dcda187da51a82277278640e65d45501056ce7cdcb31c93da3bb382f92b17b0", + "0x3376da3bb27481fdacd6be3ff75a0cec60e76eb28da47e430038da2b9fc22a1f", + "0x1dfcacf6f70bbfaf92f109387e18175b5be1ca854ea35451cda6077055e9419b", + "0x3914b59b0088a096787417e1d933d87167c0ef6bd4fdf2784f074ac95f4e34e5", + "0x6c6c867ec6a293c2f12f32192abe1494cbb65328ec4f1c7fc4e0c32f9d0e7ad8", + "0x2b4cba0a867a044e0bb77da39c5a10ba08dbd4f8d0c1144279a0ddf7592524e8", + "0xc7d84b55316623ee75401df816d8c966daa275c77709080bdf857a483df73504", + "0x854cba4c99682895e44cf8ffa40466853301acf86fbed82bdd6528719e1aae29", + "0x7921266787cd0c3116888e73351eaffa6cf4dcddfbe0903ae9f5891d25cb530c", + "0x028f7ffdc3f75d2493ce445b6b70f1938bdc044ae369a12edcd243b38b6936c9", + "0x88ae59b7652c359653dca8f18b50ca25cfb0c730708b1b131d10ab61507f8856", + "0x8415125b74439e788479890883e670663fa269186be48eacaa01d12d9e74284e", + "0xa6db7d6f515999cf24debaa986042e4e5f88274dc6b38ccf5ee25cd86b31a151", + "0xfab4346fdd5c91bbf178b12479e370cb55960cdcf600f83bc5a7c83d06af1b22", + "0x45163d67ba4e6159541de7f5c24e96f6bfa7eaf50dfa995fc1cd5936d973f509", + "0xad4b1933dc7f52a61765a07223c370265d5730d8dcb9c9678ba57bf431058af4", + "0x124f70c99d0b92421508b2d7d285e3b28749764bb94071a58a52ff33dcf14b30", + "0x10597f848442fe998b987f67e455a4927fdfada403bfad1c1801eb7092586176", + "0x648ae553e91addc950d51e4d73042590b044968633b2d92e469603843d59b255", + "0x4efd0da6ecffd756b13021a0de3fab07d32907378c188ed86f9077c1cd992ed6", + "0x1438720d768bf8502c122df9d34b1c98029f065a3c988025902cc9214aff86e3", + "0x8abcfc2dbe235574f35e22c5b238264ffe44e69c67131d741d4648f9d4a1fe85", + "0x6ceca97767ba262c8aa5f202cbfdcdc59080cfddd8b4db891403123e5ff7a815", + "0x804291bfdfcdeab71135e45feca4e122a7b8231cfa0aafe7794d0072cda6fb57", + "0x97c995bcdf3f42791e127c7507566013d88004b5dd78ece4a65e1d5957906fa5", + "0x0a4c77c9c6e9653e662d4e66e70d2c62361e9fbf982dee426cb0dc4092d43f28", + "0x3e32245023de9403d72660964a1480d8dd45102d1e2afd0bbdf01ac58a5dc02f", + "0xa8dd30ad56f3d58239bf9d0b08a2914ebf6dfdf66a504dbb2d789e6e3f2bc370", + "0x46a21ad07cd70894c5fd99598b36edbdf4f07c94cbf91ed088f37cb1155738e3", + "0x30cd558150e070f1377e07720da2183a9e5e4b0b04aa059a72cef540f4ba5626", + "0x0ae3f2f3b6b38c8b6d710b1f432f4230c60cbfa32a2dfab4bd09cb958a6e66e9", + "0xa2d1a7e4428721b0d562681ffcdd7ff0f3da01f33b8f11e5edce9395f23c50d1", + "0x8dc04653f07d2796f79fdcf166f687cb3d6ab58d18bc5c5cfd7a4d50552db499", + "0x94cd02393edb52691f1ad4ad490458f17aa57fe2a4dd7e64ffba14317b991c6d", + "0xa5cdf096b64bdc8c1cc45fe38adf09dd1b8ba2f5c50029e52e49c89da5856824", + "0x049b9c9a1a526152edc861955f3029df1b45873111d4233e47cc65753f8e17e7", + "0x5f4d75be9a485a3e359a35e59123eaaf52f565bce44055b0eb85d75ea8e8eb98", + "0x8d75117efefd2e96a1a8a011d055e97bbc7f3d8ef4e6c2a253319e0db5bf235e", + "0xb70f05142e70a0df57094672e08cec78b9a5c2def83dc32b8aaa223812e08647", + "0xbb7343f2cbb8e75b60dec6dbf068520f96d4d67c949bee575465473525c3e255", + "0x5d7bcedcddd0657116adfa1ebba29564b18131d9138b96ec124b0ed516c63307", + "0x753c2e35248665e87ef3ffd6f7d51c8f1a13a01d8057048a1a7204a6044600a4", + "0x599290272fb76d327383d56e7d3fbafd0fe624df6b5d80d717381b8c31cf97aa", + "0xd8203195edd14c62373b39f0e5f948ea5b935791dedbd8844e7edb57431f0bb1", + "0x50c0690550b0588cf665221d37ab230f46e99d26fb8cf665cb51b8d1935385e2", + "0x8e8377ce3ad1dc8c4d8f83320418e8d9258bb88c0324eb97b6ffdf66e117f3bf", + "0x44e03fc51fec6fc9c95ddf077238502c7d71d6b91e370179c468e98f4731ff05", + "0xe0a69fda515b6ea05158f1598816fe40c6e51615b4f46a26c14071e664f8dc14", + "0x8e4f065b8f7bd8e210a6c3a534bd2c212ef6c69db5c1b1df9409aa2b3e8ac13b", + "0x7ba1d56fb6ccd373c7c4663bd95a8286efac01145085f8917dded3c4f222daf0", + "0xd82696d56c8c4df8f111e267d132b0ac62491da6f0b22ecbe53e342235881a76", + "0x50a2dc88966947336fc723250e31550b158758da92145b02e4d12968566d55f7", + "0xf143281e4f0fcdeb30d4c5a78d56a7d6663b6a8dd50efd52696293bf665a34ec", + "0x8c5149f20b84bf5749a1b786b719dd2d8445d7d695e42d74657fe7834174b7ab", + "0xdd4502e54b396490b8f1f3964cab418ddbb9c334b62832bef486d3f8949abf8a", + "0x49c47eb4e4759450c261f97f4ab8e245aa5031a81fadcc9b8e694cec8c5dafa1", + "0xba73b037a55194e0532cd90b103d3006e78b0b9ef5bc336f1149ceab90f04720", + "0x64e518ee854823583fed05593cec8457119db0790d8d2fde65173d9f3303c993", + "0x149ba43635abb2968eff6554a32605889283fb7253fba79cbdc967989ef48efd", + "0xe3ac567bf0a54e8724037114d8cb9301b53fe4851056dbb4053ffdee94a0671d", + "0xf90386ea8aa2e0b33ce7ab632d5a0128010dbe3c51a6c4db15ded37bf8dd9828", + "0xb5d25b253571ae0c167f0c32993049e8bec1b3e7b450ba981f6aa87f3b47901a", + "0x8824fa562ab8fb0c0ef5ebb0673ae0f20512e1f71126a82f250ab6f714ccb0de", + "0x83b42c8fc2be82b9bae3f157440b0c78c7aee7ead6679d576bd8558893aa317d", + "0x8bf1fe92581173ed72810d91e91f2eceddbbea9ca925af0c3e3b9c5fc4da07c2", + "0x4844502607798817dad8783c05985745abd661c03d90b62da677aaef609916ce", + "0x9ebb449b96ae7e5f8b81f5d6ab401454607c04a7095407fd373080e5b34068ac", + "0x64c070292ca8607cd30b5ed49188e4324b253423084985838d7f3f7b37b58064", + "0x01b93c851d77ce8130275c5fac3c68cd23bd6aefcbf86bc799cce79d3ed7d74b", + "0xb18652d5f80547c4c16a0e81926aea71bde838528f63e0daf763f43be9d63ca9", + "0xd4ed6b27698b10fcc8171b870bbe3ecee19c9444b7bf2d18a16f0751a27028fe", + "0x3f1d1cc86dc6006e1014afb23be958c84c381405432be960e0ee3756e9595ebf", + "0x33cf4ae767f606612f7782b56aa8b64e3bd857d1063efda73acff082f9b5fbcd", + "0xa2e1e37450b627613ab49162e1dac0a19ddc555b1a870fdd743a208f476631a2", + "0x79d8e55d88134a9e03021959ce07c22e5431df9337879088cfb2ced4f20ba0e7", + "0xebfd4fb66c63026f45ad32a14082b48d7fe97a313b6f6513d2f0939394d5f63f", + "0x5c40154e5aa2d1e2737e3e8259824da3a3b52625f887e8a3240a2dd35ab7cc9a", + "0x3d428b4bc0a081d7f50dc026457a61747fba095dde2c0bb390650e4872869d8b", + "0xed07711a10546ec0e9962f3ec6a93442608b9be0242586b9b342dea709d86b69", + "0x716dd1c9851f5044a92bff17e08e835247f51ec56b59ef3916b51f4a4aed32a4", + "0x1a3096424f307bde9540a8196bfdb71ac64b5e4c44e235c2746de7450d99115a", + "0x50b3522272938542653a25c774dde0c4449780a21a29acf010013bb31a166753", + "0xb39fd9ce89abe4dd38bcff40cceb11a3c2edff1a2b27a817ad2d8449009788f4", + "0xa38267f9e3c9dd583d832bbcc217af5f11793c0ffbc97b5735093f2bec71deb8", + "0x36554830a34391558c136a980380b273f14d0e6d2bbb8190d3622ab1dc3787c1", + "0xf4a6c4015f6bafe79b0a912ab5b3998aaa572f6277995571585c06d5c7ce8e89", + "0x59d05d08689cad61b4c6db726a7585e0aedab9087a3d176e43d81b0875a9c89b", + "0xc5f199fc6599c984d5758196b38790a3db634cd4b956e1df668e47401e51f602", + "0xf953fe215258d6a7c40c0442fec9ed4dbac24e24544452c306fd290a214df3a0", + "0xf3c251ca1144e0a907978c6d7aabde4e55b265255aa396cf44fd6caae6ebc96a", + "0x1aaf7307b4c5bbdeaa3b17900744c3e92c34ed4b5fc251a165493a294480f61c", + "0xa07d67cf121fa8456a9515cd3348cc5632796dcc57f87fbe4d1f7a6200d57608", + "0x3caa96c644a3f67aedf1afbb28a41daab50ccd16caba47d32ecd175136dcd745", + "0xf7765a837742c80ebe72585541e3692ceb38335d2a245919c7daafcfaf0b0fb5", + "0xadab1415e079ccdf474b6924e464f9ff396b7ddb549d2ef5f9fbfb558eb1ab30", + "0xf9bd3e87f08ab3097c0c0115dc135dc0d8cc288e4e7c61f628f9c38deff45404", + "0x75bcf128e04c84cefc0aede5759fd34df222ec881ed20946991f75a100700d6a", + "0xcde4c76d9163eb949d21b563c2ddb34adb770e185b9b25507cfa01ee9e9edfdc", + "0xaf5a735885cc6009217e95f4c925b0197ba1270388f93abc5a6b852287fd341d", + "0xbb80364be4f976927862975b68e1b079eb86c515d1a2844642bce8d845dd3fb2", + "0xae37ae09fb2d9fd30a213f27c0613d61a7e665a2c80bf6c10309a8f9678a31bd", + "0x814315013c92f95bbdfb1b2c550d8a92ec781c496c56df17d10d8f0e103bcdf1", + "0x4a1417dc167bf98d72105b896d4d27977b9b89c36cf3a69f49fb78b5bd9a5fcb", + "0x4e3621dc782957129cf35fabcaa237cd27a70af6e35fc00552e1128cd47b3569", + "0xbd2c1bd64bec45d62af9c1865d629529531fcaecb36187dd92a24bd42db4f6eb", + "0x55f9dad3f2186158765dc151e19b7e4c3cbd734102950101f194bbedb05c913a", + "0xf96436be65d6ad46a23e5e61bc8027c5b6f7855f99743f4686ba6260a07192cb", + "0xc67e196526f8724ef54a65a30a1c866d43a572274770fd3464549ea3a7907761", + "0xfda0d2321aa16a90b35421b77c8b17a145b6de85e9ec397126b9403efa5bea46", + "0xd92697ebee94a7484372ae4201ae7e9616998ae3ef39afbe1ca8684d4218fa7c", + "0x7b4c71cc86b04328b81a4dfbf6d5a13712cbbf94b968c9d5723d2d977cbd80bb", + "0x6167c9c943adf502a359f51971c2636097e6b6a9d6f1aa4076defa77f05f5ae3", + "0x7c274084d190fdeddc6a76360a1c6250e14b2468a5e6a5c5270c6a9941494ddb", + "0xd6560b1c2c0582aac95b12909682d242ab47e1c232eb18fddac617a873d0f4be", + "0xa8232d4399fd404a7a1da7455acf13ae31434b73b9d70a9a2ed69a036c2e2de2", + "0xf4482468be6afa45c66870c8098586a0fc5ed865865461d9a4f651d350f7b69c", + "0xdfd622d0db4b72a00dc617eee1d0b2c4a6d6dffcc4f1c7d181fc51b49e24e64e", + "0x42a47138000cd1928170630b42c639ac290c6676ec306cae64cb11f5cce1de1e", + "0xd539899eb8d9dfa6dd17b926e4d5f99c5fa3e7c660bc0d4bd5053e24f6c70920", + "0xfb77d2fe78d6f4c0cba365c6ca518c0f6cf9f6105eb21eb96a3494aa231008c5", + "0x9ee37cb2a46673d7713e6aa3a60f8cf445467559301a65816df54f4f376f0173", + "0x247203643d08536d24a9a23969746c066b47ad9caea169c57e9776726859d347", + "0x2263d7ebee9b7a4c0a43dbad6ada40621e5f3d267aa237f58cad19d72bf68155", + "0xed37867cc3a505b7a48de146c87a3c6a04634bb1be44dfe8f4f1679768362bf0", + "0x5aebd7433e269ac18d1f4d821ba56b24500c3bb8c4806a003b26226991007375", + "0x3a8eaaeda51e940209389b2f73853b15699f95e8abf572f214adac5ac17cf630", + "0xd2a4c54d6fa9d9883e58787ce3b6314f5f018f63d593986272beeb1f4abed10d", + "0xe044047461fefac9c2ef121378cf4c0c2501d2dd71c89d018bda3df3dc2cc614", + "0x1af2153867e906054a28c8a9db17c3e55393388bd46f9b7d7b471953726c7d12", + "0x9998af7bc98e10d64f6ead467a53bb51c86aabd1791278355cbe1251f7fd24b3", + "0x3e56ee5c7863cc06918cd4d41fa0566debe07c192ed72a55896d62094cb6ee28", + "0xea4f6d24a24bff70a8fc064769426f6c12f266309fbfb28e5a21a66653c9ef5e", + "0xfe40c5bfe403db79b925aa54a60558e84b08d831b969ef5b5bde3a64c53f5c39", + "0x8d3d7d81a89e7eff2bba7e203ca5fd92a252fdd77e4cbf32ed05350e5e30617e", + "0x7d0bb056784f324b47206fde683330a03c298f0feb015bbc73590c845ae4c06e", + "0x295f28567311d598bc5e69e5205f50f9989fd426983313cfc7db5f0a2a68dbf6", + "0xc346cae9425ef9f9bc9cbc979ab0265a7eb0e830c94a6a01ae7dac8f6210f3f2", + "0x6460cbbbbfce17205fb4b10e3437dcb27a7d567294ebaef79e5e313aec3ae249", + "0x2bcf17dfbe90206f08684c8d52a038cc15af712443ecbcd084de22fd8ae720e6", + "0xdb335b9450c527fb55d30cc12e6cbf6d6dd4324b63ff999c49d9c778bc35fd59", + "0x01f1b163779392a1126049332a5d38ff2fc9f413a60fb6beee9f41fdd5d93391", + "0x9e78c054b7c935260dae6f7d9fef8ab86eb395f55d1b403bbe02b9b76caf1b26", + "0x1997bbea9a65e9dd3adaafe7980995ba6996374b035d6f72861ac5397f83f294", + "0x82896ca97392c0867ef473a97c62847f4f66ef4d586d0742be2e489c91f09c95", + "0xd3e18ab630ec85bf77a404055afa5ef8b7cd7a2518977112251b5635af0719c3", + "0xe64a3cfa47293e8c9f96188c2fc7dbd12d3821251939fa1052ed6a61c330c154", + "0x83017bce9d6a113ee7f50563eeea1a5cf3b9ba6ac16923503b709f95e01d563f", + "0x4b53a57162ef07ca817d6410e468a324b9c318a4a7084d492f426c41f6c11634", + "0x6f7e14a8cdc341cb0cb06913b5f94c377d3fe94c9cea370b0224b21905ec1f0b", + "0xc6e2d41d8760b9e5f6fb7e26609c0c120052dfa8eb4b66e309482c5c5f593615", + "0xbfc2906422424bdf1d07b0545e4fe0b5f08f978b7ceae9d4891f211974e4f4e9", + "0x86b4dc5970bc207ac043ffab1ad4cad621f072bf9647b11bb28d432fafd74d14", + "0x4092393152214062491b3329f8fd815c08faa9d95bd327f7aab15c8c8fe6d5e1", + "0xd8d164cbd2a0c5b85a493e5fd1c7f3da6af814a69cd2fb2121db2d2ca4123e0f", + "0xbf6fee7ab3f0630f83f91a9d5debcac6c71050c191f81b1ca6ec48df01086313", + "0x349cbc29fe889b003c4b635c6dcc2055a9a4517498bcb02e9be24180a1ff6ccc", + "0xc5b83fefa93207771f5f1121ebd126a9a1b03f4661bd73cc909e451892b3b581", + "0x62fd6aca0714883446e44d952b71f8c9ea4f9d3f34111a68c830713e17852be3", + "0x36dc389944f0aa08cde3223b730508a77da69ac26f2bec5854a5528816966a2d", + "0x7c400b204d00c7d50281becef2c18e420ed55d85dd66e25d3e6c01ffd6722d62", + "0x46df1204abcd501b147c9fb0daf937d9e998ce2566d333ff3582fdd0880f6e2e", + "0xbf14125d0d1fe2b8e895848b87ac2c3f0f2205cadc0ec64d385dba321b24f4de", + "0xdd6630711f54b40f30f986c585ebda8b68112125deebadd32846789ee4da0248", + "0x41a8650c6d9e53c042b9b9312ccb90d765b4e627c7a4ecfc1b53ee21e02f96ec", + "0x717e965e67309bee50caf73bc7cc799b2c1c1b1792ca029ee2b0702a935c5a7d", + "0x337d982a6710ba1d811f551ab3277ae35fcf3684bdd37289d143559ec44daf78", + "0x31a90cb740bb1ec074a3d75b6dd9a86f4d3dfd5e670b467299fba28261fb1c27", + "0xb1423beb57c341ee5c8a3b66a285c3f944a1769f193be03b11c57ce86835b19d", + "0x1b2d34d0605c853a6b050cb311e1bf58f003c7a802c3a5404dc10d0f651fc5de", + "0x42bd069b09bba4066e11ef2398914326b5fe8dcfa41bba93a10c186cccc1e00d", + "0xf6d4a99d0c52e75e05ec0fd3a0fbca3cd223d5aef6b9dc6e54d7784807e84bbd", + "0xcd6d324e14b7b5a231a202bd83378b5748959a89ee584aeebd0c739b3d2cff7b", + "0xb40fe6c56285343e291f276da7d9dbadec5bfecce646f9ff03f846ba708cc2e9", + "0x30688edd4ad77c27a909d19fa8085e8f6535738d94d8ed6db13c84587285ac84", + "0xf0562cb5c0166a6cb324b17a6b59143bf3e0e107c061335c94c0cf4ca72a9a30", + "0x134e203999c8fd0fb366e7300a25832abd7e7522c34752388c059df0505c38c2", + "0x0dfa2684f700aeccaf6dc71fdf581ce5fefd413b5122f8d8486a7c23391da6f9", + "0x3cf8de74190f1f57ab52553b88d7cb810b07f922c143512fb773f3584d7eb872", + "0xf8a9ab1ca613c0b34cc9ab3cb6cd23c3f6f555b45d9904482647492ffe9a3fe7", + "0xc890847b91d303681dd0fefc594a45dbda37b973d8016b69336d4bcd86b0932b", + "0xb6c3c6781c0f3f6dd9f8eed6bdee3e9183c7fed15e7608ea291e02fcaa572f36", + "0x97104f01022f1c95fc743adde8db172be6d4d6213b1243cab4bf650eb605989d", + "0x240ced7dd150987a33df74643aff4f52a2b132a5c7cedecc6c4e6b852334580e", + "0xf878e9dbd441665438a400bbf171d3fc435ca26ea9828d2f926f3550754bc26b", + "0x501f47864bcea6447ac2ad9b973725fdbc7fe7f80e460ce0604a009e3c74fda5", + "0x1e5dbbb4a91fb80505e366e83ba1388dbceb258dbf72d490a28402339be9a586", + "0xcc1091e5a5d47bde2e7de3f4d22c53aebe30c11cb58ed1e4d4fbbd46d35e68dd", + "0x85101eb9c095edbdbd7afb234e84c5c3c4353f729803697c6c4e5d0e0f88a729", + "0x3daa652655c924421f650f9d5dbd4dfdff658aee980b057e3a431ea595bba704", + "0xf09c99fb4edba0f958986b5eef28808136ac403e747dbfc97b393bab0f13d043", + "0xbf2bb780445abcf8d31d4729c52c3aa50ee1c29583bb73c2bd237290b0a2464a", + "0xcddefb53f252fb562f50898fbc01a6cb4b8db9b7196761ad0b95b8595966c9e7", + "0x2ae009b0f2579129c855722853dfd2b4dc107d5c409943910bde6bcf7756c33e", + "0xd884df1ce7d2ea18e2b9099bf160060c240dbd8b847f0a90fe7c267b1fbd3d79", + "0xc1abc7f5b9ecf933db23bee47cdc8985b7e7788818c49babad000e1dc2d4d22e", + "0x80ddc74ed5ea70901f8eee4714b571cc78e82347a853ff33587f22210eced0ca", + "0x48355555f9cde8173802672266a2b2135667d9ed9aed5b2cff3ef4e4f1f052c9", + "0x2b6e4377d8a8caaac9dde05885454bc512c50208428bb62fad34d1711a626a00", + "0x6661c433363cf2df2c93c315a9bb2824b6f571ff9b0c76b12682a7142fe6cc38", + "0xf7a4c6704bb756a48b092c919487b37902a2f05ee6fb6f8e2d9b7f11ad33ae4f", + "0x8b883cd630cf3847f3a80c553489fc06fda27dad85cf3ff5b21bcf6ce9a1e8d9", + "0x91ebabcabe5cdcb7a90359b1c6b1319239e909400486e970624643ef064c6c0e", + "0x8251d8b0417b3355a9a2bace5cbc889495cca07424c0b6f17a5fbe58024e1a45", + "0x1f7a4fa5ed69a0fcc3234c2550dbae7e219280c5726eeef7b9d9e2e98912d3c1", + "0x64b0983911177c1b89147cde576afc9f67dbe4655d697e4c22f7b6f0d086826e", + "0x4a61b634eea9161f0c5bb63c5d4ee0a31032c04df7fe335e6161438ad47bca41", + "0xeaf6e9f4748a594f1de2a153eb7846cb89235d76f684f6443668d709746305d3", + "0xc9e4861abae30bd72dc0d38e7f5d9ce02ebaaf82b60acdbe4e1571b2119bb4a7", + "0x0247b6997cff7f15d3b6361a53e7936183019b32f48b9dbe3c1b2d45b79363ed", + "0xb7e5a0d6ea51ba3e88cde0ff526a2f082b444fded5e6cf354cd7594b999bd123", + "0x89229b1698739211a2a010e5c5e3e9bb0a52f37ca677f3596ec945d551d4ee88", + "0x808a44ec04fb68f43a758c88fac0e940a0e88e936c9514a7377d62ccef49909c", + "0x727b620e0c04d4306711e668634a1fd3efdaf4f0b02d5df0833eefccb09a74bf", + "0xcdc23c9b91bb4988fe1888c2a3bfed123f4de2c0131ff25050de80cc141c2e41", + "0x542048d17e3dabdc27ec879da12ce1dd7df881978570e2e6320da0c1fa8a35b9", + "0xd35bd4af202b9ee0c278d047b6e959c22454af7022dc92e0b9b2fabc0e06ad67", + "0x78b6b52a0696ab797598f3c8ccc5d411b42dbd63d3019c36d4ed37072ffb842d", + "0x99f55cf72ff28ecfbed632fd6dc65c55616e9b1b1624ba9381595f5e473b9fb7", + "0xc9c3acb4f813a39a102cc31d205549d275e7e860126e0d00ae8a7bca50e95fe6", + "0x55ae06f6cfcf54ab994c3a2b45a0fbc55fff2d0e36c7694c5126e1884b8b43aa", + "0x553fb32e04ee70984c51d7714aa47a279c1c221ae67b66bf5c91d30724d704bb", + "0xb787e9725b00f2e2469f556612ae0f8ae44c6aca1a3befe240e5281a096492fa", + "0x81b2ed20eb9f98d6955eb2838fdd12a54524de99d50d6191e2c2df925151cf19", + "0x952dac4aac6e525244e522495e0d4e8a485b50dd60e2e51f50fae21af0a65573", + "0x080e637f121e3753abbe7afbb93c152082e2908e3f706754b6b38d9eee2c9cb9", + "0x464a247bd05c606a33a5b598a088131aa119c631ecff2755e256d01c0d19f498", + "0xc8c51cd9df1950e7658cfe2fa9a444680f5b4e9548d1753c047d18b07a2d29d8", + "0xfd0dbbc38a7e5c17cab021253f00261881daaf01107ab5d35cbc0ffc3594f0d4", + "0x8484858c238bf2dffd7260f927c0facba640365e46556420d7ffc790d34c7ed4", + "0xe8501240055ff15d2a0ad2d3910ff555e7e635b339b6b3d3ca57a30f4eb74a76", + "0x0d2f44a6fd889d13e89074a4dc2cfe9e7a1aa31442c96ac2cc3cf0c07c4692b5", + "0x831d63ce0ef9becc531ff89e1474eb56628078f6434b7d73f9dcd31fd9ed7a98", + "0x3147df756dfe46432a1421ebaef9b655fb3641c0e9dd020021c6f5e2501f3417", + "0x976c964616962a27a18286c74c8e6ceea0c173e9d4d83f29a3897966adb1de86", + "0x7c2a940537c7adeecaf029902bb82d80a6ad445cebecfb0b0a44f2c8eb0b7fc5", + "0x6e599e225507310681c108cc49302cd5a2d6127cf7cceeaddddfe7f61c0b73bc", + "0xf5ece1747ce35ed07a14225b86584c792aace8f30aa8e6904bf58c0179940a4b", + "0x93983a82d70a15d49ba470a6f11aa713ec5acf6f7c276d9d08fe60ea15aec6ab", + "0x5277c6f5b572aed24719383d24c80c21208793e6b0cdf2c4b935b35c44e68ae4", + "0x5cc4baf96e568f36eb1389bf07e772fd11b3a877660958909533ce0d81780439", + "0xaa6ad8fe09cce109036b4deda21a3ebb7db38932af967b8e51a88544334a98de", + "0x2d052a99068f57ae77015d0babfa27de2718d9cd156abe2e1233c484b3f3aa2e", + "0x501f9dbf415b9564f08f6bc4b2108112024f3b64bc15ee326414997062e0d1f3", + "0xbba223fdf8b4f4d25723c17fec58df3e7f04150903bbe25b426807b950aebdb4", + "0x8f8bf8972a1a580107317ea48213b8d60a1a0177d2a72047b3c55675fd19347b", + "0x2f18fcd126e5cbfbd8969ce17ac99281bab91b91aff2ff7c0197c8297fa76c0b", + "0xdde0fe15517833f0f264d481a34e093f69ee7543bab40751a7246273103f4a1e", + "0xa2e99ed9aebafa5183c0116db8f36e2902b5aee36d4930284d9a78deb222ddf5", + "0xff2bedec32170da8750cad955da7188b59f8980269765adbbd77a99b2452cd92", + "0xc052db69d61506e101335a35c585ef022c3b2c2742957e4822404fc6b125b4a5", + "0x7a7184650a18c29d5519be197d19598c71fbc7e282888f90b155681ebb8b2533", + "0x2f83a158c6eb15718a7c7fc56545c261280522cc09f51e0a359f6fb5d2aee085", + "0xadbd448ce471643bbcd7be15acd7a59cd5d1bd9e62f07881d3b90de10ca08aeb", + "0xa7558ce2bccddb207bbeaf49296875f6714eef4db6ac8d5e903da11aa4a95097", + "0x7df53d2abddba5e8a8ee8432487e9f7a9171e78eb00eb90a5a02e66a1daf3ca3", + "0x519be2b847456374d489e324be0196a96539a4cac7f169d461baabd82e1d6d1f", + "0x3ebec703d636784ea1deedc29a973c4839f45b6dfd26441024ba375716add0d2", + "0xbdceafba540baaa854a8d16d0e60e33cfb5ff59acacafbaafee83533a5782b93", + "0x6de79e0e1234bb4c92dd423301f955749653939c2486881240d6bfd7a3286f19", + "0x2592130231e94f61e50414fe2fae20c3139f0c6d712357255ec91efc81fc25c3", + "0x005ceda0709eb70fd2df77e8bd19707dcd0068cc12b603d5881187276a45561b", + "0xf4eff49665895bdb47ae09b160812a892e15452e2ec71150b43877e9e61a1256", + "0xa054a31896134ebeda69de18f392ee7ccb8f5535e9d78a30744d7834319770c4", + "0x02ca03dbdb5a56e798fbd20019dd37cd605978a388eadb408a8bde1081f17600", + "0x91d70f076bb9467cf86e3f33f75ce2093b40fdb9ea85bc7a3a72ff6b82c2ba47", + "0x1601521a8702293248f54415849a60381e9ce96720db92f8c4535fe37e628225", + "0x58a94efe5fa198300112b89f5a785d81b82d9e833216ce113da95ef32ed27152", + "0xe734ae8018751646a3915e10cd71181e6c08afc4a3ef3fdc8435af64374b729b", + "0x46c786d34ae14f6ab3acd69243bb1585591b4e69d42f785f58efaec2b24401cd", + "0x2003336aa85b123ce8fb501ff399a4f1b08e35d489a59b57eae75da504c35876", + "0x994836ae14ec9c9332b3561bc4a29ece22224b7879aeb6bf4ec5a731600da188", + "0x0c45d18647920417f0b7c7afe9ab50b67ee227799a4f7b82253cb6f1ecdaa294", + "0x087a8b67c61791ef262ac024e871346cf7978a2bf6862dd678f63424df12ff28", + "0x016a6edf690af5102916678b343263423995255493701f76f30f47aa09be796e", + "0xc09630e1eb8135ce8df90a87e50aaa04e7cc31dcb719f9fa03f815b6d4ea65ca", + "0x45850db88f4131359ebc1288d8d8dd48067f9086d306e5aeba8a07226f35fe98", + "0x09d63f7f6118751cea50294da6b69100266cc042b2ca36a275db8b56a7062add", + "0x4114a0c74cdbe398228988c73f77f147ecda8536f6847d2daf6501dc1dc99065", + "0x6a91913745ad38db3c5e1a414e807a27efba133f3649f514890f4922ac988955", + "0xc49ca9a3ccaba77af47054aec03d94a5468ae215157ed7e504fce8f6179bba9e", + "0x0a4d12ebb07d6bb054325740079c0509bd424c75008119f86b2c1054b175d1f7", + "0x8efb92fc4ce249d52f056a81f242b1066b8f9571a2772e9a68a3aa5937d1ffde", + "0xbad0e43ff43e3fa9579bcb57efd5787938eb0d1a5d56c1c42ee776427d709e2e", + "0xecd3e01e0dd0d4fc52b392548fa0156a79db973d38772e2f037cc16c8c8681f5", + "0x0ab151a3fcf68a909380c2a6455362bb5ab8fb1e0c618adb4910ac2468744bb5", + "0xac1184f8b6a696853412e73705cf8e544bfc12a89d61cb355ae3fcea393e8f10", + "0x5e04e878078cd5c3887e96e41ac2847bddda36f10209a21bb253a67f9e8886c9", + "0xe5e12d3a1fdd5d9dbca0a4f19dbc3be337cb1bedf81ca2b3c5323227a53e9de8", + "0x28f9b6a9e0ea1596ace792259dbdab408d603dc3f7b614bbd26b3d6918c6647c", + "0x62c0c229d543100ad7bfc2d542570c6d50d562f9b83ea4df469035850b2702fd", + "0xe8ff394f5aafd9776ec6276e42d0055372e9b8470946410396465a08962daf65", + "0xcaffed661ce4b7211c06999a870d69e9b8a597fd20b82cf8386b4dd96f06a405", + "0xa7a4210d0ed0e8488b610582fd3e5fc7d8db2c94ef4df26acb6c2d607660646c", + "0xd6d58d24f92ceccbc8ad1dfb7eedb5ee084f97e8df29b4673ecfb1fe20e579c8", + "0x6199221fe4470977ed32254843598b62c62c13516ef8448b3877cfa1d927dcfe", + "0xbfd3e188ba94d24d6d0b61a8f19232a9da2fd4b39410d9079c9e3f1211ec452c", + "0x11c80431a2315b7d91f6e6bda4cce798ce8d0b2c785542612531a9dd5b89c41f", + "0x616344d1a145eb81cbc7e37ea5a69c390c29d8c094493e77efb1618eb3624bef", + "0x91d1a058b4887c382f47d90eb433e3d701d30805fbdfc19003b8602fb83d1c29", + "0xd149a6a67d2dd4697703b92b9ecb7ddbfb533c26ec403d7720201cf6ba3c4766", + "0x1ae1838d66eebf4e665b09e10106bf5f85a47a58760a4cccd82eba73e88d4ab2", + "0xaaba122ab1f6528da3f81b10a4ae10516fb080fdeb59cd7eebfeac9f071955a4", + "0x7edaf66e02499ce3a56a30d02d6428b531c113fb60f154e1c1272a5801017802", + "0xc52baa2d39b59f3b1eef1e8692da25e2da631a5ee75144b8c785cfc7c5eaeb62", + "0x85455eabc74f4ad1a348470ad26f26a294075c6e98e9f1dd42b6a4f7954e201b", + "0x8729a0b0ec90964ce4a376d0685e1a60adc4612b27c687a45cd896606b0526cd", + "0x4355c88f077156a9d853d0e237df9bfbb954391b88c494c42cccb366b9fe0ba7", + "0x656c2ebbc949d872fcad73cc85c9e56137db27ffdb0544620e5ee31aa375b2b5", + "0x93ccc4a12da87c000ff4ef71275375d68480a460f24b68bbb9630a82bc042ac6", + "0xa6470087cf63e7a9a1a58c0a840ffe232f8d25c067d741b6a74d77b71bf79205", + "0x51428a417c30086b57e26f4e1d5a3c39c7b40ca822755f5f458562761ae99442", + "0x3fb5f26c55be26bbf0df275365118be2a43bd661d9375473940a472cd3857c08", + "0xa05f15b1daac11682721b5345b071feed266c4ebcc615ea93d3e80b3324dee65", + "0x3eee74308621e9ccbb9d212a482a6821e54081df0257f5556256422ff8f6f3bd", + "0x2b53f228c84ae1d92a681e77db18bb7026bf97f7576af2e62202aa22c50bcb61", + "0x847f85d6c00b47bc52306674b6e2ee4640706ae35def9994fabb5837e360dad8", + "0x9687fcfad756e32e9d2d27181b3535f8307ff0f342f9731f35a4b93fa120e608", + "0xb091c813fa100787ee15aef5c2180a18813cb506d2abee9ec76f9b1d816dcd65", + "0x71cdb1751990c72f5337c7ac7255df506c5e5dee8f03e019009582ca48dfe6fd", + "0x8273ad83f262b93983e870d70538d62245badc094d48ca109fdf79fb5236ab9b", + "0xc0b7e4dc6b8da7d87b7f925bbe33e06cdb673ceab86ff3a633aebdd262096878", + "0x59b02c3cc427e63248b2bc2a5145de97f799bad4774f73c6a984ba31efbba3f1", + "0xf696c58489a197c7230fb0c012926ae9c3326f38371ff5af524507ea3145fa1b", + "0xa1cb4eb5b3ebb325cb1f4ec6553ca573d812c63a23c943b21e1e91ad8f5308fa", + "0xc740759d449af90d39e1783606daba12cae3c88f0508d7c829cb58f135e73e7b", + "0x73a10d39be5855d870a0f394a50a145019a84358638228a788c13b5c5c15c1f3", + "0xc6d07bb681051236b7409f37160b86da85cb4281ca29f7d78318d2802d19242f", + "0xc3dbae89499f1b104d5334397c9619b2574f5fd4fcbcf24d5e88e843763476e2", + "0xa7b473306d1e8467b4865d555d2d616832bdf48eaa5a6b5df254cb7876115095", + "0x9d2c538bf230b6a08d9f3d921a13794c9efc6dfe643bc68018c1664993f6e16d", + "0xc87dfe99771679b4dc3116ba096bb30170cbc2591531ac8d541aa76ea2f05fa2", + "0xa3129964ef9fe5822a8d29ca358a47afb9c09b9ca321ba0f7b84b08c23c11d70", + "0xe1ed2e8e3a052c727ce300729c7ec5517a2944740eba075158145aea1015d27f", + "0xd957918497d95744fda122ded795c6facd295bcd1817eb2fca94a453bc30c22c", + "0xca175f9d89d293e1d4c33393d952d4c969d6df97ac9feac73d9be7aff4c69cf8", + "0x219a7c129b938798dc89ca753ece4720f3524e4f4d6087b5d70e74f4c3fb1895", + "0x63215cee80eb511f8c1508bb271230e5d082872e1259e82fa79933de772dc112", + "0x4d1efa0f5e06737ba4420d8f7132ce348cf295e91de20e6faaf748afaa875db8", + "0xf1aafca244db39de57d792d2949677e5d3f48ef47f248f8a88132028fc96023c", + "0x1099a8588d624b4003df9da680f4381f8eb40fd3ba6407526d09c93d03f598e5", + "0xe3d5419a835ffd8a8927a07709a42390abd0430f1bdcbbfb3f9ba13066a39fce", + "0x953377594745ebc0c431f851f33b8c5e8602378864f414bbd3035ca1788859be", + "0x7c715ae9d7933f06dced099c13a849773b1a519ce2f1688a9c8dedeec49aca7b", + "0xde36c2fcfceb9c231d6c0fbb33164c06d71882f55025642e210e4d010409e843", + "0xeddbd429004bfee1067b3b21fc78cb3c01034a9bc92f851c4ccb8c27243af3a3", + "0xbf3aa83130b9b525cd8f411a710fe942a482c6fc26614b51fd04f121bc577a88", + "0x2cedb2ac98fe167ba8e0c9892908194b82e7804adbd09fc15566be9196f68b88", + "0xe7f40b3c314baca2031b0123b16a6364ff1710408564f671f46f5725c649c9c6", + "0x5d41f6843dc42415b59724130b8d343a01012edc44ba304cdc18dc3acf56b54d", + "0x4e6fde3e8b29994c5a25f88e115844f942068c87792024315c3b41f6849241c1", + "0x85c73ac2eac4223ff8fd07909a5a6b9b426aa8ee3afd366d068bfa6184c056d5", + "0x2b7de3f5aab48d8d1b72f10918a8f520a2ccaf6d6c6d91d75b22008edf195587", + "0xc406cde5569fe7839feb9297859bc93b10285a2a352bb709bd4ac4770a9b5bf4", + "0xe819da520e41fdc07a1edc01414e89823f2653031d37fb54992457f4dd3c0a2c", + "0x3347a5136bf95303b0270637b459c3f70f031c6cb6f74ef159cafb4bcfd789ce", + "0x97113fa11765d2841f4e9bec0a5f432f2067be1b7b4968eaedb74481ac551fb2", + "0xee9770a9a41cd58876ec893925e8bdd0b19e13aa9c91ab2cef6460bb33c08a96", + "0x61223ea7ab0f9be0ae8a704ce29def39912ad467400c0074f776d4c2e33a5aa2", + "0x161967feb869757e58aa45c5799f38a8f2ef03ab137872b95b41b0a598c20c88", + "0xd4eb3532ba635a0c73a004981f302f34b5b71eaccee64f86abda184cd4875314", + "0xab28733695178b9ade9d7ed72860f04451bad30715f52f23b56dc902b1b6dfc5", + "0x65e4ce1f06f24c5c81674166edc2f35250ae4186608011b8c965153b8ab8f67e", + "0xfc29318baba3df75f0a60e495f2a96740a28c6b1145e2b080b05f53d3b48ce43", + "0x63d16f34e07e960e36e078de5feccfff72f54d2557a9db2349efeed8c13360b7", + "0x493c1e989d0a2cb52e1e57e240cc14ae9522919b0bb93f1fe980845d262f6204", + "0x74ff1a89c48d980e12ba337e1bc0a604de568ea348db5b8f966b24425bd3c6fa", + "0x4f29b47cf4a845a49c3881f0b972fafb5bfa966abebf34c8872af233e78a4570", + "0x0ecd63ef82aab497c8dc1f0413cc6224c4740293cd49265882295b8b73d087d7", + "0x6dff54bdfa9847af76edbbd1cac3e92c8f9fb4180aefd61f363b082fd107bc69", + "0x4695e83e15afe0036252351d2e4f56cee811280e35989d816ccf09755d7cf323", + "0xdf07067bdf6b6e49ea9c225d659bc1c8b8c788c3dbb91699586695a25207ecef", + "0x5f77aa3e4b2210d759caaedd3a163d3fd661a8ab7c8f7e845ce98f26b82e857e", + "0x7a7953b5648a16b7d8834ad11eb20cdf52707ff6647e997903a23612327aefec", + "0x531c39b2410f45f7645b52c0828ee9cd7a0cb835c4cd1eafd48229092e75ed26", + "0xf3db11ea8491acef57fce2e454d3d5201079f4ff29af20ecfe942f9703579340", + "0xcfa21712fbca93d03781283c072b579f8d6484fabfeccadfe4ea3fed26aeff94", + "0x288bae3e95b4b0af7721f37c74db99f3f6c5d1dbb594ff6af88beabe65d7f6f9", + "0x27aaa52dc7244cf300ed3a74532205074cc618b7c7c05e7abb4fd6a095139d7e", + "0xccfb6f8b149b34d05df75f460a8c311fc758e7c4283ddef2b81fd8a78e27282c", + "0x570c11e392170f0461a83a80f850bcde220609e0507bfc8547e7a10aecf86524", + "0x34439f6a9ca492b2724557d4f9890433c3dae47e3735ea02f1a6e90b8145abe6", + "0x3a803812095f94e789498740977d39b8398aff6717266b17a4da7677c6f3334a", + "0x774cf7bb05d43fc081ab064658a077f76454348c8b3b2114cb712db138eacb55", + "0xe545a1a8eb64dbbc32e66eab1c8fe0ba9c7156714ab6248f82f6345ee4c15c4c", + "0x7aaa21d77ffa084b101c5fe2726253230acaad22053514be7c50e97e1b7e2735", + "0x8d504d69bccb2a5c2e4bd2c6dcfc5e679359a9615c15ade64e8b5476b96b4217", + "0x9aa3c71cf8a3ce3a7b6bb93392f1f3d8ed633c4e4b60895fb9bd16142a854e8a", + "0xa54d3e4e6f8fcf47a6457ae0f4ac6d5b8c9a810ba8c55fbdb9ca36e59b93d877", + "0x14171692ccc13a0084e92c899932e55857af1240721f7602a69d5285e122d4fd", + "0x9841f50a417dfce70cff0669b459d97b6beb0229251a081ccde202e9a00426a1", + "0x20e2ac1dde32e5c74430fa17b576911acb88a319b6c1dc5c6202b9f7b82e0fa9", + "0x0a31a7bc530123e7fde7335e1d617e4cde004290912ab70a6808cc1256cb5a77", + "0xa770f23ee0f5499fb5e6b16571f48843f393498101754a2b8ae812fed08265cb", + "0xaacf9f3b554e006189c2c649f73cfe2e20c9c26338441f006b63fd95121da018", + "0xd366c85e4823c1c0ef778978fa269d38dff958672638ff7c1687cc3d51432d07", + "0xd0fa889d18f6e8dc54f6c97ec52105570c60d6ff653261c9f018d3a24ce4867b", + "0x1344b2b77aae44b208f41f602baa34556d5ac75835dfbaf1c09e5bdc78466432", + "0x4a423219c962633ed86567f5e74bfe6c75ae16d33027e5cb264ccfe94ccc3bfa", + "0x7c5e31b582ff1e4224cb95fc0d0596a65d1ea54ded991642a752ae2656757882", + "0x8209f04910f4faa880686325830269aabd78525035f2858840d4a8557ff109ff", + "0x977267a4599061c21b46e1f318970abd5853268fae714a6d5baf0b9b93bba733", + "0x1450b37503290b549e9781575172705fc08b0c869e5ade2d72ecf0eaa2588cea", + "0xc732c51a5d79bd2eaa2c46cb58afc24572df723bf1b671a294e64fb082824cf0", + "0xa2f5d5b72c6f0265cac46edcdf629899b63eae097bbcce309108a94842a7322b", + "0x26c1e10236d6a30e9256502a01aaecdb84f68a6d9dc23fb505b70c03349daf27", + "0x5a513a5d50ac02e9cbc28c70e3704b97f9c641011801ad515dc0855ba78c56dc", + "0x885662be9e4c021251c672a84eea824d36f11aeb986d3898707f42b63b817b58", + "0x8278f4fecb9c603b13440a109da530707c0982c53519d62e0c889652f3c78c32", + "0xf819a0c460432f0d54bd02c290de48867c14d59b8aabfe49d43f40231150a3a1", + "0x2ae240f23a7ef99cfafeaf51fba85a94945f50c918ca85e254c81282a3b0a9f0", + "0x6e4f41fb106d3c8a724806d6d887c007230d0d20b0c80daba9bdcb807cbc5879", + "0x6ee3efe26fe8880983ebda050df910500881c70c1900d844de9707cd98fa96e9", + "0xf666247714a753ef75c359d1403095adbf025ce31ac3d411bc16a222e4d0b9c3", + "0xd3115029336ceb098511ac2a1a8c602849eeca7bce37a8f7251e468d8ee0b8a4", + "0x0b33cbac5f852c78d7353bb51611b3fbb1eb9dd2272273ea4d1f3cb56ba20055", + "0xcdff04091f5f2ab58cc732aadf5d5aeacc7b72370afd33d3c7c3a5e7a652fd33", + "0x386911e7cb5cfd005d7538c0d1fb4b5c27dec5c212d3ed0d77786c2004693e43", + "0x31cd9d6ea44d25239ffd94862f0dd09458390e1d12e5dc9f76321610be92f5d3", + "0x5efa4263636be712eec80faed9995d5cb79aef07ab97d0854f7a0a6b44f58e30", + "0x54676d2b6df43f905ebb26d980336b4cdc05741857151c2bb66f8047823ccc76", + "0xa0dbe2e86d04dd430cb383f37d03b19d94dce42a0a08278ff6eb743f2ea7aef7", + "0x0670d61516f02fe3aef01e6af0e7bee22be2418e8da018d78b8601646a10a5af", + "0xa686b29cbf80f4b5b68c86e17a893f701251d5045d6671eff953bf76820dc595", + "0xce04b03e593037759415b3aeed01b18c4ea484954c1a17f01d0b40ba5abafe39", + "0x28b911b49864da62fc959c4b2d009312c9288deb397110df6016d2f75dfcf9d5", + "0xd1a7fced81a79053587ca15b7f802c1430b7a89a302b3f6363357eed4feb30b6", + "0x036f01e4fa36ccab75e0895f644c54353c397b2677f376777155dfe9040a2fd1", + "0xac27f420f52c8ddd029fd9b4d9307442d70474f19cb2458eded25c1b4b4d67a3", + "0x12438b627ad54bc48ee43909dae1b6d276396ae13c2b815a842d6fa06122dbe9", + "0x01378884dca2f16a5ec02d7c987a9abdffb581ae4a007258ef0f5f50cda6232a", + "0xf55f7951f114cd84c6b22dc756737b0136583ae8fc18a57165959931ce636fe8", + "0x13fdaaec50a157781a915577836f9353a27aad353f385fbd3d07fd0cac8826c5", + "0xf579b063997adae5b9955b54a47b8528a97bac97eec2158148a809d3c9493264", + "0xad9344a3065ed187c2a6e954751b14f0f13495afd5b4345d7795e4b4e69099ba", + "0xa1413848fb543e45d5d65056b06ec96e00c5a5a9e5d453e71bf095607abda7ad", + "0x281565b8bce02cd2d4ed0bf39adde9d074377e91009162d66257fabe77fda908", + "0x69d121e472a694d171e8a76288b5aadac92dbd1cd46eeb0f727a2e5eb58a7e0c", + "0x47856ac11bb75ffbd65ff461746988b88819edda6c2223ff643570bf1f49cd0e", + "0x711af0aff4a17dd4c397ae6a54c51a2826fa119f7b6af734cc297f205597ac69", + "0x6e139f9ead2f7fa9b6292d67b3952c2395032cf2a59f2194b2ba6c176836f8ce", + "0xda3efc9a8e745996a210e992fa06fef47d0091ef3572c706be6e57021aae421b", + "0xd8a4b778d85d7184aa03f313aac393a7c6642069b0ab52d6ca9b62031c2fe288", + "0xd1b85f59258797e27bcb1f84f24b6bb8b4085288181dd4666b1336386d89b95e", + "0x33fd2c2a1c6f601169c7d4c0010040dd2012d0cb83cb9af0a25db9eacd84c0ec", + "0x8bc21ef10d16451c1f90f51ec27ba3d13014b4fa2962632ce56cf4011672ee71", + "0x527f5ea328864143704766d7f4df0702fe64c2218cedad8907fcab34452cda0c", + "0xd0bbe6bd9e74626a10817017516fda6f081a6625c04f1c36c82d9ff5940f0c08", + "0x592eff1bf55d73bcac0f952532612e7a8f84a4955f57ea4b78c64dd94575f360", + "0x4567c2fcb9c2fc71f231e99fd90f74a62f167c2ba439796f31823fa617144cf5", + "0xf3aed57f0a9a07773c81fbaf5fb2612e5f97e74ce8be888a7698df289a815155", + "0xc67ce631bb45fa5221ea53b24957b8e8c76b274019d60b5e7b349cce3a832a6a", + "0xeb6b84b8f3750772990b197319a352b77bd970d3e099ecb079cd05be24fa36d0", + "0xa4d71eae89018cf0414ef4c82dca27ae040da743098eb2d7ecbace18e529faaa", + "0xa31be1e02d787bc66ed56b54755f7a9023627c659796c46cfdbecfceaf20e087", + "0x772b5684c0b16912edab583302b7548726357132e80d7a1f2c6591548a86f915", + "0x2c8c9a738359c0d18ca3d17ebac466b77ddf369112246f9fd2e8fa42c2ce547d", + "0xac8f7fab38b3a4dd24bf534be9f91831de3332c2665e8bac7d19c6d111cf7586", + "0x4d77f3c3d332e7b72097fb6b46a4bacbc781185551a3d0bae0149eaa7645c114", + "0x87ccc463a5c4fee37ca8ff91d643b38710d78b82b17d3597058b9cb99eff1ac0", + "0xf80cec4d71c0fd3c55b5a27b81fe9e3df3fd2ac1c2b5b8c848714fb103c9f4eb", + "0xb1a87fed1c44248113687d6b65c43c8173816bb13a136908d8dafb72a6f04ef1", + "0xb9b3921cdc2949ef7ba9458985bc2c0927bd6965a9b18ff33cebf36b1766f4f4", + "0x03ae8509a5a86c0879fa204e38fdec4022418d84c758c22d4f94d29e7e6aaeed", + "0xe296260f97bd0354cb52f5be3eb2e8de462d9e4d061201c2c2fd6d9d523431d1", + "0x3771cfe258bffc47d07b5bab10cad3bd618f8ede3e308c0e6c8e130eb51568e4", + "0xe2a7499c539c0beefb727c9928ea0c57e83d66f3610a4785e350b72208960ff4", + "0x1a5787c403c0c7afb3f53b657e62fbc1a1f0575d91a139c2f9a4d2a2756467d2", + "0xd87833faea0eb8b2ee415339aa9ce943a7c0dea8bf0a0e3f4460afbebc1595af", + "0x327ddf425b5349e8b4794f8f8de05682d9220e1297d3eae2c8d1c62b578e6bd0", + "0xdc2db78b00fbf7fdfcebf75879518d42b143b6fbc9d7b8eaaaee917bf999f0e3", + "0x010831e598ef707676ae208cbdea3029b9a09a5ead73b02a829a5af96e144f1a", + "0x03659ff5ce622e2c5b5e2d25e5ade6d7c62d7c15d74a4cf11a8d79044242be24", + "0x031148a9853256748323c9a8d4ce93a2b3b1f3985fef966084aebceabb432ae9", + "0x5d85746f820d145df9b558b5c011d813d159ec55338aa152fec4b7f80b059dbe", + "0x13af09d43391379b2d8d360123ae907e298a440f9dafeaca38252e02a6b9a4a2", + "0x65635cdaa5586fd4bbfaf9f63ec2b87a99437c3daebc3505fd04ba35d0a2ed7c", + "0xfd25bdb96ef238386d903884e4e8969fae6dabaeea7dbfdac88c08037edd6ea8", + "0x995c0e281addaab3309173bf058e14375a6b9e900ae7dab4cab7642227c81c6c", + "0xcd7c22e89ea5451fae8eb6964ad515a8e0af0717ee8f307bcac2110441f65fcd", + "0xdd145a40141409f71aec60ee2807adccf0417df6c63b85ba30565b1619fdaaa4", + "0x1adeea2ba1f145d338e1fc8a4098f2205467c87c01d074422faa1586f6a34d2e", + "0xf09b1742141a930895ed6b903fe57c622e2b827672c7ebda12e3fac75518b825", + "0x7835da3fbe2f1198cdf0d931392493b03c054dec6d1b473ff66efb8e7a538f5f", + "0x4066721a23b0373c75ba9481d17d7ce300d570905cfc3e7ce24181235344a02d", + "0xb59ab5d68e27b6bfa7cc01739b5b4cb4425225e53fc3674e295a278bdada271c", + "0xd420eae817f9c911da12d52d145d14b04d26436f1d53b9fa0691aa55c686b84b", + "0xcb07fbf9705d3331be91f006b26d065547e0032a0c972e4dfc73e7c63ef29fc0", + "0x12b055307ff95b1889b83bafed70d4be40fa244bd161f9c6ca8da50df16285dd", + "0x0a31a5d4a81a0e75095817ae3e1c8bb8284ceca0df51efb62896869ca08b606b", + "0x2817a6c7024a12d218d266f2516ec24f6045613bf229d6db566c820c1761a3dd", + "0x63db30825e62cd15ed53e02c60548b5634a0c9b69eae02ffb4d281368d0344a3", + "0x48f58207bdd689dd39dd35b3c65651245521cf4c5db48c9bc0dbff0951d57080", + "0xca323ecfea362076fe143f90a5ca605b08dac338501efd0b29fa9ef95f672c0d", + "0x881f2dabc93197d7a5116cc1301d901fa6b3fe0961cbe736af202a2a8d969ad5", + "0x064412d6bb3fb85294be68c89cc0d5bbfcec98cff6954b1750809f75cd327403", + "0xb697ae13f17fc343265e21bc98afb0465d69ae2cb2ff6dbcf58e475a097aafc6", + "0xd1bdd6dd39227ffcc4d0f6f1f766b0f28bd2f1b3a7fbdd9b497032d649a0d3a7", + "0x61bacef94a269463c912e22d2f6cecac3027f04ab124a60644a8e7874039ae8e", + "0xb6c361fa302df915bbf389e21e2a3ac8721774ca349f6e4516e37b41eb331466", + "0xae5b995d435284036df0c65ca1292826192bc82cfddf0b03262e2ec790d69a23", + "0xb7aaf96458860f83738c80b329bb1369a0b0c87f93bd2b27091992c72956497f", + "0x882759910103106dbbafe77a54f7288710f4d074c71a15955d3142b34004b0b3", + "0xe86598203b6bfc8307ca42d6e70e2d9823ac39512d933a309cc9a1d46f92989f", + "0x3e36e4ff78f171a9ad47a98ea9f1a35f6bc4663ee0c7533b8a96d126b7decf26", + "0xfe9fea9c2da0710a1c9435c6ab02c54db1165e4bdfce525039302077453f4ee0", + "0x9ec3769fe70fa2317dcff42bf728adc938fc528f3b8cda0560777909bf04281e", + "0xa767a233a62e463e8c77955491260b217bade43b772160084f4c1ff74f6adbe5", + "0x8678e61970deb056724b70b9735a709f1798193a990ded6a6cdd11d00d833183", + "0x6b0223c06fdb9dc4bd7fc418a025bb56865c49587a85fa1acabe38a4c84d0b9b", + "0xba1e03c5b422e38bec01746dc87cb407e2c98cefd146649da53e2a19bacc38dc", + "0xee64ad2ac5df5a7a5aa26ad44923070b09368fd6216975f6b30a81e695006655", + "0x6c15843e297aaea9f07660818662ee39894c76989ad251677c5396637a5916a4", + "0x0381396e6b9b675a43937661437d13ae54347fe53f5737bec0994e4dd6ba2e9f", + "0x751060cac5148e51a1b74efb6394b46f566cd2f3342a43826e57a8ef49ac514e", + "0x73a5ed52e05625cb9fdff693e9d02f4f30e7c7c4413b2319ea85b4f879a99928", + "0x593bb0dab3284681511170c1e02977f52a805483d97b4bd41d9e6e615d2a8367", + "0xe2067a79ad58ea4b5194e128bfd9139df168f77a21a9614113063f6402466357", + "0x2209879268d5b17693c532a713d43e81f86b532ceeb645778d7eef5926f45755", + "0x25d849c3d3e99b1012a2a2fab98fe9aee619b9e7937359f2df7bb341338bca13", + "0x08a76d918f5b269bf5286de73f307f94a359df403858c007d0cbaadffe5ea371", + "0x2a057adc8558bd8820ec14f79ae15ddf5b5c7bf3b5c32f05572be335dda3623d", + "0x94cc7467f9d5b03bde6b1ad4f08f9a1727ed346f177db13814d0dab93c628a57", + "0xb8a4d4ca1ae5cb659e4b00881f1ed9fd5369d2fd1569d27882aae2fcf56c1087", + "0xbe7071057bb84a35f16052e18acfd42682682857af78a04754006ed2019d04ef", + "0xd867dce6c561cc346fc8bdf8026bb992342f8f6d5ac065d96a6405dd21c3b66f", + "0xd6e22c794cb7116481cd6e86909813049cd4bc49a63fa3425d6c3125c28a1bb2", + "0xa7ae448e06dfa315a01790ac6fafda0ab3ec811c2776473c7130e87bd2b999d2", + "0x7418ccf0b9c5279e02313d585c221dd331461db6f637199d1c970f127aaecf7c", + "0x21b0b8966314411a72129e64521605310c97bd8ee0266f386704feb8592ce729", + "0x882039f8af0d2f368b1a7cb6a3698e5efb8edba3015e88435c3fa9d3ca8b2f0e", + "0x17adceee57c5258ab775e5794051ba5451348894be67d45553d30acdcf47044f", + "0x9fb2d2726bdad195cdff83078d78b53286800940b4a19681a0ca4bd8a4379529", + "0x47dd90ae428602c26359f7e0de338984c0372ad554f73b528b5243e5c7f7fe45", + "0xa4825c78911dcd7fe8730ae9a0f8527ca2f262b83776ac136a8c64cc90228674", + "0x0f737a43a3ab98d8a764fdbcd66b18e71590e54a93f4bfead7e65213f7eb9b61", + "0x1975c7bf468985d9f3dc1b9dc588b33bc4a2722ab88053f1a564ce0de21d9ca7", + "0x477a1cb7b13e0dbf8c477a78e9ad3a0d0d627463a05c7f7fcbff6802badf888f", + "0x427124c021b313eafd50cb3d6dde09fbc2786849ecfbbe35ffd8d4922a93b122", + "0x59794db400fe7cd1b59d83c7806a8cc8c62a7ef3482e00f4fbb3deedc1283786", + "0x08bee45d8782d0aa3674c06902f9388b30fa21f38d393fd15624bd6bb1c092f3", + "0x2459424d4a18cb09ce724f1142372d6b69ffee638675bb54388710c2208c8c5e", + "0xbb3eb37d7ea690dbe2995c7c8849f415540b05a132aecbef4fd28ad273fde13c", + "0x155ca31f2bab3ce8e097417386759a331e8d2cf83361b073f603f2383af71ab2", + "0x5fb2e8cf601a9f9254a3b68407a8bc841b89d4026630215ec6f70421f9eb20b4", + "0x5a284f93277930b73230b907b6151532462b8baf91112c97450d7f46c3dabc52", + "0x3eeeb0f0e9ae8637551987c64df0885fa7aa9ac8fb0b10627c8b8a3d44d18d97", + "0x201fe6d24461029f12c280685e05bac55c24fd6fa75274bc9b88fb1509b226a8", + "0x7d07536e58d3682648be38a8af8c23a8272a398af28cb80046189117036e9c4c", + "0x9079ef44aa1db733a1e2f07084b97862cd5da5ae47016efab170bceedb803ebe", + "0xc75002f25689426922e8141a991771c7e4000873311bce7893ec5c058f6f32c7", + "0x5cb6108e5f5974273d836c22402532bdf853216a568b8fde310b889f1234cdb6", + "0x03ea243197273d1763aedb2a1eb5a36696a5da9efba64f8619276cf2f2017cf5", + "0x5a827e7ac85d9f097f1559acc56c76d0097a41a713709a39bc3edd9500110e97", + "0x679ef2ebb3bf5fd3b22ce1f25cfe16168adacf7b073d5372f165411afa110964", + "0xc8b03931b2acfb2b463c0f17b5da1189d06237205069ec4d31f5938ab885ddfc", + "0x50b0f9f05377cdcf7a77dbcc3bf7992be7261682eb408a0bfcb97c423e5aecfe", + "0xc9677a733db0d025a11358b33a2a4b52ec4e19e711b6ab728a7ab84dd67d09eb", + "0x34b0bb6e66bce289768656e2e89ab7bdc2836c96bef0aa98cfe4c0e9296c25ef", + "0xfc1f16391237fbbda070083fe1feb1639a426a5c79de2fec16dd6851e69cbb6b", + "0x79eb3636fd70831c59fd035ed872da19992a5e15aae45810421c348c93a9dd7a", + "0xb85ca6b264d14558fac83d90d867e647e1749f85b24421eedaa3cb4e2d8cf01a", + "0x0b62700e189159f775b8ce80ee4df7704a1ac69998c1e90a5f5da9f5e894efbd", + "0xc1ce468711475a144951545d66995268f031018cad8c7906a73bf6aeab0f10f3", + "0x7f2c16fea0d1a8f794c5b6c2d40b454d68d9b89c73f5bdab1789f62caf881600", + "0x58174afe000717e71ee680b1481668e3f0abaa6bf5a69c6b96b5f48f665c23a8", + "0x85abfcf8b749e95828119007ab8b1d6afdf9720b1657116c841c1095039bd225", + "0x6552bfce3a0097e840a9626e4019464352ea8c7ee212511be58044415df1531d", + "0x612e2c70fba9974a8d5ac4ecc3c06b5f2043f582740970c163d5784ac55a3ab3", + "0x73ecdefe8c47651cf61ba3df5df72e13e56c9fa56505793c4602fa74ae3d1870", + "0xe68343440dfeb42742674480abe1993369c75eb6933a5089327c0b436bb53384", + "0xc06fdda5b2d648802e696fc89cececf92914f8a0b56661e899b34a06fb66a2bc", + "0x697616e2fb430f99718bb5a9df1ed69eafeaf24f72a9bdbee68966b04052b1ac", + "0xeb62b15bb376f4cc981dd5c10df43d471ff6d9be550fc0cb3282bece776846f3", + "0xafcacfc95a2cff9b519a7fed549aaca86ed7f5d7e37602d517b0deb697622259", + "0xba83a413d91637f9088b222b0577b3d052daa5ca9d51e83c581cce2e01a98eb1", + "0xa0b488f964ec0d6d05127a1ab8f1bce4b45d660780d394e26548a8019a3efa95", + "0xbc675d2c633be07bd21d1ac763cd5e023fa37ca8a03b7128b32df430e1d00948", + "0x9dedeaefe339ae8504bef65f4425f9c1517b96b697b1974785342afb7fb304f6", + "0xe8ad482bf4beba86c4ddc9745c1cb30a1bbbe1623d79ce2e6f66c486581658bc", + "0x593201bf74fef7cae74341f1b2cdc8e300284c31dc7f31934b74139a9b006078", + "0x8d5f0bebf141aca61c55459e443a71fa2b8b2740c84fd2c4e9c2660e1da01596", + "0x120e62081d61838cbccbb05db8d068364104b63b2f6750352fd9a070cb2f6f53", + "0x54c081fda49a763a582db98410c498979aadda461f4382f0cc17214953e1eb69", + "0xc0a3300a15ebb0bbe14f3db6525326bab0143db1bcc43e4dca4ed46a8815e839", + "0xe741b6e9bef5c31dd4dc15edac58bded04935b67f108006f6ceef1f4adcba114", + "0x427abd82b68757ed376d8d65c69ad4fae8359947028367e88f62a559d401c7fe", + "0x7222c79464d99e1f01d98002a812e0a977930f0312531c035776ff62625e4f7c", + "0x7f7938b2b0487146c3af89434253b163fc59635fc0ceea13eef7d711a755f31c", + "0x7abf1bcbcd8e0c29c1ff9de1e4e8343f54855cfc16f2e5369f87c41253b727de", + "0xc9f107147e777965d5c729bc31f2ac277d134b2df8eee25428b0d79c8fcb4997", + "0xbf929b3699b4f97d4147691239d0dbf1913a875e75c9c68f6d61fa037953e6da", + "0x0346333fbf7a773d87385e777bde0778d590d04fa73f6b3db6f139840fe3763e", + "0x5a73831b7794403f6ecbdf18b6ecd2c7744a34ff934b6a913890090ba53ec63f", + "0x5bcce548acf5625acc2177765686a5caac3b53fdd49d95ac0fa2b264ed798501", + "0x444bf80ee7770cddda54c9a5c854261927c3526b707c2925ed2baeac4ebbb189", + "0xe013987455bc24da917c4e15b8b5648bb62e40652dad7e66cdee2e8df06f2dae", + "0x6baf96106f7bd826d3193802150820535dcf137ecf4dc8a731669dcf7b159b9c", + "0x6e719109df3e69e1a6a6b7a33fb3e73da52e45cbb8e023935aba9961ef575249", + "0x7344cace02063c0d321fc4e1cad314ad4424f683268ae407cbedf20b2e0ed916", + "0xf483437319156abad40bd38a34197ea1ad504e9f9e7ec8b8157f099eb3c849d2", + "0x0c21e2ffa0cfe9fcf2d58fb66ee24db61969ff0117d644d8b6318680ee981c55", + "0xe037959e3bce2bf53587993a5380553ee67162263a96c30e3d62ef4aabffbed3", + "0xc1488cc7fad54890170ac0c9fd1aceec16d9c68f2c010b346bbdf30b99a73dd5", + "0xdb8714655ee98678501ebf15daaf8383f51ae1bdcb6dc44e147da6a7f619d094", + "0x41e7b69703b57bfb2141e753aabcf2fdfe04f100e9c4f21db7b99a602a370212", + "0x9e55e3da7740f12082dc95b7979b27c141bd90003712d63ca52220beccce3fa0", + "0x896c95c4702a47bc8bd4fa015a2c62372e51898bc8ef166b18f60deff64fc6e2", + "0x2f8810b46cb00b4fa8dbab86e7f50bb050abc52edc882455888a3f8244dfff52", + "0x376bfa1f8fd6288d61247249580a5304602461b1337a450e66f6e39a4016d82b", + "0x33a287a5ac39eda2d17281b1641df2897ab7f759b9d68565d464ef1ea46f1a91", + "0xc473d1f035993d4c8c6d1bd49d4699efb2947cee12371fc76576537e02aa63b3", + "0xe894c64c6facd991340440a2e7908cfc557e6e900aa8d34c10e9ea7b224adf23", + "0x080efb5939153a61caabf6244548ee3deba95644bc9f526de5a60191a9c91a97", + "0xb2248922224ded4c692524fda998fb0afe806ae574964c731fa2e7f435506433", + "0x29c6c2daa6c2f04f1527ba6232eae756301f4443f6cc99fc39d9741741bcd7c2", + "0xdb66044851dcc0f0b279c462dc540decc5518758eb7e8e13163dc131df77cf1a", + "0xefd6dc8ad9ef728a72e4e28e1e405e3bfd74bc685a6de42fc3dee9aebafe9782", + "0x1204759b0e933b0032348dcae99b4c102302a3e713aa96eb230d9f8d11ed249a", + "0x37a73f93bf4627cd76cb2541a79410f397e1dd6427bed68076379064ddc6ebf0", + "0x51f95c6d2d7fd282e6ae87d5538fe7d238c514f0a138e4cbf6c098cf471f9eaa", + "0xdb563cb883d808565821a5fd1ffa52b2589c79a39299a55c11d51981dcd4a98d", + "0x33928171b0bbcc7d887cf28b0676182dfb2504d027034fae1cb10f4ca60b8dbc", + "0x9e17fad41f1e33b4a5be59ad7bcb8077f5fbe2d31f40acf316b2c8bfdf62e41a", + "0xf9dbaa86f13448bc3ec77b3496c62f89b227caccacd371730d569dc92b84f66e", + "0xd4a6e4002fce2684116a6c05991d2433aac463dcbd56901b3751894986f9a276", + "0x3bbc29a930c3a2d0f8e47863f37470bb2ffc5db072522fbb83812f188e61d5af", + "0xe1c1ab803e5eb00e5fae52f8eb07deea5e2a363e684adefb0e68d7cb26d6f6c2", + "0x8950a1a748a328b55217ae2a8aa3aaa4d2288e70b00655c6068f3047aa9ca6e3", + "0x4ddaef4f58f8a218b86fcbf9e56d4bebd1cd7c813959b6559ea06c8846b671b7", + "0xbaaea87f0f249cf30d7a591a3bc7222c3a4d53453f6883e09508473090eb9ab2", + "0x367b52a29fcd81cbb386a665452673781bc96c307c7ce884d8c84aefc90ae0b9", + "0xb4af7f55c2ba561db1be3fe76ff6d55ee7e5a164878c771f532be253b3db3621", + "0x5f9ede40c7393d6e9eba6e2d87f28828395e40dc2c36da3e47d7f78b12035f0d", + "0x853386dde38720029fdf81c74fc7ad3e70b4876af128eac514108961fafa6328", + "0xf8a247325fdebcd13a70e9489d5b45ac5c2a9c0b462e251e45a5584580bfa18c", + "0x20ff8ca8a89ae887fececefeebf2eba0778e610267f226ffef9d14c52f6b8c73", + "0x40369145077d0f80f7396fecd9e9cf3d59d5b1700e0c614d978541fa942186ff", + "0xd69e74f2b9004b63753365ad06d42c179587ebe5770d0f5bb594f75ba6417109", + "0x87e0dc9f6010028e207498cbeb1aa1142df9d8f26c67897c0a7879f94b55ece9", + "0xb069bdb9fc755448760ab0b6ead1bdaa408750311702b70db4601e58141330a1", + "0x3cdacbc01fee1687b96e8852e24af7ed87f6857044416395313c42c40da05dfe", + "0xc6e6747413e1da6ac4ffcaca566374e865906325bf9fdf09e14fe0ebd2ad970c", + "0x1f72384b33f3a584d7e636403f4034f8428de5ff060057e5f418a41b16850eac", + "0x8f4a70baeed166a90353615f71504e3944000c07b8069e037fd6754b078f43d9", + "0x4999bebe7c71a196debffc88ae2be118a55a2b7af2e6f1de76eaf6fe9da5a76c", + "0xc728ecb3582fb68873ce6df5ba9de11d89f094d3fd19e3c8b268b4ae3aca29bf", + "0x1b7d1d8a28e47a717eeb91ef857c0761526ee9416dcbb9d87235e4315765533f", + "0xf1f843e7aa7d804eb568431ffd6058aae34b8294db273b8b9a542ae8ca157de9", + "0x829bad292c382637d427d528b57b95eac2b875b10801a62bb084cefb5e48e88b", + "0x52091f6a90d35a21446b1540719f77d3b73b4117b69f155cc242edfeccdc7e8c", + "0x3c3962993a1844e254035d80142cdec9793c04cfa2719799fa6f61cbd6643062", + "0x28a5bf720da5e4a8c0fccb4751e1ee4602da1e609c576a606e27e4011c1f4b20", + "0xfb8623ea6e62bc378c714de12aa2194d65aaa093cf595549adae56a572e6a3a0", + "0x6929dabae326d9ac4d1ab9ebf04d9b27f4a6992d0a67cd675c27137132c9be22", + "0x1f0e8104e4b60d3011f8b470b1ccd53433604375f51e04d0004c90b70163cb38", + "0xfe7e448152b450bed8651ea1d5769a135490873cb9aaa4f4a49220437fba5f17", + "0x7c0130798cc1ae5e82b1547cd3f9a1f302a52a4d3daef359c69f13f5f9105a0b", + "0x0b22aff7e79429c3751e33c5d8c7f2bc8e2f72e0622320fec54bda68e812e7c3", + "0xae87cbf5de32b4c1168cf69385f77c1be04965d3ae9fa4b76751c23b9c69e3be", + "0xa343e1305d6879513e558eee5c2333477b7578af6a7662dafa699c676acbc8dd", + "0x826bc7e40c3bc58b74729b3bbd1e4ff88270254af8c079e705801632bde5767a", + "0x432052c0f38ef52d353e94783056fac6d9f4a003457a6eb5ed9bf048c02db455", + "0x3e6a02fb6b9f5ad7101dfaf4d29b5c10749d7d7af3e06fdfdbf55c1f7d6641bb", + "0x9e0b98d45c3248249ca803e70be2b414d79b37275da33692a6db5b77273c9fcd", + "0x42b6a72dbbda1e6923e3936981fc6135f8a08714778fec8d6b353d381bdf24e8", + "0x75d9595fddf022a38affd3b588b89734d08883413a794c4d5be30922fe832583", + "0x0ea2bfa119adcefe846141fa398142eeaae31efde88ee1ec1f7cd569839e6109", + "0x51358c2a35d15076eede2dac06b7efca4800815e28cfc4ab38674aab8b28b605", + "0xde5e4be9721fd1c6057e5a413732d804fb79621a2a7c640e39cb6a276f3b9bbc", + "0x404dfaf0a36c6de7fce5b5a0ff683d844ed090fac0cb4b2c577137a94548535c", + "0x5451a97c4b7831b6fa4600f89ff43da7564a5938dce4ffa33f104657a8757463", + "0xc6275e3c2a4593ba220b3369901023d9a2a7ceacb53a5f52267e2441f4e51c82", + "0x968ed6ab22bf9f55b1d4c9d3ffd7f27a13eaf8ed59c4dd550e77ae6652745b35", + "0xa2b054bcb45e822687be652b09eb6aaaa8dadb8ccce5b85956a8ceeae25e78b4", + "0x59c5319d01a600390bee87fabd9768e5df22819338e5d7f5ebe15d14476df68f", + "0x7e2717bec1761aead7ddb6f8168f6c5fbc14d4ef4c7e05f1785e8bb996d734d6", + "0x804ada861e161bb7c50959f6908c8c680882858dfa911077315a103b89113cf8", + "0x71f8f45a42493f1af756558ebbb9eb600d3b7ccdbd70d12a2c22e42f898302bd", + "0xcb3a6eefc1069ebea2f87bbefc34740a6d72e4175ff7d2a58eec37856dede87c", + "0xa5f89133f0371ff30542717964849572d9e27956d60d47241c776098efe8c7a8", + "0xc59eb89cea7063da4250cc12789d1fb07bfc576c59a7b9145ec85cba25b1f6cf", + "0x43f366ad7432e3808247065d16055f841a14931b82632d607aaa320d13250373", + "0x72f354ca2ac10771affc7e64fec59208d6a0bd62a7352c2bf6cbce59142d559e", + "0x91420dad41ceb98299873c317068302e4a57f3d48e72c228cc58e4a1bb0d43c0", + "0x99a36aa705ba72d4f4bf9d4a51f61339da35e62c39c71e1f7e74f6967c23e187", + "0x22b0c41b3e76b4a8acee3e98a16729c254039b8eeb3e3ee65afe524052edbc1f", + "0x3104e057b75e580aa4f2736e26b278693b053736d4c64d9efd02db90914c2581", + "0xed79ce9015ce295428618a5d9ac7a67194de9858cc9fd548b8462f9e2d3a3862", + "0x83bba86797123337f92db08dc7b48b44a692f807a772e576e585f2cce352fe7b", + "0x9482d231eba432de54ec463cd3cabd4951d721fe53dfe80459ffd34eefc5b555", + "0x1bb5acc89d360364bc8bbd3f7169a75fa5faba1e293c5e3dbebc6eb1b8b72ea8", + "0xcc852865187bfab1a9d28937585ec0cdfab58fa401f887537c273ccd537b81f5", + "0x4f02969e1f827dde8135586b14de0eb5bec8d461a3d14dcc1984abd913b51d32", + "0x4723879eef58443640e22d010d326467b0bec688c03ae9c04d0b0ea5420721a8", + "0x0779a71281b6969f22b13b03a24a4b14fa99504564f70962bd05a2fe1389f8d2", + "0x9568e517fcbac4a39046003db1976ed9d870fa40e91f390407cf43f4bc5675da", + "0x1f69a114551a3f14abb00f6a63ed2279936b53b4612b559fa8b15cb7984c5e45", + "0x66b0b6846807841663439dfa081e28edcc519a810a35545292ffbd1933f26125", + "0x6924d43b1cf961f372a530e25edde504e11a7ebc03493bcda1a2c3a027c6935f", + "0x301601a6e17afd5a60f6690cf194d490ad9885915f40d5df341515d0171bbc10", + "0xce8f9f2763797e19ff21e2c76b18ad01cd8f9479c8e5374889acba9b60c657d3", + "0x43e719f31baa7a4b61af2a8af8561b32185e95863788100955ae5f34edb85a9a", + "0xd9f2ab1acdd1ea1cb0f16548e3854f60ae0942eda58541eee12a4878b557e6b0", + "0x1e57e064b64455b6e847a9677093759fd4aa9d51467dc63b29f6e328089760cd", + "0x4ad3b4c4432985a632d4ff39cb6986afea9aa6653b1921bb7ef14435d5529ba7", + "0x488a4e339b7b52de662660b98dd1cb11ad7bc6824e08cc2c0faf73d0f4bf26ab", + "0xeb37c84ce1eebd6816e6f07c26b6843645425b0c9300670bceb9f74e7e1b3b4d", + "0x5427e2343b7dfabf2e36199f0d4489a50517d16df294967b7301a22480c6c614", + "0xf1282013652708e4e715af33a64b4f977b82b2dc4be96be2dd571b1daffbd4d1", + "0x3592a446f20d85a57d7a78e7e065d7e7123ed502b60549de320d06b38485ef63", + "0x8d7b06e34dbdb3d9b6f4b2ce76c5e94eed90a7a4905a00ab97e20fc5610c6b22", + "0xaff7c584e50a8b51609e106f1cc7b20534ac56eb2408c0355a11476b1e7b71b0", + "0xfb23e9a880f9bcb1a4df3f5cf73d5b731ce5e1eaaabf0da433e08dd582daf85d", + "0x2b8fe463e4c47e3f46dbd6879370470bb0c2b91c25d56bc9b23852c56cc074dd", + "0xaf92e8f4d65e7f3cb01480d0e3a592eef76b6046a62073b492531891b22591dd", + "0x54bcf69e51f3c66751869405a5d1e89c2ce06557da303524cbb2e18408f4e088", + "0xf755fbd0b597498ab21d540239ff812464ce0f29fbc2532431140fade76e2a7b", + "0xe8738c1a2ab2c19c544b3981a59b6d93513fd268e5b38fa413b3a1ced047989f", + "0x60dbeb06007fe063d22f6d2a18947ca3d6ff7e935592b5d592a53abea6d4685e", + "0x60426e1f62c80dcb6c7445ae3ec9da7b4bc3fea0d721c3d240b3878de8f706b2", + "0x1d9512b6b289ddc3af7e6804fc6bea38a659091253642104702dd47c3a36a48f", + "0x6be5e9cf405bdcb0eae9f14d6f3c985b8ec8f893c4b12565aa60d3a593318e0e", + "0x1875cad147aa476b5a9bc1420bd3bd3321e3cbf6da578186da751795bbc2bb20", + "0xb15a3af0d6a18421b9c82ae87e76f5f90e15ffa5175722538fddf6b0975909ff", + "0xcf86b5d93c6306f630e46b67e2c75afbd871b28fd14f452b724f44bcfed1b527", + "0x77f59b9839f57f98677fe4d07895904d0b5937bd5d834a53caf622f082ae4325", + "0x74a8d90659984a2b23f5910f92868e95e641c8fa00b2ffd07545c1d013fb0949", + "0x55467017b9eada8f1600749d6d817dfa8e155049efaa73360b6cb0024ce4f8f5", + "0x1b4513f09398b3fe57ee9b57cb77c341934e48502f9d6f8d442787e16c2688b5", + "0x57de5952efb06ab4f961ac17b73874c6298e4bb91398d5ced6415c97dc50098c", + "0x2a93566543dfd547186a76250d5867509506a483ca20561756dc6dc003dc596e", + "0x2045ccf08de734ea300f1441255935675febd57dca8ba9bfb2a56c09bf8d0adc", + "0xfbd0a1fbe57b5c9ce24b5c5c4bd9377f090076090c7cd8ccdecbf542185db5b9", + "0x64ee12ed03f9eba33faa96bbd130bee77af1fbe1602a671fd962587a9f915f84", + "0x4354a58c3e5866b9eae13163e9df3b11ee005786df0b933502630021ffb4fa34", + "0x01660d2af24219a62059c494bfee3a8643b718889ab1ae3291363616ed7bd719", + "0x6c0895a2406189bc40cadfcb9934e70b50bfff2c3886a18eb27afa57e14f304f", + "0x5441e1748f4d74fd2b31c779167c4253cef703b7f10f1a98ab0af659a439a721", + "0x426f88c242960afade27e39c440bf22bacedbdbbc00b933866a657085b13fc18", + "0xc526db94a21a8b3ebfa83f93b5dff812b3492393d11cd3b686a7910d6dbc44e4", + "0xeba7fba3b1699a587deb440317f268ced8cb81c451354d0bd22138b372576bcb", + "0x27c27fe82919dad4b6a015bd6ff5beff4cc07cd913225de93040c6a9a49ca0c8", + "0x06af2a5982ea138e44df581593a40a8df1c4a8eb95e91da1d7c6a9ce7bd585dc", + "0xac3fe757fb2793bf3f8cf6a67b6eddb60ca265a6016626403619590bf2c4f066", + "0xe607b8340ecb85f7297b7b82961b3a3c08ff39949c2d6bef1fbca408c3217163", + "0x633542bd418f3d8e00d5321f4da1ada141082e0d5050f7171405e6e93eb3dc17", + "0x600719c9d77d0e04a2813d2caaeec75cc445ac4018cedcb378fc64994e564f4e", + "0x2198a2e87f9e662d98e6c277f96a4965188d2596f09bd6910995fb115d731220", + "0xed24e6a5743cf3f634bd0f3360128fef868305ab4b819b80110637ff00bf6674", + "0xae5572d1fa7badae5d59c5d04fcf4f394275d42570d255d784d90552e9ddaf8c", + "0xfc1702ae4c6fdc459dc810176e5be8409583b05ea7b99a8b1004387498afdc3c", + "0xbf036fa35bd148bcdf4f13032f96f0a174cb5d6b9d4f231119c04d24dce51159", + "0xa819e6716cdbb296ca2dca0ee82a480595f85c6fdc90a99f047c3e657b72d4ea", + "0x0c1be7b812d0852898c5ad4ad60640131e0a5504ad11e2613fe2dda89e4cfb26", + "0x74e7a3a1657192e61a262219ee1e68059290f2eaa818b4ee07a36006376d3a79", + "0x8a821ad52dfa24bde9029c8946616c19821a2834d0538efddad6bf005ae47a57", + "0x1767dea54baaa142e7e49781011e6631edeea90e7477f474600c1ecbe8546c34", + "0x88391a62fee75ec2baff14c4dc22885ed27ef265867425c947f3798a694ce7de", + "0x1246fd76b6ab81a2c0547fa09d0ca01760fb6253344d66cf93230130c6b069f3", + "0x440b92f5554341cfd281d779b16aa0bfd28320f31b27154536719c67c37a048d", + "0x62def7bbfbf04b09a5f8674016329994ab89eea4873844eba178a291ef09b94e", + "0x5a8c59875e59fb7b0b6f843c4958927bbdd72d3ae146dc9a9f1b9b5f2eccc089", + "0x073db2064e3061f26a0f8574434411f43c881fcb749f9c5da53cf0c640851cea", + "0x98d4d195f7258fbdd959c8c73f32a1552cad170c4c0a49cc0ae420ee87a1abee", + "0x8e4f41aa255efabdf6c27bc25672e1cca88a522391a71636c05ddd9d6b522f94", + "0x60efa393c4efdb51cf890dccf2e319994b1fd6b8a47f89c06573ba7d91ea9a49", + "0x5f3355dafc7e5fa75e21df5209352a79dc8c96fa628f48d60554692623d8e43c", + "0xe0b770e0f2f7b0ea91c792fda10d0721fc7580ce8f4abdab89915fe87ca46d73", + "0x6800f50379a55d0a964cf4f98d59258207bb173a965501bc2108730844886293", + "0xa6bb23ddf6379dfa5fb6ea27c3956062be050baf7fb2914fae22ce08a46089ac", + "0x35b7d3236611d010758fa3d6c6ee1c10347a77b02ac5ad74f43303dd5cd27dc9", + "0x7f0ef464f1492239ded01b207a7888e79ebcf4c5d12c250da6850271da6eff44", + "0x512ec33ac237207c170733c569d6e4641236368dc5b215f6ba491bebbb7cf964", + "0xafec7004b60a3c5d674d5da6dc588d02801c11cd090b3fbfc0c55f9a9323a0ba", + "0xebf9056608c75c1e7a415cbc499bc5fe2eb5df9fff1d022389251db066e77207", + "0x9fd58ef1b53b3d30112dfaabf5889390481d9ccc50272584513314b0e662340e", + "0x5d917290a5684e2288b6906ee7524618f5030bc600949d6e089e6e124db478bd", + "0xc55c0e3adfa76fddc13cb5270cc06fb9402876dffe022bffcda6d9650767aa63", + "0x8ba0b67dbbd299fb4c88824fd0ef36afe78c1e16ab80f13e23d31d61726846f3", + "0x5c40045d3c772319aaf88fc165ffe678757dabe3d59d181a0934a4ce7a31369d", + "0xbbbb63b62dac13af29c34742625716c46d95518bd5d9cda8b57d4c369d885fbb", + "0x81b4e7f2ebf6d23baf255218be7c67fa00faf62971effb8d7c5e03936880c248", + "0xbf883608e7112a15a2d4afbfab8a3e83135989865c23705363115afde3c5143a", + "0x552e23872ab3dd6dc90a7904d600f26896db52c25869d2d66bdc46fb4854dc39", + "0x72e64e36a407e212c3e0599cb5febe74ebcaf3cf2bf6b66a9576351e46613d74", + "0x4204c5edd3dbffd4f501275830f15dc254df43f9003c29d7fd6c070f4179a190", + "0x8ff65129c30bfce3b0b854832f3058f12681f913bb76be42a72a7b038f4096d2", + "0x3bf1dd33977ba28da75cafa39b6a30112dd1b54281b16986f3d0e846cb3062f1", + "0xeebd400ccc7e48d8c81a6f60e7aa69b84e0720732a2b767b74495b76bc467ff8", + "0x04b0df38a9a5ab9f9e30cbd4cb7195659d09b5de4e4c253262cdd4185e13c857", + "0x64d18f4ef07d4a4392b25d8d51b54bc9ea89229ebc2aa2a66201da19f2087df5", + "0x0e86fff430602b48f70992f7f8f49724c8087c29d86570da607d5918031041e3", + "0x11c8064cc1e5cc5ef197f95240c70eb579cd0cb74e499e253ee1c69b1a4e3d81", + "0x01a7c5a19fe3062487940bfabac42dd146ced59498eccd9517b65b171e833ceb", + "0x46ebd1d17b7e0d3828d71bff8adb27dac90103e28973edae4614c9e65a94efa5", + "0x22881e795b3ff5d5e15993f0c0f6301c732e5bba78dcab690bc1b0aab762def8", + "0xf43d46e97e7fa0791de69aea7954cd951f92e2004fbd1d0aa72cde0c808e63be", + "0xa0c9df82a9ce811717da994930675b6ecb22c63f89f7bc7c692c0ab859881085", + "0x3805c151493b31d1b79af1e6b2de4ca097ce49dbb22913cfafe084b0b149a210", + "0xd6ee893259855499a4dcaa95019dbe20fc40d9303df91581fb5fc6db19a1fc15", + "0x780cc2810286d4c41e375cc909dccaccb2c41024bd0a77e1f3f3ae9046e3e3b6", + "0x31c58d26df69880e47b88645f9f4232e1142dd8f26278c2f11bf41c58ba3ccc8", + "0xb73bfdf783f4f1c8866bbfd04a6a0f0d987ae5d1f9aaf4d19ab55f116898161f", + "0x657db55b3860b8aa003abd95a91aaa47c1e1f2517db4901d683b27d5fb105a4c", + "0x61609d32ea77a72c494c709c8769efedfd84cf45dc7c5b20a28d8e135a5a957c", + "0x460343a8f6496b9c86114ebf86654133b41e8143f7bc68fc4417092646194a69", + "0x6da50e10fb42e13f736fd0a88aa802ae82724ee6a828c71cdd7ad6d2c189c37b", + "0xe7fbf03fc16cf1ea2cf2145fc2e643e2776f135055642b684c5f7d81efae1b29", + "0x06846e310b60a59071b8a2dabd8b5eb44e8c9a22ffb478b8f968c6a45fe3cf4e", + "0x46cfebcf99bd37f7894450f513cb03716a88081c91c9a673e94e0603eb9afcec", + "0x5934983f2761509ca3c30058fe67d442ad83db4dded4e7e392d2bb68d72b64c0", + "0x6a05e568b79c5363d83dd15940401b425b243a8568b3e9335de13b8dfafea1f4", + "0x999b1c9431b989bcde8a6d9f39e11a0c2733544413d88f2edd2b6949426bec14", + "0xb5d2b68f839ca0b99fadc1833359fc3fa8eadc1ae34b00eb3d613b5031c90da9", + "0x29ced92f266dd37ce4a43de570ca790b709e2a4a0e7abfcf92ff2187874c0c19", + "0xf04548264e220067b290005b2396a6f5c5dbede35a2c6f1078cded28e44d72a2", + "0x96bafc5ac0c90a8a6314d709d111c50b071bfed45391f0a2ece1a0eba3b445bc", + "0x8e9042f9a4589859a83505b6c9a4e556ff57b7736e8caccc46e0380fad8dcfcb", + "0x85edb7c229dde5eeb31d7f339bfd1e1b13eb7c09f9e81a6c2af94a7d3cac38ec", + "0x1b5fb271a10db5213ed62721af4d111cb32b958049ef7b3ed77b202023bfcfec", + "0x74548aa9a1830720a76bbf4fc03bfb2100e9fa4524f34a4c578b116cffaea42b", + "0x50a114a7f0d2f7eecd2f5966dd9d73de7c68993ea633cd1573113a6606117a61", + "0x0372c30ad2b2c8632f80586c2da4f670c23cdb0f85594e1798453cd560d9b3ad", + "0x7e9094981b7f862f4a4ffd65c1b584a623907b48143447448d05a1cbde2466d6", + "0x8a62631597d8a60c27a4a71ddaf6f9fd3e460607cc380ec6e3f7887932e7c596", + "0x41653a96126e57a3742cdbde45b64d5b280166746d6ace6d81e4442dd63a178d", + "0x3ae9e3ef17b35ba2841d55e51a5a392c18018294eab4ead00772f6b84dbf06ee", + "0xdfbc25603c78173a91ee5b624842dfeadcd01a2973623b5bc780e6692eaaffdc", + "0xf37eb7ffe0f2b3705ea76e62736d54e3645b7e68783238e3a474614e086cc3bc", + "0x241a9580521df222a1b9eec34e40c57ad75a1dfc750b459f66aa5dcf5c42961f", + "0xa68df389403564fd4abc65964c76305639d3806db5d584e7028423398e3e8048", + "0xd7d68b1a5f3d748d1459acbb82183e3970052991fac0e74943f84c8f02135c83", + "0x025e1e5db29ca52d179c67aff3c0efc141dbcfa89131fd8464f279784f22beb2", + "0x160aa84091e66c2e6e95aabd507c012b6afea4b85795cd3b603d2aeb5084fe17", + "0x89913a16e00c0a84b9924852678c36809e6eb1dde88be82cc41d3a5d8f9fa17b", + "0x29b1ce64edaf9c7c23ed9225dd10289579c247265739f6d2cc61e3911f63acd5", + "0x689f4f0659d6052c32cdb64f60dbd83421d6a9e816d5be35843a4e570905c2fd", + "0x320a85c83f32436c74d17ccfac3d0834b65d8db54d15e7075dd53579fc5d8ccb", + "0x71c05913e0af13512a0341cfdef3066e7a7486c6fe64a6d15e036eeefbb12233", + "0x9b3fa93555930e079f7a042d81185d8fe2428d7174d0dc9e391a9b8b01b401a2", + "0x13f3689b02df4361e0b7a914e59804115a603151540b938d6235ba191ea5bb16", + "0x278e0f1e5bbddcd880a7edba633c30ed50aee74aa35661e0cf6d53ee0f4b6f48", + "0x8c6d418328a671d52b38a681be09d8208576843b1f2786e8542e751a1537ec72", + "0x6152d2e08601990bca71c6294c2daf35a0aa3a3ac12330c6ac527cc5014fe0de", + "0x201644d610731227cc0d4b62b25126d785f92b43a03ba48fb581a089496efea9", + "0xaa27c1e1c59ed7ea3f16219725c45257174c01beca632ffcc360005ac9b58706", + "0xbc9e1fab9d3fe75d6168affb7e6d051fab369064a428fb3a01e818f4b0894afb", + "0x538b71a9b58e0a0cdd370259c2e23bccc52a36fcf13a2449d158bfcbd67f3156", + "0xaf2a85e829460c93613275d19a19cf1f7c33df6b2fcdfcc93184dea4324a8d72", + "0x80dfaf0bb03ab8a2fae62fb20193928be58a170c37d8b3191204188b3f7fa89b", + "0xd7c2ec4c4befdb946e338ecf6915d85d4d099c1a912c6810d3fb926a1799d667", + "0xd85876bfaeb5a50df7975965e2a03c8ba7993bc8f8e2e18467e77ad0697e053e", + "0x57f9ce9d1d8618bafdb9a7ac4e004f6bd9a3dd29d38c2efc0c3da74baedb51fa", + "0xbebe9f37eb21efc2deb2b6f9db37d0c950eafa6eb3942c203137ec452c6886c0", + "0xd1e5a45f149d492b50b6d355a0852b1074b16cd3218f811fe6893c97db73dd2f", + "0x4f3b9fd5db05c3a0a3b24214d759e264b1fe2278383257811fdb37ae56fcd1dd", + "0xc9d66e25c28af174d6309bbfb6051e019dca89931966212c3019e84486a39271", + "0x2a9b36729739f291e2ec98f6773dbaf07d45590844af3adfea942e84a8e4f456", + "0x864b944e2c9a6ba6ab4db9489dd5b7ba686d9eb910a7a043b1ddcbb4a4056d5d", + "0xa640998be780e9ade3e2517ac71eae7e9d6283a9eed0bc76575aed65376786d5", + "0x4f1f389609578aa003b44441ee2b7696eecf7c9d0ec73e6be63bdea9f174ef0b", + "0x3acb4d3122fb4ea6c8ee24256e0af9dc55f18ba7714a83284e3b3cbf8cfa11df", + "0xdbd51ecc65005b136f6a33fd851bd4e011ada909cdd990ecd502f749f4354c2e", + "0xb088fe0f0ddb0af265b04f1b7272c5d3ff06628d60fbf0e747c1672557e87ab1", + "0x59356e307a08b3ebfb429146ed9ecfc75dbc181e44ba7f5afc9b6178c9c58e5c", + "0xdc8827d93d842e32b46f078ac718840907edb57a1d7cd334c4b2cf26bf75f664", + "0x684416c18d6e4b8b57511620f801ea51510c32100b12ee3bb4e05b37f948eca2", + "0x79e4c1b1efb04c7f545f95037117288ff8929c96dc7a072b66f1efabff03a4bb", + "0xb9580741a0eb343c2ab1fa07bd4e88b873503cc9f2860916f45db611fa6b33c7", + "0xac79fb87095442d5ebfeb77e225b9e4ce1c6284388c7982bf118e63b630959d2", + "0xa0b475fc4ae9c8ad7491b968cbbf608ad537d081d1b8a56f7d8fa04fe6a37517", + "0xf666095b34950f9ff3bff6cb3319a5af30073bd95201a9dc96405f1f64107241", + "0x76f7873b43554c07b6bc5e82f053f2b7bc75245829930122ec6f723fa7aae9e1", + "0xe7482d69c4e0c8002eb301f391b99e419f19e4f7b450c5bca464d7fd2d00902b", + "0x992e1d0d46cdcc59b70a4d64c43a96bc4a61dc2c2f212e0f0e58fd246987701a", + "0x73109ac46943e0b56f25383d4a5219a2f401f2386a88108ad5257ef11878d486", + "0xedc36f8e7c7fe037290af7c3b344e538189084aea7df30736ff8cb5f5e0c9d7d", + "0x9365e7cd16f1862910b25817d1bb23b72fe74502e7623134eb7aa29f3e5c2214", + "0xe5925df4f22cbb9e50423b735bb1d8b0b0adcd94aad9edd123b60c3c33bb1013", + "0xe46da39f684fefdb92afc8bc666e6f094987aca313bf8724bdeed164bd24e294", + "0xb0fcae02051013ff9832988a0d65bfceb201de21bb33a3f2c13e4a6c703595ae", + "0x6e46776e99e2604b7cd1b24dfd690c41a5f02eda093e0d960fda414d2791b5a6", + "0x49068d53af515a7fa1179209174800cf05a6ea0a49898d738d0d533cf7c5c199", + "0x3dcab0b59630c4eff8b9f5212ff0801540ac8a8097298b023dddca2387e3ad2a", + "0x266ed6b4c1281f8cda8cd4f22d079ec43d75d32d300a0e84724e5b035bd957cd", + "0xf7dec0eed78c9631b1b4e8b122230ab1102e80254574d13f1818f89e52dc520b", + "0xc6ec0ee73c0bc270a67554ff4e650e6c3c71242fc5aee809e4cc6f8c240df466", + "0xf0e6d496c58c8715ef1711a26da53a44770863e9d21623113f5c56debd080b3b", + "0x9aaab0dba9a42c1ac96caae41144646ece073f67e5554352abd4d505f8c3d8e8", + "0x8dce5dad8800c1c323befdc9e1e5a9016a787ebe7b3ff2cd0f782d0769c68413", + "0x7660aa9aff0d1c3a5619f6bc64068ed889e92279fc3a40b795aca0736bff57ce", + "0x73372029d9dc515dc88b53e0ac365095ce8fab5284203ca2aae9db4139351946", + "0xa32775cefcb82db1a18b1310d3be5353287ae2b042f366115833e3b1665b450e", + "0xeedbcc55d9a76706f2e93811248c8ac41025b09620ebf21800549c258a7547fd", + "0x35a4f04c4da351e2b23dc92f3dcc9cd999861af11e0970ccc661945a4acf0c14", + "0x78c90d2de393e34b2d37949b5815b049f8de7c2c2deb51da0bef484e42287a45", + "0x7d84bb1d08c72881647c2902974ee625897c134f04597f3dd01df221995e4799", + "0xdbef206efbcc46425f52d82c03f067172f681c9acb900f7f29f00007cfc5220f", + "0x5a86cdc78ead5c83b843f0313619769b8a302cf56c7c1b6936efb5303477991c", + "0x752d7a5f2896ba10ac83678717e6cc9bc4ffaf2168320f55ade6e1dd0bae6889", + "0xb11593772d6686ff80432a20c547eade36bc03d57c36243a37931ada220f64c0", + "0x304a3e826ea8c1094004aa31300013e71a6770cab7f04092556f5ff5beb54192", + "0xeeb975993035967fa19dcb293be861e4923daa88ebdbfd36585c6ea1c14768d4", + "0x2c66381fd78792aa26dc8785f118b9aef2b3a2a9c4ae4737133a72db5b5cbccb", + "0xd0b357aef43d0861559e5014f409f91f9a984d2f5c033b273ac0d6599352b0ec", + "0xe629c02ad160d08c27053c644954334d5d81b92cb973b3ea946cc9cd6f628024", + "0x2158cfbf48e34d4c42a7fc5806c824635f8cbf45405006eb792665fdc6d0b389", + "0x9149d00b9d8f01e377d43cefc6d665b706a956c0f84e1402b243600aad1d8e11", + "0x2f9a70ad5c6751f014f8abe65fab768d935ea0eb5dd7c9609cdc22f3715159b7", + "0x6b0aef99d6004c3aa8da8414cc72c369c6cd10975d330e56df820bbff753a7a6", + "0xe33e6e23be8c73c4598de4a440bb4ee2b4ea800722967c9d857166f800bda711", + "0x9d81fffd7a366a4df6acf26f42a8d67c7920e032b07e43242a61a8bf1c5fb366", + "0x544b2f347f4cc151cbde0ba90ac31f6e68b2d16f27963b8ae1b524529d857b86", + "0xb415fd9b8d4e3dddcf781adc265e6300fb870d74ddd1a7b6c7a1924d56f5f67e", + "0xb9f0d7e7e516dbd5973574beced80416295a3e186fa03c9cf9762554b64b4212", + "0x16c5275dbf3070e82ab954a0898290d066fa1b27bdaf9e4fecd12109a95eea44", + "0x9f74eca3975e64eaa62308c8c0764fd4d9aae1d86085edb3874c21844cb16b04", + "0x7bec669eb866b58603219c905444d7828b4eb501305901161ffa9458ecf90f1c", + "0x2213b06f2625c5e3a98d0407487d53463f6bb6261a009a73c921413a6bca073c", + "0x65c817c989f329f94aacbc4d257446d1d6b62462f3d77b4b36089b438b5fdbca", + "0xb283a4ba978ec5a9c7c59d2490f4b7155bd14d6e497a975b7de5d99015e1fffa", + "0x5e80374e2c478fecb547bab6336833b01eb7f7263e9d5e1db4fcdeb5b806e604", + "0xbc45f52fc45eb3846c2b4b0c4e8bf2d336fdab8c6f3e801206f663226afdbaaa", + "0x7eff47e58d7bf7fee0aea8d5ccedba9f4fdd3339d3f8a42ad829f943035963ad", + "0xa866035a1f1852467096a79fbd22537c65d58cf769edb01b5a0e030588021d5f", + "0x895fe927bca9c7aff9e150f76673116100ecaf2b80b328c7fce9ec08c56495da", + "0x7a088b59f3827fc5608a5ab4b01211518170649fe58f9e92d6ed06e34a761f1f", + "0x20fa401d6fe88f6ff657a3c7102e7b56d53054e5eba2c330584ec17c767e7264", + "0x14e99f23989c62ae96df41d5257c80e7d65bace5e93508e486227a9d6377d210", + "0x41f799cfc8b4a86a4de69214dc1924b03156c68c802653a87d8e131c56fc8e63", + "0x6b9229a987f650d4c734fa86d7318a0b17326808fd6b6675193294e33c578f8e", + "0x921e833a99b9d88b9f7f368a7d9d81f7126f7ab4b93b9ee037cc2e7c59232dce", + "0x684f12af081fe1f10482688e0fea7b7427e902828f71ed7ed8dcb6dc0174907a", + "0x54054f4abcad24c087dec40e6357f9bcb14f4c3c4f81d5f475977d2d6d267462", + "0xc355c3b5829756a5159592b1c0a7d1abf2ee4a11776bfd99705b1e49161459bd", + "0xf1fe2d69c86e1a88b3dcb36f681fe59631b8357c427a18af7eeb547dbadc246d", + "0x9a3fb0fee795017ed8e164337ad7be0d12948c42eddfa0957c2cd2cc1775326f", + "0x381db04bd79ed2f1130f777f7cf8fc59ab1f26b189ae1e4dc2c070a16a6ac0f3", + "0x878eb68a5e05270fd3f1b73372e83c545de4be1b6605489c66e6840ef456caee", + "0x8bc675fd6b66a16ba27ee07e471d734e77a05bf96639c0ef71650d8e1418f3fd", + "0xec1efd5a93870bf8eefbd67afaa6f91aa2155372b2ec72c2b08a382f9094bf89", + "0x0f3ed3952afda0956c6c4eb3e2e7d594f7db924a9a1c4f4dfe075d7320348d22", + "0xc5ac3411aa745755cecc8aeacebebc5426b515ec472bbe9ba2a2df3aad2de185", + "0xe03f42e6cecc37bc624fc1a0c630ce9b8197517daa090907386003904bf0c445", + "0x678ebfdd915e8a3422c0c461b228306b6ce571613fc809d0461d9af55ad51de7", + "0x91f67fa252861d642031171ef3bb1214727aaf59a3b087ee03b4b52b7f2e3090", + "0xfb0fc5d2d8d3ec6b4e11c81a0085440884d32850cd1b486b2c687156ca6b45fc", + "0xf9ff7f007253453f72cfcac5ff3678c3b0f1e65f0013be402d469ca2a52a92d5", + "0x94b4d7c1ca08469b009229be9d278528ccaec19323c320f75bc4f2b0090d2bc5", + "0xec378bc49bee36281c4f55f865a09ecbc5007132dc4d5513a8aa1b0c43798d11", + "0xe9a01687a79c3a3f394a28e6cbce3ddcfab742eaf38c271642509309c083e88a", + "0xc26f6c289023a0ced9f162f963b1fe8fdb78fc9a4471206be9703d15472c3872", + "0x39e1fa4fe0772e122433a86d61ac9f4f7cb063531a8d03a0414f8c81804c76f5", + "0x16e49a8521389f62a18f509b15ec772a9f12697ecdce9935d0cead160e878d90", + "0x1265ddd7a7c41913fbc65fb5ed6b643bf1daeadd599fcab7cfd8086481298a6b", + "0x3d7507df9f973964b3d45d434a59952601b1a702ab5984e3eb815f45e9ec8d06", + "0xf61eb44e5e5cf281bd970c9ac64b5c4bdfe9ba1d2cef433fe7c88d48216193e8", + "0xa7e4e8d09e814bf20bf24107cf7b5c24f72cc1f764a69f7f2bdc3e1cb05e8251", + "0x51f123175de8ed11fd796c6890ca059cac2153fcdd1c2f0c3d9cd0ed4e13d8a8", + "0xe46d18d28d00d24f42ee4286052f074cb0b4aacad5820eba423828e3726b8a5f", + "0xe12fd4c6a14e3439b79cf49d3deff1c9dcd804a51111e5bf51d356f570280e5e", + "0x3deac120b4ec03c374f399207684d73b5aea6d9dde959f881b93c6bab7b8d119", + "0xdc10de0fa6f8945006f1f8684af65c54bc5f90e3db62d700b0f12f0c714e62b2", + "0x85e3ff147cb0309554db17d0965779813e83c6f5945534cb3f422cb76afa05c5", + "0xa58cb6f6143676bbe1ef636fc6900fbb21ff33bf99bf60c9ea7a83a245c9cb86", + "0x17167eb391efc2019e4da55b73f31c2ad4af86390f5116e500001fc9152dc17a", + "0xd8b32b0f301a04ab4ca0b72ddabec4c6bae9b3a8475d4b6c2dd8027015ae74d4", + "0x91943241a6807ed05c5b2b1db76aac2f9369a47d687cd1a8775aebe951df850a", + "0xb5e5c6c9ff54b18da20c084d7435ca994184dbac0b600383831bb0a8a70a811c", + "0x011f477733aa2e77717b72f52ffd16321f1b0a4bf187aa9e1176bfd45e3ad528", + "0xafebb7574399a267190df02ef766ebf2887c704b9b51fc39da1e14c324ac6d0f", + "0xfdab5ffdee4ebe868e5e07dacc20869758655a81fd8752e08e8d6e4836d0d556", + "0xa4f6d2228c71677b066770d00bf2e25ae309f851e0942705cf63aee1dd49bc3a", + "0x2fd4424ae41486f83874766d6c7f5f0616c0bbad877a3ff3842595d9cd033d6e", + "0xe9e4d8bb9add800ee1b82dd6cdd667346b1908bbe1f44678652932f8c4a273e8", + "0x29a01e4db9b2e90ea8ac6fe3c91a9746790a2a85a40597994c7eef660060f8ad", + "0x50363af3f503405fdcb9b6749050f2939ef511422ae606f253160a3dbbcc634c", + "0x0bded397ecb9f2fc35ab61a4a5ebf88790e5e9e379aee9e00dd47d155d472049", + "0x59960c6651713611e73bcd6224e9c072b4fd38d9c4edfc463b08f7e28408a5d9", + "0xb1bbada2da1eea7bebc9cf8386b73f0084b09054bc8781707919aa57631b8e3a", + "0x82071070cae53006c608f8bde4a5714f882b383d262f7626bd4e2b6132549c39", + "0x0a4a9a137eba7b6ba240672c0fc191fb3b8a7f5fc63d37d423d070f306b16205", + "0x07d146fd5c4ed587fade2f62cc0105b78c19d0618947529934e52bc3bc79f177", + "0x39cff34d86940878442d0faab5eefca00dc7cea468efd54cd19a97d730ec3f7e", + "0x0741ee55392ae4a042d5cd26b2995d82a523b84eae45b3be65cf2ebd0bc3db47", + "0xbffca34cdec95c3b4d0d8d4e57e16df5264675fe599be119417906c00c3d2fdd", + "0x7e296354c45e25e495a362e1a1d3220d137c52dc2ba24d602be8655a5beb1795", + "0x9708860dd2c6624a26d5f6a10a8819fa27e536008020f89b00c6c9b0e3349d35", + "0x4bc779e280067e2e65bdc6dfafe84a6051029295231cbe0d667d03e98fcadb8e", + "0x64f0e9795ac47fc2c33e50e6826c3397e37cd8291d3eb5852f607ecc0ebb3970", + "0x8dabf657300f857f9f1039d6a0a69d6db8aff3075aa3b73f828b912dfdce18d5", + "0xa42077a7c917fe7cae5abdc8001362039f82d185724604d10ca22d5bfc10a03e", + "0x7d95187ee78e08b0ba177b1ab68d45c2fb9042c681628949b2ddcc515f5900bd", + "0x961ebcb10e4b67acc1ab6c865d147053ab64ce3097f25e3efe015831323de76a", + "0x81d2aaac6208d5bfcaa387029e504e93d09dd66a0a4b29eaf16c6dec93adba63", + "0xd18273ec05404149eb8e41e53730274bcbd5c4611a0c7775bbd424c8a89b24fd", + "0xe94ab32de7180c0a698065e1fa377419beface05bcff11a8679b0c181e3491b9", + "0x153f8e0d17fef11b8cb6d345f8f70df6de9fdac73d539f9aaf222574c9d1a08f", + "0xa1e5c78272f1c772413013683afa5b39fbd610a331cb1ef1b480474aa15c86e6", + "0x5dacc3239b124395ddbdbdd5fb400e05587bd6e6521162b959a826335efe721b", + "0x1a873e1e5c1961af3cb5d5f985962dadf7877c5a5419c774006303899c3eac3a", + "0x079c5f57d0505ffcf23dae38f0d0707aa9202f4284f7eb2ea1982cbf95ebf3a8", + "0xd761b4fe8d6de710348e6a7aff71b4b17d3a1987f4a78de9b1a31118612335d7", + "0x6c7df267026981312ffb202c45e1789fed8fe442c5b0ccfd51b23355a349d17c", + "0x07a17c5fa7d8d495725c02a31cbcdfe09fed285d171b68922f7acd9846bbe721", + "0x400f0e749130e99110ab4660e69c847a86332a6129a50006098c89a9fda06b76", + "0x686d0b381a9e4909c4f6bfd20c0b0a4e2574920db5508a8fbd0414ce008a4a42", + "0x54f446c985e7fa50c9aeb2bfec3ca3b55081c4e0c85fde2f6131a2e5eb09d539", + "0xb503457c923413b8401bae531a71f67e57d349fa3527aef6b58288009217d2a7", + "0xd44784b52c6b46ed642eb2add181e02538c2cc077217e7c6041c99d2dad13231", + "0x2fecc019dffc5af6c0dd85f8414645117d4660c90c1747f87f748bab8b5d66bb", + "0xfcc7692d75df9a16ab52eb08d8f41c4c412a4f58ea0afd64b037c7c9e024b156", + "0xc44f22d1fe4088a0493c1d21f1f5bc020409b892eded14114ed2e33a063ea212", + "0xd0d43ea26562c12be039884d5b79d7b0dddd0c5d833ba78a1c62a1085ee355e3", + "0xd27fe58cacf4caab170268087a75c68484591e36995a40e171e8a3f8699247dd", + "0xa1278d38d2460dd92ecc75a3827d0184c3b296ee6a22a7d52457ebb81380dcb2", + "0xf951ca5652c3bd8b4a19b87db51a517ae077d171afe6dce1818cbe28b95791e3", + "0xe1692e1c8c92ea354195469bb3cf9640fe702933ef681fb305aec21334ce13de", + "0xa8f5752630a3a45e67e288108b43fe0e4370ae27b876c6ba7592703d8a342431", + "0x36d3fa953c3e36f7aaf0936644603fc960200d01305d2a7bca438e010e0c4330", + "0xca95cef96a6e610a34045e5e6011769d47a294918373ee3f5c5341e63f575c34", + "0x917955699840110753012d959c83d7e4d03b4ce1f489d745a291eeaf338d3509", + "0x5c3e1bd91810d7ac2cbc8d00c613fb4d9d7f9def6e511c52b2d1a79cfdf45fdf", + "0x32c5b6c65b706d10f8d42f488edd4927cf5f11193aa5759a9186636cbf5c8f79", + "0x74b61935dc128d14100aea3663c695371861fd271414be25179ae18bade695e7", + "0x644b3e738213528ab7045403a3a693958a2e0ef008d7581d528e969ab00d30e6", + "0xcad235839d1e8132206e5b293aafae5c2ddb993315e09e447be79ef89c97c1e6", + "0x60aaeb91e81390509ad7b21c9656f934b14888939baa8100eb3caf0d7276174b", + "0x8db37d3c77ed8e1a96523423bfe414168a2b544350a29d8bc2e547741e95c748", + "0x966210178a46f843f3263c89c1102a6d83a36b9b35aa332334472e771124976d", + "0xe8501a24d0cd96d69accb05897b8397431e4713ba2525bd5ebfc478954a35b89", + "0x63dc1a58d652f72786e8063ea6071ced4b89a0069cc5a1786ae8e6db7f1ee082", + "0x5873d4a31a220d3d0b7c2feb96a2f3827e3bc3307446d33a42c4b863c4e999ca", + "0x8996d377eb10e59e295d3d0ffa245946758b8758f65d9451565ba2ca2e10fe9d", + "0x05ab43d6ab61f303decc32725d897d38b39a7c606f0fb24ff5b480ec888accbd", + "0x2a30a847725cdbbce343e5519ab3aa6e7d704df7d0d2f3405028b2547af98073", + "0xb40b8ea49cade205bf3a09c5cb987b1ce0505d0b797a8c98bf4028bdbe7077b3", + "0x2962003843951ca0715757d86e0af8b9faed1f877352e492e388f494d9d2bb00", + "0x62b54ac0d50b32eff480eac30714072906d400cbe169c8e605876fae20ee2ed3", + "0xd741434b6b61784fe1abd2fa2f562288737c10a75cd1a2d4e8bd683b7afd5d55", + "0x27e1f1771a2a8f998f89b3c83d777bd46b18035125f7c388e17f6ed3b2f51303", + "0xf4034374c82d5ce0de7b3c251f57370c27a7e7a88f167fcb12359e07104d1f92", + "0xc692e3b9417919ab07d4aab327e74e2221bb5494c11b76b75c2369b232d5f390", + "0x153bcaf7d92b292ce8c17520dd8701de9399273b5ab3ee1b568f381efde3a5f2", + "0xb66117bb45ea18b4585242cfadc3c01ae513e3b18d6c140d23635dd8795d7896", + "0x375b1717766c62f5921571ccaf9e101dbb3322edbf833c6de81e8b8cfa6f6530", + "0x6a9a5c261ca34922b8fbea3fd1f4e85bbd0d78a9df436ba05e46b77757df3968", + "0x38df886ca2b22975272e4c931222dad7d24a03e686022c63f04106b4b6acc246", + "0x7884370994d91171caceac9a20c728ebb13d2a293066e754e36338533d99866c", + "0xfe62e1f4f2c227b344b5fb902db27722c918bc67e609a021b5248a557f85c109", + "0xfb25a775206f1fbb32eb8459d9436d2a6ab2fd241d9781253c05a2939a6290e6", + "0xa5c0669ce43148fe74ed56e84a15fb2349e97919c5553723cb614eb7785262d6", + "0x089db6fc2780f79ee3dfa43e16dc285c7b14c22f45af9e8759a55a313dc1d8c3", + "0x6b80b446e66dd1613605496328a100437158275949b629c9f74e070616f1ec4b", + "0x417a5eced4cc33b65c2b9a889cb7e719890cb7f273a47ced0ce53a01a26432c1", + "0xa7e2096d4baa99f8e21eecb1750d4301028f372565ca084961986886d9e0b597", + "0xfd5c8346e941f998fe270e3c8721c37e2ee1107fdb05089324909b81ba0037c2", + "0xe340b2298c0ca2dc4a756a94757527e8041ff2875f71a7e83632638f187b7c0a", + "0x9474354700242fbf4218c5e231da6fad1f37c8cc919ec4d0a9c828765784dee1", + "0xa54e014b4e1b94a3fb9f5283c38c30e6d6a88f4624254ff91541cf42194b3616", + "0xa5f7e947df35d6acaf4399386bbf7df13e4ebda4339eb83e1d4b77c401acfefb", + "0xe504d9a5813fd6e6151a0a817a48a5caa8497d4c8bc55007701f475c4c8a2485", + "0xb01901e6f1e343142473dea2e4675027afc329c869197691f224708a1d943698", + "0x81f8a150d64e1d231e4f7559197d758308503ed89e1c00e5d9f8d740d2f3f189", + "0xfc9f714426201d85e931ca5fc19b816544623b2185124ae90b40d22ed6491526", + "0xcc97e54ca8eefdbc203b28a1fba3b49f5a2f351f828f1bba0a0a5d9832f7a817", + "0xfaaf3e4e12939dd49d6c8210c63458585091c302f3832ad64d019212edd20cb4", + "0xcc8595ab958594e75772048fbafa0304c8b9c448f361c3d72d24ba4e71e40ce3", + "0xa5ae09f9a572e005fd55cb56592c01daf2da3d62b75165f770bbf62edb046f0e", + "0x0c9318bf5f3e8742ca7f825af03028140621c792628f86604e5a623c285ca665", + "0xb386f61e9132ab422ce64a54460e287fbf7cf5d5c46af2d8d8763aad6757c120", + "0x76226942ac4cabec9fe53d280556d09644a54a4f1e66f6dd466f0272274f48ee", + "0x99f208dea014545f3b19b3813adb86a78fd63ed18723cdb65c8466e929f7422b", + "0x6b1f1efab493ed9028c43d05050c665aa1765b1d156d8e25ed79071ccb736094", + "0x8df36aac406320983751b8732f138f042722803de4b283b3458146c698be8f3d", + "0x584363a58392f93560aa6a460967c3b00429146da8aa56a59319553cecb0b0f3", + "0x5c24ca866a9b0cb50235289fa24ecaddb7548e61d62baa90de4fed3d4dbbf744", + "0x96183db604cf5c2859ad197055ce11dabdf4d56ad11ef9da8d9d54a81ac27d1a", + "0x357d070b5597fa2e28769a01a7861f9e06bae507930811391f45456a4a0347bc", + "0xebf250e408c0823ec43a40e3b54115779471c4bc3e92179d11e52a974252b599", + "0x8cb47465f35b182fe0b7b739d4fa1873690c9481a4a6e009e1ddb6f294c95219", + "0x61fecfaec49069feab7960857b1c402baab23e8ae84289ee8d0fd9ea68cb986b", + "0xd1d725bfe840aa78d6257db7c8fea1980d12a47d2263ac610fb21daaf25385c9", + "0xd9c9d965edd972d3e11cf8e48d073864f16e26bf933e95919bba7e4dbc6a73bf", + "0xe90320d81794fc0b0a1e25c81e8fdb37a1b3576483fc5020d5240d7d86908b19", + "0x6d0c92d27c40b2e67d53892c50122cfe2fe12935bbc1b49a7e6fef9cf1d968b5", + "0xb1c0b8c6db529f4f8a7dd9f27d9d10cfc1174b405c68bddc0d349eb3fda46d45", + "0x912b7e66768843e582a67ede3f7feec093923f38e0e2e49c163df558dcdab5cb", + "0x4ad155f37c9a788f887e2cdd4421abeaea0276692fcf8f028c6bc509df6031cf", + "0x3ab35188271ed4b06a3cc91fe3608f4fdbfd1a797bf0a1ffeeeb6bff665936a9", + "0x4b1d5cf4a56fba76daf45b0cd7ea6c2c2b1ccea0e6ce11038ffa6f4bdbc22b3b", + "0xb98550ac823d64c20bc0f3cf17017a087a29591e885dcae1ff8d03825b5eee23", + "0x6f1ce323258f7aefd98aaad36e6eaa1cf49c0a5689398509dce5038466a45e8f", + "0x5131465526e5dba03ef62c6f51f91ceb728cda6cf0ffc7a492be289804a4b3b7", + "0xb7d941e6d5bf5c5b0943939626ade2c4fa1008b7786e4f377ab578553d160e9a", + "0x15d121dacaad131e943ac76376fa5632955648e4cf0c396774eca0025a4b6f76", + "0xae7699db946a4e92fa72c6bc279ed2e4503c47f7db6e7684f930b5ff84555f19", + "0x551998d03d444d713e114e915f2c9197cd572ef76d2e7d728ca6ddafd0e4456d", + "0x038f5bc900e19c4df89d6c77b2cc3194ea9faec622d0a8c2d74c9791083acdaa", + "0x1c9620d59eb813de0c193bd87ca057577005a5f15251c293b0820e2d54a63b54", + "0xc18f5353e7eb541e7dba1fb6055b0aa40577ce98f94099561ea27ac5b10b89af", + "0x8f2667868d878dc4cf0a370e2fed9cdaab17b13ba2a8d6a7ccfe3a4965c43c56", + "0x0220f4512465a554615ad4529fbcae4b65bfb81f755419759f7ba1256d842598", + "0xd5e37870e567b04faa2f3e83da2311fcf3c0bc7d9db0d706477da7e259dec706", + "0xa5905c952c8d2f3d58b13487ece71a660216056e1cb664306fff0282605f77c3", + "0xb904a58d1cf030bf8e704f9cf5fa8939f12d45068d9ff1252dbb20cace64a8a8", + "0xba9c688596372113564e502ae5ca744f05ed91a93d67f068a8c369b2add2344a", + "0x277cb552203a4af4245310b40dd0f1eef2c43ddc07a6fc0579f83b14077afa61", + "0x887e347d2875c37164cda57fd440796445aa08ab016347165b6d8c393b673d9d", + "0x2abf9a0d9264d772f56c63c541c1df25787346f29d0bab7bfb39dbb364e741be", + "0xeb975ddb2fbc70b97361d0b7e497d5bd139cbeb8a5f2190d22b1c6547a211d17", + "0x078296f9e1cb4890eb5c8108eb67253d5dbe6ef921e812762f5cf3a01dd64903", + "0xfe6686b6371b2aa45c3d0ddb37fcd9c1f9e662a734c164fb2caa0efde2a0a5ce", + "0x8d4252bf11cc766be48b8efae676ca81c6e942755dee6bfa4b5d1c1e075f7223", + "0x6c16b29f507c1861b0013a87be5a3859dc740380d86fc411fa1fa83446d64ec5", + "0x29f704242f83c508e0864e5bc5f129ff7601027337fe756e975cb402e2e71d46", + "0x09f77a1839c28df20a95a84badbfb528f7f93ea3408a3fe0ee9d043c747aca24", + "0xdb80f514a2c541f28fe28162a637009c443a4e6fc5c34ec7cf03896ef2b14f9b", + "0x470ccd41b2c39be02b465e7f98efaf865a99de389a202703e3faf1b07609566d", + "0xcb2a2f524f3012b8b2e29457f48fa4e2a037c755b8c81ddd774b5306ddc85756", + "0x03c5a2e367c0afb75b9d604f1f2ade2294c9737c4e4df0ca2f0986e262bff58d", + "0x3f3b882189658b718ea45ac8e4cb965ce0b009d5b900764038f09a89b803ad18", + "0x193a92bec35fcbf2f2f2206dc81bad13f3043b0d88d6c55fa829fa84cbe0d27c", + "0x6722340f8e7345a3167deb2f733f53bb44b9761f3661481da2002044f8033f66", + "0xe1f432d3ab4b3dfdeb23fe4ad8033c0b901dbdbb5dde18edeeddc5f443570930", + "0x96e17a5e28ed8553660d0f970da82590441037f762df1895dafda36dab48a37b", + "0x3d9691b6ca8be1cbe8cbabc024185653a5ed3fb99e6b0ff8682c6068b35b08b8", + "0x1f98e8c19b40387962cca2bcfe57de8a4d9250a0a0d8d449ceb66bcdcde4c5e7", + "0x42643c6c3e94fd2773a105de85c7ab10aa9671d16d9bfed6aef460fdb6b82ea0", + "0x4d7b7f3875d0ec71282f44c0ea785035fbaf3467fa9e4529652436bf9ba72677", + "0x0f7bf5d0518f400075f9f35fe766b1972202c4f6e7a1b69df86033087f0ce698", + "0x80f8d7f573d02ca0b60090cfb94da350c1bdd7d86bdfd66fd372b93a4f0b962d", + "0xe8ba161d580bd46aa820336a55040989c252194764cf74e62e73eb493b3875c2", + "0x4967458f89507793841e464491af6581604edaec5ae41b5c94c77a023089e65b", + "0xac63834a27e3426daaef7feb18cd9b78b747757aa43e9981f9a150179c1011eb", + "0x41c4a97fb2bfe2042c8045201ddd196fca6c8dfb3f071f4111d53a27f3e35144", + "0xd41f32241529680badc5ea8023292526bf701476119e66d1981c2481068a9d9c", + "0xa99c464cab9dca087cb4124db21a35890683852437c4ac1ba883fd62994be143", + "0x8436cea31c27072ac586a891526c61613eca69b1c33063653e46d5d9831c6870", + "0x068e9f8dc534fc5f167ffeb0353ecf4ac22e0ea7816ea4999dea09cbe8330814", + "0x9b5717d751d3c366c2d9f2c2827b9b626663aff2e27232492c0bb2b485101b5a", + "0x7b7cc7f421c2bef18ecfa94f91cb8d4c7ee0dae27386b020d4a6c469894566a9", + "0x560f105c480749d025de32943b1649c927d161598eabe026c2bbd37a1d573ac5", + "0x16e8e12c7b0a4aa4e44c8f77acb32702f133828ce162e9882af0dda4930907e2", + "0x8e48f57e3709cfe003e30e1123515428f48237809def81483c22a71d623928bd", + "0xc08a8a4b00bbd6517673589a43a863377ba1f7ceb2cac48679cad7c1c5c50706", + "0xc5ffa164a2110d3f289a77fc423a3520b68d4e5627dbee1b7e66e853bdcc2d21", + "0x367f791f4f2deacdd1ec2ce84c32bf7ee97ef9c129f2da6dd13053195be90b07", + "0xd8c015af5da3062c7fc042a01c9cac8692b1cb872eeecf1feb5b05bba5e7fd21", + "0x150a2c31e7cdd73da44345a6df7aee063db06ff377a12507903ecd5f1aca7819", + "0xcf1267e62c644091d354d4e7433beb63203ec340d38d8d23fcbfb5864f4dea5b", + "0x3160699fe6801ab829b942b8a318ef97690487dc2e8e93b0d488f4eecbb9170e", + "0x236152e8089720d65541ba4d8894b0d3471a4eb573b2514a176e9d8c19eebd90", + "0xcdcd25cb584f0b5aa4fd37cee80c2d66ab535db16049d619087ef9cb46baece8", + "0x4581a3c715042d7f817c8dbdb25e97d52179dcfbcca763238c3b286329ea8744", + "0x0b3207e1944b9e36af484645b8c931adddb5ec158fb26a5f17861ebc4caa2ccd", + "0xe36e73abc5d777fe3a69636f899fd2d6ad1d75b18f3088735fea66af3723ed53", + "0x863f2e81394cd93dbf45191db456e479449f099ad91c81e1a42ea025215957d4", + "0x76a79cb73d0ad91cf398d404566a27f3363ad76667169610cb8889c1e3dc6354", + "0x6a8ef7b881fb96d0b2e9b0f940440f0cb73338a2d6f053150aeb433b0566b750", + "0x04529712a92f1b3b223d7b44a21a8076c2e3e56643686bf76647b67e9b6459f0", + "0x019071f2c6a0ee3896828a30f28d150934d5dd4d538f6768dce6234b1247a7e7", + "0x6cd2b5349574279b471ea0d8b61ed44d93a3fe79c793515ab871742217409f49", + "0x2441366be4fb904f0a415a6fdfef503f9729964cb33b2de554e8a28fd154a4d9", + "0x4c885f6857b1e66190072a064ff303526b8847d032aa9f77cdd26aff91fbf07a", + "0x3b9e61be3e9dc7e3c71149f24271e6143c5cb12304ac29125a47bad3b933e502", + "0x86fabdeb304d3b9413adde78f441de4c87cd9ca9afe2f4b042ea21c24c06969c", + "0xdc0b97a9f8e770d982a9ac6653d19122fc33f1b1ec01d38090770c8c47aefa52", + "0x80c5134066f56bff6e4c64381d79cea8f7dba44bf18c3160b3543d5a2c33e029", + "0x895a8f481fe5997b7d4a9c4afc6997f9864e19bc3ac712c0ae96ae85c21f0b06", + "0xe552304f1d07ff53971f1970746f27fecb432f3640e78cc07eb06e09f44eb444", + "0x2e5398480a4461cba24742700158fbf3170f4b5db55dfaede848891f746bd196", + "0x330dadc74cf91961e7ee19c3f846b8a8a70d8089d49e2ddcde8bac53cc8fb564", + "0x4c98dcc15e5cb906289ca80f53adbb4f235dba7c9c326ecd86063bd9f59c60d3", + "0xb0103f522598709acaa9395bf25578568748bc5c440382206eefd7f47f1e5437", + "0x6351ea0189898ed510c86d5600eff8e24163e879c7730761111131f1f8d7f586", + "0xcac88212ed24a738a09b68ab5763f9245de85f432e40b153413239308830dbb3", + "0xc9a31a20e5d5744df6075254189e30b42bc857f47c9b844c078f336a18af24b8", + "0x8c1311fdadcfe904105d021ad2cf3a7c1f032f285ac8694539caa7014bdd18c8", + "0x13e6179eaadc4f66410b3dab6c604091a01f33545376f48733217973a9aa65a9", + "0x58c33c388acbf21e8db091c0b6286206fd66b8744adce795a5c2a3a9217504df", + "0x4125cc79f38b505578565c84789ecb29a2868179668dbaf4f5f223974ced39a2", + "0xa14602d9d9c829b22ad49f7a910218db1f1e5d8c73731868751bd6f87d0f9bdf", + "0xc0558dbab4ca48090b02bf00e2f95f8f563552dde9a8cf1af1fe61462b63f5e4", + "0xd9fc4e940baac0cec196095e4aba8caa177cb3bb2cccd1a97761ff61b0a58006", + "0x722337f2627de8c75b9776518bb5e57fea197f2be28d848fafb0e0c1d2921d19", + "0x04155a40d3af6845cc43bc54c594aa8984e9131c03cd375b185f3904163b2eb8", + "0x5bc18784ba210cdcfb81544e2400f3defe6724740691d8e64e9fe60029e51944", + "0x319dce053646635afd72a5be737cd043198270596e8fba3e74a08b8c90a89cbb", + "0x1b90b07c32cc63c5825e86c6218cd1767ef469138d4adb73728c8494cab5aea4", + "0xc7e34bc23323b10089b333a3f2955f681f7e6412d2ffcac17b3f3321efdc4353", + "0x06efda3c920e80c0c40b588227045d9fbf581dd6b6ed409874421ded8c651f50", + "0xb2a7ee8c3ec616a9fe2d649ea4a23e0a439ed3ff6d43f9e75a1e5001f097cdf7", + "0xc37a36bd56dee43f65335696f8b71d149234fa3dcad09507640d9837ca0912a2", + "0x809d37c11f7f40e450927f090a42724312a7c94f142d0d45b5e6ac9f350995ca", + "0x5854c1f5ba6eff16bc8f7584eaed7434d3cced0466439a175aa4d341b48b50ef", + "0xc33dae9cb978446d0813bf70d90dafe34de3d311450d598532949378d8bad10d", + "0xe9006d319d4798ea0a88713573b3ecdac0f0526b863cb9a79a193a8d3037f9ce", + "0x722f5b545c626be80106559db4fb27256e4b5f60f0e5cca07aa8384ff6ad9621", + "0xa2e92db3d40734c999fc19a9fec4339318e5af39aa4a235638230fc0ae9f6a2a", + "0x5e59813fa3ff33f0e548733ed2a9c1342862ebcf2289b4c408c64bf34c249b3a", + "0x9673e1e8bbc2486596efe3c87bbf8d8c4c3ac25425d86af1f6475a178cc109de", + "0xf8c269d0983f6b427401d9fef1572cc2e374188b233c6c0c543a5aa604120e19", + "0xef86adc1e872b0cd185491f700301c95d00f58361381fc2333cdf8b420132ddf", + "0x5d30205c7b533de5ce21f5b790878fa825c379da4cf814fa7ef144828b64ab75", + "0x2b85e072464566141508b0aa2b3db619e16c8e7adde0e4b3def3d8f415728680", + "0x98098fd0fbe5006b74705011c46f4e7bd6dd6cb2c08ff77474630e2aed9d9f01", + "0xf84e04cb3c936831429d81562657633d28539c64e33830d44bda7474ece3eaf6", + "0x060c532c0437ee3b0822f335f028f050af37eab85571a00acdedb2bcac5f7a24", + "0xbdb2906a66f61fc44b7bcd8221f1f2ed6ea5e34db48724bea83d7401476d6a80", + "0xdb69e1b495995b7a7228a907a7acf25678851083f4a8410eb2ebff82115fdc12", + "0x9b90837d74346246adf95ec4917fb0996479b42361010810d9e4b4882bff7678", + "0x7501e722acd4e925a578003de87b39d5049abfade8569a44423c285b32fe3e34", + "0xd516427e6cef1c5fc01651fdc0e403842f7b7ac33d4ca17214433e80fdf488b3", + "0x859a385247790315b3a1c7b513b8b8005e5395f38e57c1e8036c8ccc3c86a6a0", + "0x21da2f22631e4be9da85fd012c8cff61153d965fa77d41bd16e035fec76f9f75", + "0xafba382ae7a59aa6dfdad7df6ffbe31e92c07ddfa16fb85cdfdfe130f249a52d", + "0x7b55660b97271d75a850360c5f910c28d4d849e9ee906d2646258222888f58b3", + "0x8bb09aa34e7f1698f39158d39f087540425c0e31954e754181138a81a17663a3", + "0x4877ef61640a6f564a2a631452bec633b84dfb34be1aba1625c5effc841b6315", + "0x93cd910181deb4035b2a600c22abeed390f9913ba0179d5fb4e118056ba2e960", + "0x4d7c56f0cc7016bff84f369dcb430152b18633f7fe8c7ca5d889f1bbb0875650", + "0xc11fa6085723b39e2904cc7311cfe76ed079b8a789af69b99626a2ab61414307", + "0x4c27a3ab68af1b22c775156df5ae4604e439b507bb461fad51d50740caf34049", + "0x099e3f9dbba32f3955c8b9ed153f80844d03b006c3693dfdf53809ce9552c26a", + "0x2743b2f433af8b88c817ce99c6a638c35d9a62939937b5c187c7d1e9057688a7", + "0xfc058005fa4fee5b369b20a355d050f7270bf21900651a412bdac8aa5dd5c0b4", + "0xa5b03484f77733a75320a530c7d7f6c15ccac98513b039f13caa7994d44c6c1b", + "0x5bb8759a09f4e3065b6093a490250e4e1f66c8d62b45ea9cc5fa18305d89ffb6", + "0xba9d45978a8950884c67017c26addc14ad9655efec77e2e2fdff7cc7abfed95b", + "0x64c1d1e9d92607570390fb12d1d04c4ae5cc7089f4b05a85f0065b1ba5a52f22", + "0xe6dab9326058f05d3b9f1e5ed6ceeec27b7d87f016db9b9d91ab0eb3d02d1252", + "0x7a34b10fcfa84292fa619b8e8a4152a4c6d05ce76f9681e742b1032e34bfabbd", + "0x67fe0a7636f4d6d76f3eda5f3ba9b7129c6cea8b633cb39d9f916c470f64f746", + "0xb30c65d96395f1147aaec80bb078c0fd624c645b83bc35718d33b9321bf6d868", + "0x09e12b3f985ff9bc2b273cb81e201c3140331f5501d9402bca0a7024f29b478b", + "0x105331c490dda46513daf90fee23159d2ae8cd38c7d86d11ad0636e83b9025d8", + "0xcd5001edaaf5074d90701158abcff94c69d13e341b84197095fab3d5a5d6f04c", + "0x64f3169695df251d78c4edc34c1918c0291bf193bb22c5e3fb9180f2cb962eb9", + "0x2b3e2286806f222ea83279e014f77e83ec299091d1a8981d9bce5f253a9aebc1", + "0x3496fe3abeb7b40d1671d7a9efff988f043daea2059d415bd3ba8e0d628575f0", + "0xa168da996baa0f5af84d14c55a8697736f349b942b97a68ea1246195fbfc1493", + "0x32bf2bf64488adf4e860cf252ed826709be86060a51a39ca31209a5204bdfd29", + "0xb20020963210896930fa48b4be7d102a535a14d23eace7f7481879dd0b6a26ee", + "0x2eb1cb24fa1a6554c7315bd559403113ee922ec76414ffd766d2f1bcec41892e", + "0xd63c349c89997a3222faa33d4a7920a92157d336752ab37f9daccd2db96b2a68", + "0x7ae715e5ef46001b43612bed59c469e8b6e8eb6d8c6aee15f0b3c41f4c14b968", + "0x797f99b76e510f6564e272d383a120ed28deca1439e3454a3fa78143791aba32", + "0x8092a1a026ccba7ce87e3eddd4019f6d714c7b6b83fc7352ce425aa85938c9b8", + "0x0c39b4f50b968983396039bdc5f5d0f6c428062892e9f66da5f0565dbe193cf2", + "0x543a5752369d866fc986bc7ff2be3f09a3163cfb946d7c1ffb6d4895a7ee4d5e", + "0xf1fb11cb939b8f956367ade5722ee194e4eed22f980dfe232c4bc674afb59dac", + "0xda7d418dabce03c419157086da6e9bbebda9a3a536eceb43a21c340421abd694", + "0x622d9e5fecebbbec822f01a982da3473d108b590d2648236d20d7a1d00b9344f", + "0xaacc6d1fd7cb02cd708da25068ebd5ddd7c65a3677ff32788428e8576c169ffa", + "0x17782a833850969bb314be31153f134c97c76dae38adef3e15e1065d77782d87", + "0xc7949d912d4fe20fe3a66bc68b400fd31720f9fc9fbb73645d19fa4c67663c77", + "0xdc7fb10ad8d8a6c9bce2df10aaf5a4caea9871715819e37e2e1df2e4a172bceb", + "0xf821d406f85ebb8003306a73912e73e24cd4cf5dc44bd2f9b3643d3ed9020756", + "0xe43e43efe7a9f0ff80e1705bcf2ee786527ad69a6386150eda1b6c8382a231e9", + "0xd0db62866e77ab438f4167ade90248a52a88ffdb978987297073ade185958ba9", + "0x3540aaf6b3ed68fe56fbcbc0188a287b2833434de5a16ac315eab09b914006ed", + "0x01f37cdd4e2d02623d3550dd46ae409dde54cfc8c039248a361238c7a609614a", + "0xa6cabba93d8e37b613e4357e9002debbaa4d88cab249629c096aa46bce7d9785", + "0xc7b53049735c7e43c47bee21d2c1411e7ccbdeb1244ffef8fc5e4793319fcb97", + "0x42e030cbcf4529207d43cdfe6bbcbf183d1e9e81a4f57f08ecfd91118f36f497", + "0x1c694f080bb873a3670e49604d7cc376728d58d5c7d0f66c88a53092767c140e", + "0xb23af019c5fb47e316581ac225bad629598e8d5cd533541b2dde1512bf622dbb", + "0x02c66c4bed42dc0351fea53dc34bd20efc6d64eefd350cfc858e6225fd862265", + "0x5b91bb358ffcf4e8d5771a3a39fcb11521e80a4ee6e6df3d7eff9811106eb4f9", + "0x057ae6223edd353992f7f54b7dfa2056ae2df0a218af45b56ecf820835a463fd", + "0xe88829b874fb342a61ae4920989c89eb7c0ec77ed084057d0102077dcfe78f69", + "0x12cd6aaf7441debd6b97c86969c1371078a6043b19244e638f0f18b64dd07dfa", + "0x8447484be7d8fc5d91ea829f1c04c03cf2f2d0ae8771d7498d21183471bb928a", + "0x0771c226634ef73550c85582121faae6acff53fb33a06ac526488a93d1ca4883", + "0xc0c61840a3b70baaa40b736dc8ab2ae461ae9a8f6752210382d514f25c5ada7f", + "0xdecbf06590884756e9ca40e478b53f62674cc8ba14f0378f7f8bc24774d6e52b", + "0x036ba6586c3ae53ff40a93dcfe738e86e6114c68995ea3dede8c26ea2ef741d6", + "0x28cb67c0cb28b5d6d2554ebeba55d5d1bd62c09e266cab127989f0feeb07d04f", + "0x9bec4287704d2223df93e6cde323cfade7204f33bea2e21840a7d777bc4215dd", + "0xdd90be4c32c95ba64e29ac10a0075189d79346d2cf88382b5dc6b81f9c71b4fb", + "0xd8f186ee7db56280e05aaca831c9e874d66346039349358232f4a8e24a9ae472", + "0xb3522869f25e65dbd722f8d25b6289faf435abdd4be58475ec53c70a2ce37c11", + "0xb095631b4ea0e617b60aae1fa7cb413ca03914f20d5cb990518f3f389e3c6583", + "0xe7545b0092ea2c7f160bdd7caade3ae77b608ccac0a60d796c45f66760813806", + "0x6c6419793af291880027826076a909931abe710674dde1e300653b0499326082", + "0xdf7f6c2f6d79511b62b1e1d4ed73ad25cdf12effab24057251bf4305178a3c65", + "0x630c4e4e4af58599afcbc4e3842cf81039414d998e96b3ed23f2588de918eff0", + "0x7c9a4ebb473f7eddba997126095d566ebf1906e6fcc3ee774273a6e57db98cea", + "0x685d131a989ce6efe3c34ce94c002b343cdd64dda9d4a06cfa619c6e791bb374", + "0x2695877484534b708a170bd020459ea60b1850e0f6b7b6c8ed009540f4042671", + "0xdfc2a20bdf440fa0c83959fa6d72b6aebb07c1088e90ad485371e85b697477d5", + "0xb7cdc51f1c250349ff428db11eff0d1be173189892935017d46715f7b2aa6d86", + "0x42b5ed1fb3aa0a2e4a573573d2f9adfca5bd9b87ea9f8ab0726937f1b3b43751", + "0x165713540d1e26de8e93bb0647cef0c4c5d9187b10395c8285b89c6edce1321d", + "0xade29ada4c9baa27d8a619c98a489e565b5ef165b84d2e13a3e70d6314f2f724", + "0xfcbb912dafa7c093bf62f37f67f94e40b7277b6fcf05635b3ed6f187b5ad0987", + "0xd92b1006022deb5e85ccd820dfb6da11e9a347dfd466505b325285cd8a81f915", + "0x963614306cd10edfb5dcdee7d9c417717e309f456649b0cdfc0fc085ff22fa28", + "0xd5006eb7a606015568ebb4d0291a499e10e3c819e67343b0bd48fdea4cfd3f29", + "0xe0e99077a642b21d465ffb785336b5ab74dc33633dfe67b94dd917219eacdc00", + "0xcb923b95a717b24af4ca5f79240a2e744018480843d9220fb9a03e79ede17447", + "0xa20217a24e69e664c602ac8992396fbf560879fdb4fa03474895d0fe7ac7745d", + "0x8783fe2b4d2e28eb928bcb346e0aef3a69bfb24b2ba4000460301646a7790532", + "0xfc4eb62672844f9d7967dbc1567a72c9ad14a0d33d3b4aa0da1a39bcee0bdde2", + "0x119c9cf3ac2514b5d3a62fddc18524e0480983627e84cb486e5bb187d881aa31", + "0x8caf13d043b976bc147914db91355758cc0d5616e9673365e4d5a86aa0752258", + "0x7c6324309f9f3b70ada7f81324dbaeaee25bb6b62f9f76d559bddc3a41bba3d7", + "0x03846a6179f04a1d3b951f5f4aa46f52582c0c130b985c044f8a0b6c87c4be3f", + "0x3d884dd0d0e01d19dac90c823dc80ebee737e20b0b8c8b5835dfa1bebe14b004", + "0x469278b21187c452b13470b3949f69f092567fa3ad41c9b6b177e7fab5e40bde", + "0x21da813a663f5e56e3a4fd128a2b515cf3aedd87a80334de90f3f1556ca7f7e6", + "0x8818ca41758d5634ebf2ede69caf63dcad0347b127e977023c5682bf62047aae", + "0x06fae28e3928996f0d9578cc5220b0e027a813047abf78bc9281cb8f82e83625", + "0x2046dc56e79c0238194e63bba1268c1cbc12fd2229e2f4e886f6316280308572", + "0x14af2e6281dffc1e3dc2627d91dc5e4eee5fda1716a438d637ca87952706d2dc", + "0xc8bca2113f849e7decd7cd26cdfbf662f7296653dd0c0c93f1402c900b6b3664", + "0x7fbbb75f5559b269f0e4588ad872ab2c13889d180210420ca425c31f0e94ae38", + "0x425cd8f30aaa851c8bb22f4386df3f41428d013918061a9df4c6f84a8a0d1e1d", + "0x7e0e6ee1bdc22dc9c483f7686b30246aae8b0d746ddf4d29b619b2e8ca7fa505", + "0x86a38826019fb6cca1983fdf88a4da3b3ec3b39c69188d9a2f2ba5af000d93f7", + "0x4c09e77870734a6b2f1edb29b06ee046b9006bb481c2ba32841d4cb418c8ed01", + "0x7c9f0a7f5940fc8f71f25dbdbe344afe31f9280a5bf011013ed76c7d506f51a1", + "0x1de3bf0ba124a9587654369f69f9e5de4d0d564c9f16be6b63c649a20bda0803", + "0xcb83c42f663578c3d2feebf3fe64d2d9aaa83d9f540ef98e6ae5e2e448f2b0f0", + "0xdaf7b29faf69682a32cea6cf98a8d8b7425f46fe058d4376f02f56329378559b", + "0xae727f3a4f46e683e689ef490ca8457449e887967067efc0bd90aec191899d73", + "0xcbd8722172e792114eec46c4d16b702c6892581c440b202890299ebe96888b53", + "0xbcdb346d8ebbc80460863e06a1ad729c97fdbfbe873bac6d29057a7235b261dc", + "0x9e9c1a5a190dd19886ec2e94a673bf9b2d4dbc694588050b5f1c711a8a1edfa3", + "0x45df22e70a5005d4a53aea7cc8a1d3a8af30acb051e30ead482ac5e7c72084c8", + "0x1a3b6319d30fd361aa1c5d9e43426d46c27bd48305c4b3a4f3c0454d4afbcdcc", + "0x6ee62182c69130be047ecde75563bfba0946adacdddef21409fc54996c0ebc82", + "0x4a6cb678cbb8d630fa9cbda8ec186b8c2ff2af0405c963d9a6558c87ed09335e", + "0x70a4137f4fbd5051fb346873a869363dfe6d8910766893d2038e56360ec76a9d", + "0xb7299233ad5a38623ab3529f193f57c2d76e9199daa6a2f48fc9cdc91314bc3b", + "0x4871e5496fc1d14910e375ccf10a028060fb672f5a5f8dbe9b40c9758bc46da8", + "0x0df60350ba963f8ac236a10570bb3ac462bb2b4f8b48db525be9d2bb24688a4b", + "0x552234371e8aef0d6c3cb020835ce6882f9ff4ca57a81afa6609c30be9bfe314", + "0x1905377649b287364d02cf84adb0be3c51358650b3ceec69f6cae0cee6b551b4", + "0xa4145be666bbcedfec96569c6cfe4806f68b95089d027a444d8be3b653423455", + "0x5ee743825004a524478d5ae04bc9a3bc5b6a14f0d8ba94631374114dedcf340b", + "0xd6b665fb08c6e537cb50deab8b62de59183d29bcdb7859c7fe716f73ba022469", + "0x640f2b43b92e30fcd7482aa3177573bf02dba87758a7fc43050608cb83143f04", + "0x5e32789c7399fca6f6782382022710855b2b3a313d45f91c195b7c6c66cbe6a7", + "0x9a40bdd78223664f2cb88520dc6504c0257f4432c0aa6d0d88a6bee34b3d92fd", + "0x532b798f9f035e6e981ff727d936a0cb1ff358f258f969a3579fe868d1a33f27", + "0x0bf6fa05f510a5a5d11e2376e64b5d89e233208bf741cf21ddfc8f4f3368ce05", + "0xf9d98477a73d2b30e2cadd69d887edce4ab20a0bd9ee1ca1c38c38862512f527", + "0xb5a96c23ff35e2b2f0ce808a41a7493774858a81727375667f6f6799ded9cc05", + "0x79300a2ecf9548b6b6790021cbed6b2e00b5c6030588cdbc3428cb547d79d94d", + "0x9d0e3ce4e8792dbf914cb0d0a1b8d082370d1eafd900d4813d58af5bd3529e20", + "0x66f5439897ce3452973d4c2add5858da16c898e8ca3cbd087248a5dca5993fae", + "0x25be017398ec31a5598c815af04036fa252afcd2e722e086a2ad68a7127bcca6", + "0x85d4a5a08bf90f3db3140597a01fb6d9817dd472a2ece8f744ac0d20a59f81e8", + "0x2f4376ecb3305b3039bd36b5e0e7bcab873150315577cc6ef452527cdd4a0863", + "0x6eeb5cc732b4164fa65ee3a27e7909642203f572d435be9ec535b56e0f35f704", + "0x7d50154a51ea6c71ec33b46fcce6d189ab71935dd6bc83d1d6bb3a2a1881bd4a", + "0xda118b3667e43400b425269a1bc91b3698db9abefd1da558b6e252057ccea5ed", + "0x5b3c3cb502ee9910a2304d780fec87c91bbb8b1c62ec7359854b83c02dd74344", + "0x10affd675d1f973ec83c4e364cd1deb3a447086f7e028d5fb9a742ba869a2fcc", + "0xb8985e27dfcf88e178076f602b728d79b13b0e3b896f5234aace42bc8e10b7ef", + "0xc68fb409a2bd099c2b9c427cbf175117f6457aae2a7686dc83e962cae1d09605", + "0x618a85e84f79c9225cae18348f63b881512d7953d702bd73c6412334cd4bf262", + "0xdbe5e2fed70a944a3061de6daa669a1bbc76aa5ccd9f25e9679665c6041c6051", + "0xcd2b7f47a1a8f716a0b79c25959f50178f93005f0fffe893d8d4e1311df8dcc1", + "0x343073cfea85217a52f4656814cc15e94c7d9738a3efac12caff43e0b247eed8", + "0xd1d95fcc7469ea5ce45321455e9462373d0ca563ecb4ee953aa78f0e2be47454", + "0xe571d9b84807e478eda6e5a296ba39819644f14912921d28484046f3c0ae28d8", + "0x2fed1a6a57ff8aaa1637f0abcc0c43d2be95053b5008f10bfbec6f3bafc37675", + "0xcf90722477b798b2c233367aba01352f118537de7ee3e7763a248f2a2de51f75", + "0x89a42fdd06cd7a7788ad821192d5454382061b517ea788e76b116cfde8cc01bc", + "0x30de38f8cf21934a888333e2ab3d6383475743c3483ae8caae900315411fb36f", + "0x6ca80d36a27e2836f7e569f7f55d0a1bf580a1dddc079936b016b3b6a1b0bce6", + "0x0a71a7e884bbce316a6bb93ac58c711dd75e57c88d892fec956a897d3fcabac8", + "0x7b6e3a51f187aa8b7b5dfa3250ea5b1601f05ddb3651583ac8d62d04a3f1aa71", + "0x8293a320e12def988a0baed2f56986558a9cda45eeadecfb4833cb72b47ba2b3", + "0x9e47c7def31b3ffa37bb275cf7a6862c3333cad0fe5e97f8ac5db5ad0b8f0150", + "0x25ce9d70d14c3ba00d1e78341dd815e02c653748a6179eaf0782e3da021508c0", + "0xdd6453a900fbfa108f4f9b25155c1625cd2a1635de6bcdd4297ad96966458364", + "0xf4a48452e6f1fa92cf17d920046181f69c0e2dff03e8155c169295a107770c35", + "0x3cc01228926a59353988a435395d10686b2d2c60f79f1b15e49771531ab284ce", + "0x589b1f7297cca3922df2c3ee37a03307e7d304ce00fd923404d08124042f1b96", + "0x8a23dc0a1624267b1487f7f29013d669f250a837263e3042eadd3b7cf6b6e0b6", + "0x1fd59e23fed6d21878c2438397a8bc9eaab44f02ab275785dba64bd59dde7c91", + "0x62c71da26887fd5abe66181667b51e0ef1964e84492e1fec9f452268d614b938", + "0x11323913be3ebcfca3e01337982b23b9ac7e6b9ead6593114b53e45085ed4e88", + "0xe0e99eee230d70b502636c6614eb7adf51c087bf3426759dcd3b2d2e797331b7", + "0xb34ef567a4886ec838d5d870344374b700d1fda731a1fb5ed4ed5e951800c08b", + "0x9beb5fbbea04003a5738fe89b2cd74920e65f355b66f2c8fac4f1d040b1b1859", + "0xbb5b0277eb0c19625612cc14e036d8c12c5dd85597e7e2ae590f519ddd68eae2", + "0x9821fc27d632a04a1da2b60747973caefee66c1720dc9376f8201630ef4b7b98", + "0xb16b15d4e7fdbfecfadb3758e1ead0ef45809c9bfff1bbc67fdb56ce2fde66e2", + "0x4647d75b4763c7db7da9f2c11cbeda068ad2d3231dccf46bfe03726d0bea1e07", + "0xe0b49805911dbef12e2f869e8ccb6c265f52740c10353f3345d6ec175ee82199", + "0x338c0ce6f4ab7b1e4de6e3c6f08aacfdb9bd9105b8b8b96a7f0ce00a1a8d6161", + "0xfe8e3162a6bcd5e3c19d2be35fc2de853145b38e4bd9e6f2e78e8ebdcb073481", + "0xd6e2dbac77810cf17d061eb545530d5dd9550afc49f6510146543aef3aeb5e2e", + "0x5ca53590c79ee93470ddc7226343427a616b9343bec00574659e9441cb04ab64", + "0xd2e88b4506d9be334b2c6963d274a8b90997072fc34fe15d989f3a5f332a69b9", + "0x83c3e9273b6f2e810b037c0ac27ff249abb2d01421e287aab47d8a27742788ec", + "0x9fed59f7b97cb96532e1d0953b87db686f1779a7d2675661b6de986fe2396045", + "0x5661291aa3ab778ed426326d6f63aceba206cf945dee9325faa8757d69b424d7", + "0x678c92ec0bd86f484002c5ed14c63e6553dbd903117e562437addf5c640625d7", + "0x24ba66dad76c9704cca78eb1d411a190cd9305fcb0ba67ab6631ac3d33660b43", + "0xa2f11d671afc35c8a363d4f9e98bae782ddd89c7767169b16f3bfc64e7f2c6c0", + "0x26f45db3fe523ac6bbfcf4336cb30bf35af81803632ad4589f19402721cb8d99", + "0x0748af49056570899c8f05358e08c8c30b9ab6c55054191915c8eb1a4dc21588", + "0x4d9bd71aed041c88915afc9b7ea276c56d2ea4e2e5532defe8edb1a13b734cca", + "0xdf8c4923b982b9319db03b170656f2728cecf403cb439392440efabb1651fb35", + "0xad77871d42da867ac164ed54917ec9e1fc9049c0c8ba049f3567dfef24e1bb15", + "0xc9a5ae113ac98e0816c7c18e7fc374403789b3021467012849aa04122caf66d7", + "0x2c842ba3875779f2f88bb7fec864642ea3b180ffb33da76ed763d8d39c8d4c93", + "0x996fee459f44e1f1f4a2d044c8c8d84a2284c558a8a4442f8e29189c337c4fef", + "0xa004d850dd46439695df35ba4a7e80475820b67f092ce92c252a171c953bdcf2", + "0xea996c6a170a2989b1d6ba1e7ba8adca1c14a42b7f06596640af1d2c4cced2e2", + "0x7d8646f54af529eb52256b1a788976c485466f8b3262e63a4046297eeb848909", + "0x0b643a17097f5ea17ff22e26fade3c0484ba55c2cc3037cd3d8cd35f3ea7a3b9", + "0x0c3546890c0dacf99df14351066d0816d7fc95251689d79b853fe0b7da8e5d5f", + "0x6266b724b0098b530b7a9f8d836097be6128c67cf4c974bc39de08ea5c2011d5", + "0x559501fb76382bac59dc5eed09b42c4e82e0f4384d581567f625c9e20b749298", + "0x89635c590bab610d4880a48054d33e1724382605c2afee6d942765af5dac57bf", + "0x2398f553a7910479556a98e797f47e4215ee018f7889f045f10929262b596f7b", + "0x67688fc4a16892b9841c7820a27a3b52a3375515b8bb9581e461aa3534060679", + "0x7b5ac07faa870f9f95fd5932b4bf84ab3646d898a01d5f1f6e0f21634b1fb56b", + "0x7cb525510ccb9e09df697b32b08d9cb2c6c51b321836d80b3aad582248d73e8e", + "0x0d81bc1c7e9509213270ca7e6fc628cc31daef97e9bf88f382740cb6a53e6e2a", + "0x4157c5d0d7891c551da8536215b61f11ef03d0400a86d5473659ec3d122631fb", + "0xd1576630352f1812bcac8eaa84d8eb01a44aa01ca25c94fae7d5e09dbf7530ed", + "0xba13b4af5ca162a928993bce16297b0e950af7f0a9aea0336b672da342e6a139", + "0xc602645df8cf483c9ed4a35eccd7e9a7b745e3b11a7124d89f1e2ff5af47cd1f", + "0x8e1904f37cf0d307874ecf19cb5d1e192b6ea29306894133424c5f3a0c828dca", + "0xbee92213cdb670e66809de07b79475232f77e9d315d424c9611b3f7a7b48ece3", + "0xc977b27fbdcb88b2be0c4e06ba368f44b8c3bf1fac9c3f28a978e00858ac914b", + "0xdc9a717f8bee3b706deac7db018a19f56c4ca6aeec5edb39b12821d7b64fa1dc", + "0xfdcc880071b632e4c32b68769199cad5511b02f852de7b5fc2ae7f0806ac9ebd", + "0x30bd33e40488ba9b1d869655d5c92bd0493debf24dacd453fbd8bf7941754dae", + "0xc9cf3ed871afa60df3a049f9165ff89c3fbc12f4841a9c42182c9095f924ffc1", + "0xdfe426cda3d94b457d31b800d1bf69814a54f5a3cf95bdd97dca5e9fb9f5bc37", + "0x55662fefcb3858c15aa4f746e3f47b017f77a6773ba42d65f2b0b8b310554c18", + "0x5b9bae2865e64dbba37101e23b6cdd0ea58e96eb103d657111ef18e6a149fe88", + "0x76f4746088b965b4cbaef86e7a803f099375d778e95d8d52b97cfc3292ec21d2", + "0x7af9fdebdb4faca8170c14c5ff9faaa8253862da48fde2e71a6353a66b9d2c3f", + "0xb3563f4bd7fe29ab25e2d86d0bdb5d541246f47f9c42c3a7bbdbb6b2beec9e4b", + "0x52f0b5a14c5a7ea275ffa9697ec3f1bb653e12c810c78a502c8dbf5a48fd4f40", + "0xf05ae73524d936b72277b74e0d236cc9a5fb34a27e037076179db1a430aaf763", + "0xfa731d45a0c6bd5fd0c689bf638822a31497acb4a35eacd03014400fe9016b03", + "0x2e9754ce74691a0f121ccb2c6a0be699ed3c2411540569385d380c5756700657", + "0x22959c7df3b990f5f3844cabf67c1a3f4ad313793e5a7cbe563671835787df48", + "0x0a4de2bff5c3961d491e143bd158da1183171342ee586fbb10c4952bc38ec551", + "0xae35620d48bab7749909113263ce3aeeda058b679cdb14a9a3d3d1eecc3d1f08", + "0x2ef6064d3935ac0d3ff53e2713b6585d183c5d27854865e0d28ddbce8f2355c0", + "0x27746f57a4866af5973ff254b9873ed2a8f87e6b6608ca2a35318136e09ace31", + "0x05ece86abad0ca52f25a6f2a552f11f95f74b2dc87cbe136de0d4bf804d33a4c", + "0x45a40fd670bb2462a160fa591a12a6508ff3e6f42e101fcefde27928fa8f0897", + "0x240923b7eece49fc823c286b58428765728b1e7946c695e88d15a6d52b296cf0", + "0x123b2169a7d7a2481b70625a4e1a5c375226ede70b02fedac96dcab8061390e6", + "0x82cea4ff0299a0fb73285d1c9d7b7db8cc9e8de7e010bd73da1c46eeb1f8baed", + "0xcd6081d2deb04ce1b4969c11a6370593fe33426c1ebadbcfd0262107cfc02629", + "0x76eb0c3078df0e0ce4fd2c5bc54af71facbe325a0d02a04ecfee70082f3558eb", + "0xbc326aa1c39061a0b2c4ac0162f2ccebb6eeb110af91b27a6b8b2e5bcb792075", + "0x08bc4dc56a18402749e1fd12a1e929ae2819fb8473b816451fde980af3ee3dff", + "0x7ece647113d0b2af1ce31361d8fee372a24b15517c60c4f8d670848cdd2d1c9b", + "0x2ac7d987a3e884f7b7dd1e197c14a975a66b0aa9058830aa782b0df7277c67bf", + "0x10f63ae48b004ed3e2da7a01cb1ea910d6b35422476909360814e7fcfc9a6625", + "0x970cd47193966f97826e15e5df2697bb60c551b1a7e0a43a98fc96eab8c2cfad", + "0xa988a325115386b5f929f9c7919e46c68306a71e502359b7e0ddaba34c3e40d6", + "0xefb7c03c8b486512bcad0810152059001bd179af354bda9b1f1390ca28a5490d", + "0xed1b4545486466e3dcf818aebe9c52e6f529dd3746cbfb494ec5109eaf53d0bb", + "0x2af5f45d81e11bc88adb6ab479d4f2dbeb54b125f20d8df78ff5e11e1e33dd95", + "0x624e8baee39394f7fb7a86b06d0e7cd6732fb4bd0e3a368f3023b630d2518714", + "0x2306c03b3af2bbcab0e90955ba2c29dc437095e58b579a9ff185fa5458ae7f74", + "0x5f0879e245c2b4d2b670b9b3f008e59862e88b12e5397ec479106a0d428ee6fe", + "0x2c58390f9fd157ceda550e72e34a88e76906f006642cc73b3ce904179be27b27", + "0xb3f940df993fae5723eda63d6a01e2e0997e53570f8df6f8b821502197deedc3", + "0x8c19f5b26e20397017da84c10c3034ae1722a45883c626cf79d6cf5a5f5d6a63", + "0xa1df8843e58a7559383be8aa5a84b3febeba17af6b13bf9bc188d7045024f172", + "0x79db28fc2e051883058120f8c03a5dc670d07df9097051929ce7095328fb6be5", + "0x7458ceb2d3c7940e38e2f53170114a7e58e6cc32c4eaa8a12a788420b0c33258", + "0xbfba5678100a1367491190a6e0024940dd3b64ba7b6971174f8a69704a65837d", + "0x31a0db2a7284495f824aa85c45156db51a557bebf00eb94a834c9f81374ca617", + "0x6831d8e349d910550165f70642765c927f9ae5f2a40bf6ecf342e22473ad1b32", + "0xf0fff013f62206dc88a87a20df36e4105f0bb97c83a042fae747e640961f746c", + "0xad5ef4119db9e5a16f380352c46fc8e34b6f7608af811eb4e61ce9e3e07d08a8", + "0x27f7ab9ff1f15a1dda7dd202223118c1e38b3490d6a1097f292c86bde3d73b3a", + "0xf21d473c5c2a077e1b25d05c73d9b644510d55548f4a5efa78c7bcb299fc91e7", + "0xe7fa07ddd0767d3b1cb401ba83f8216b18546c0038ab5572552e2c34e628c182", + "0xa34ba0e9908b1ecc77db59a0ebc9972601d79c8cb94a5811c1d584a85e71508a", + "0xbd1bc04e297abf34098127e41a3eac211590b2ecd970694367541161892c5843", + "0x781a7f99bc4fa5c6c2d235c0309437b458d13d23159d4338322807b94892e8b5", + "0x4e0e4b567056daed98cc99029c720a13346f238eb372e4b88d0b5a10a90cc3e0", + "0xdceb3d0d48a2e8725bace7937e05354fa4618f87ed72afa452e2d465fef6a5d1", + "0x900feb9149505df11af737ee26e293b036c417952d17d22e372f0bbcb3af8f98", + "0x6d0dac82e06e85ad2ac3c5424162194c1517be56bcffd1ed351d19256661fc5b", + "0xa5b93fdac4a9178295d3c46c2d144d6dc2c86a23c295d451d4a61a18c294992b", + "0x3eaa61e4a2ac33bd0ed6aa714a95c49c708f69cab38c170cebdc6f8dd83e598b", + "0xa5e3f94e662381c0dcac9480cfceb7534b806d12f2391c620b30cf2e0850e51c", + "0x338d143143c19cef272105c3e29cbe6807e4868e5d2886626c0b549e58da9027", + "0xb061d2fbaacb97ae81d0bbbfa7223b1c24c0a62381407c4b1b339b477ccd35c5", + "0xce6d39847bd0379e97bf494d8dffea65446db3fa1e85fda91d6b4fb2ef5504ae", + "0x0c26f88875b11ae66b8465be3c0dbac1798f5458836726e936333fd885da5341", + "0xd65d863b945c6c8ead545683bda51d8e7fa9370396fe2da2d0fcfb6982e2c9d8", + "0x5fe3c3b54180c48be04405bec50b46d478ce2453833dd158c43a033083f31038", + "0x6391b0f557d8a9912cb3e298d87b1ef8423048b4fdd1eb0cebed3bb9011a1236", + "0x9d311cbefe9dc61a0ecc97867adc68c5b6c7b78eee4723a4db2b9c61ed2e39a1", + "0x994f0da90beefec07e410a0775e74bb305d47e3389fa174d53b0dca222ec881d", + "0xe7a13eb40895e14a211a62c856796d3bcb78b5ea97963b693e1153774077561a", + "0xfe38a2e3755f98e916dff26f3dfc56b6b85c2838790655a6b42add53db462cb3", + "0x365d58db97ed7d86033cfec37d6dbb25627dcb5de24c88353b83f2b9c25733d2", + "0xf3a2fb6987bcc9d314ce504953d06e1a4bdc3b01815e4f8112ecba3cb3a7955b", + "0x576c0b8e0e9e28387cc50ecbd0f2afb41953d6b90f29b306304becaf672a8aff", + "0x3574f29b855c9eb7b28bf77519027026b19560ffd46b5a3c0e87574f45b32ef7", + "0x6501dd33482a55e633b9d8d4ec195c20fdadb72ca303000af98d3e27872bc41d", + "0x59d231864db252a3007fbad184cf86011b6734bf3dbd4c71e10f00caa385d878", + "0x3182c1fd115cf57cb2633c9bc57bf73ab75a8898130e6c8ab89f47ff0847384e", + "0x19e0a5f4787732aba3bd1166a10db184b0e87607fef94e65c0956db904f732df", + "0xbb04a7e5f3b1798b1db2fd42b6d08ff0e219205d6615d0cb8b8ef5a15b998d6a", + "0x73dfdf4ad8c457f7ce3b4403fb37f38d3b25f2df5f9dbd5482644a3087a15a21", + "0x8545f517ecbb380f1c13b8a54de385cf67a250272262a686a8afb8d0d59af25c", + "0x30248f21cc9298282d820be58bfc8d43acdc8c9a613295eec6792bc0d90e15af", + "0x50068322193e3ca493f8d029e910c2762259eca4d0847d3fd883400d0c2b2c1d", + "0x1f88aac53bd0ee810148aa99ac6c55b637ebb1c78251260a07ffbc96be5cf027", + "0xa0d90e62d997cb6c7b4fae9668f3e12761bce4ae025e34c1c1c2e0bfc9abe2d3", + "0x8b0ba64a61acadea9c414592af5f9b940540116169a021b57cf744ac7c5cdea7", + "0x867d7f1448020c2007cf681d95fc4d0dd020ef09b4164291aa74b04cded3ae5a", + "0xb2fa005a2f4c284e0038e7266c78e43daf192effd3e11669b12ed35d2bcfc59c", + "0xfd87abf7739de399aca533541f8a18f0356a3ca226ae52b8f12ad020cf8eb1f3", + "0xdbb3591d48ef0507433b7594138f4a829dc633e893cf8e3f4b42096972ceb13c", + "0xa0aad0e05d1b6ca13c90c22c70e32be5acc9daa14e7a0fdcebd685ef658340f2", + "0xd9fe8c2bde8b1233c83daf36e3be5db295ae7db175e28020502e1d09d539ea76", + "0x01cec51cb45b53dc98b8d15449b3dcc361c705fdb76b08dc35e786d32e7755a3", + "0x3e12f4c0d8736617c409e4334c85dd4baf5e5ed46b6bca6ccf52809e72fd7d16", + "0xe4ccb1a150661a0ce1ef4ebc67493e21f208a9f6038729f9be23bfa51ad48052", + "0xfa7bcf55a36e0af1f8af323978f35370c2ae0f4f13456d0ee84bff0e460d92d1", + "0x2edaa5b20f6140a232cfc1c10efc9fd845a99fd543f300b2963f62130420f4d3", + "0x99bb2b584b5fcdd27fcf11a262d6f2c62662906073285e0de13ede00f32b44d1", + "0xd278b9d584cb86f0e2c00a546504aac5c5994bf66ee7be140206d88df20227ca", + "0x7c1039751da894f5cc2edc2ac7cf92cada1c5ce1988179806eee438a58e82ef2", + "0x4aca21ee32e2df9b0f91a8b26fd1fa13ee970e053b37e2bf88c4364bba1b8a82", + "0x0a79ccd93d2703a67d50c567efdbf7175ba4d140cb61e0c769205aa21e45e650", + "0x126ff960a0dfbe2be3e6f2151eba7249806686d7d93a1c9535baf661a9e41950", + "0x9aa91584a34fe50da94d7895f21925dd7c9efc18ee095f9c40c00c74fec10214", + "0x73a9dd2ee25e0245dd25a4d1e8d81ea24fe11b35283efb1d7d97cd44c38af612", + "0x8d9c5ba9c021a99774c48b6ce426d5df7c59945d564913f033d2d0d8d346ca7e", + "0x96e86aee0a5d0df56c341a287556fa6862c9870b795801e38410133ac852a9b2", + "0xfb8db9287ad9ca0839baf2532216bff9548b5036198405f4ce2f9101b776805b", + "0x9ecb01a862cdaf0f5d818b0cef9ff0dd925e2d26ac409c4708d3dd7e4f3ae2ff", + "0x8905937d8d89edbeabc463fdc3b81206af04657f6d9d599fdc4c493181db7902", + "0xfd79081388e7439ccbaa75d952eb2ded98e4977592ed0364a522722bc348bc8e", + "0xc450d62510b007ad41015df0f9e1ba6eaa315b6ccb9c7f6fdf34423f86d447ba", + "0xdc51e5a1ff3267d7334218869412a351f6f551c689050a5147f9b0dd2d925f4a", + "0x728275eb046a65bc4828ec6ea9853577d16c669f9198a6003aeeb80750e38f10", + "0x36f2582496db84ac3727e6ff4edaf3fead33f631bea94fc0e395c9422516e11e", + "0xb93e9fa24689003fc3730a94cbe9a5478226d80e1223db11dacc5a7fe8ef421c", + "0xf9837d28019c383164ee2b46526ad1970e15b9dadb727f66a778ef36f7333522", + "0x723fb00bd11cff8d389bff0d083080a3bd57872e174dec81fb8f36acfd8efef3", + "0x1e4f30e75ac6a97c842ce142991d1d284b87de7507b27bfc7a46ac858baff2d2", + "0xc0c31bffc4503a4d73975240a243523cff2d8796172514a9081e701ac21276b5", + "0x4ac83922a14376f5ba17f729bfb038b7032f8fa5be9c0dfd88502bc50f51974c", + "0x12f7a2bdb2bd24bbc658bbd79af4967ce58cf67d3dc2ef23ae9e51c628efb17d", + "0xc0098fbac0fe84e171d4cd2acd3f593f7c458ff971a934e11f6936ac0b6a6151", + "0xe50ea99c473e5153879ad18007f319cd3f65bf051e84a25b073cf3562f908cd3", + "0x9cce5cbb6cc6b4fd1016b6e0c5cb22cba2e6dc72fbf99272c43c7bfa923de073", + "0xe3ab7ae18cc75a6e90ad16d3a168905fa42ef128b75b08f95083b34dd75e0de0", + "0xd1878bf39c12b1d89b97cfd68d066efd3b40a663519da92893bfa1d6e60d2433", + "0x38f1399bb388f5e2afa3346d55ebe0abaea10a06a3b159667dcb2fca84cbc546", + "0x1fba211ebbec9febeb77e2b72ef41f9dc1e8db34c2471f7ffe5216fb02cbabd7", + "0x83240314304aa3913d5f080d06bc6e6cf9345093f5e5774d2c8d273720f0e699", + "0xd3b710bff03883fb9583104c3b817c9c02efb6fc5ac8f2f40c293df2ffd780e2", + "0x767bdc82b188fa43300e10b1ac92402f61d8fa9592538277b79569d76f809471", + "0xa2f2e4e09fc2cd71c704cdd35b0f3263e9ba908ebc88adedf7720e008fa05e0d", + "0x765f32e9da6278fd1de8666609d7cc91842e58a6a7a2365469bc0d144d2a1baa", + "0x5ba493275d638660cd1bf0b389b156b145a6e1b51cde064e198e35ee04845999", + "0x3fb87168a30cb7cdad27a685da3c370916afbb4fe39808822a337a5248b30190", + "0x4967975e4c0c23a2afd931752e7ad4f1ae14ca4eb03a0bc7115ece6fae30480e", + "0x790a5297d710b52d2a62ac8ef954fc0b64fcec0dd52c62d6dfcd5db3c729d047", + "0x79184a1e24b2f57c03d7cdcf7c0cdfa8a03689c4375106f58fef072beed768e7", + "0x0b837353d2e68dc1f1bc0d220cc78be359800f8a5fb437599f825503b0d18a20", + "0xdce75870e264046034246f11a7f620e907f18fb3543683efaf486d983a683a76", + "0xaabcf65115256a84f91345723be1baa241a053b1aa0c00384293331ddd71b433", + "0x8af843ec6ca6c3066c0736cb001b49e15a7be2cc61f860d19d97b7d2f96a7f99", + "0xed9c6001f5c91ca10f1624a9e6e0e89eb105a624660786ae4b33ff96acce0f76", + "0x842ae153a202daab5b0ee10482c73e597979ca435debe2f14879c8aab9ba928b", + "0xa00f65c8e6b3a387007b5a96d39ef05eaddcbd9bd60ef1766370e944873a31b1", + "0x0ae40b7adeff0798c4ad3696a077e3fdc9652ab97c305de491a35fa100745bb4", + "0xc07ff6a9f08e0e8b3d9249e65e571395e8bfa5264e7f94f478d5008131707860", + "0x64717a7140d99db12c9d7113cd10c85a04a51ff26ae3c1e8441175e1850c3b1b", + "0x9e656739282a7c465170057a71933ce0d67852f81173011b393f7fcf922a37c9", + "0xa0b232d1f4515b57c6e5cee2eb05ccd39ac8dfaa32513aec97995bc31a469f9d", + "0x68445ea5dbb2a869146e76884e900ad006dc63a097f6e8702e648f3fd5e2fa36", + "0x976b8886181e285e12aecffc472b5c333e9305ce611278f6221376c73d55d009", + "0x40dac6c793b988bc54cf3b1622a7c47d541661f502611de0380d42cb39db4773", + "0xee9b3735db43727be4fd7d1ef92bde90ee731b2d06fce87b1e51827e7b1d0bd9", + "0xe80e56514e46fe5197208f669d8640840ad999f36b69e3f48c538dc3850595a2", + "0x2517321c604e8b82762b75c468c70072a5780c7b23c6d3c049e3a36ea2479315", + "0xef26d341cfc6b1f8d4311885c6b8d78668978629515baf4e3061bf2d0ffcde52", + "0x6813832a83c1c50bc6cffe8c7c95d9fb4fda376e1a8b660cfe0812c7d0d38bcd", + "0xcbeec9aa5041c6625a65fd2d44c923ddd4e3aa2c87a57e04a35e794225597489", + "0xdbf3aa7ebc4ed3d25f8809006131e62a76d637ff86bab149d65513160d0f9ef3", + "0x24c576e9d43a9140fb344f49262f6b3897b9509c77d125cc96623470d4b4dcda", + "0x2020dde6e79a6fb4fd314f9749ac9e27ba6acd75f625c8605ac74c9cac8d8575", + "0xfb32362024cf558938cbbceb46cca45c7220579c8fe6220ce0f3d9266fcfcd48", + "0x06b1d9298c5e85100038a0c3eaaee32a7b06b98447946366f7f92ae068ffd4e2", + "0x0f8c907630afa1b067f9e466575f7c77144b8d0a392284261832b510b762f346", + "0xf948c6631f8bb6466f6a5bbcc15980a40f3cba99bd0267d83cd492aa1707d91c", + "0xdd0dc32ec39f23b512fc3bd21531aa2e439b90f5f0cdbc7da20a6435eb06ef09", + "0x69af938984fc3cf41887ee685d966c5bbd2e52c40a1721d7dfca4edd387a7c75", + "0x9bec0356a2cad99ccfaec5b3ed4a6e5b0b69f878480d3a230fd40bc2e5ab84f2", + "0xc3784342dc60add24c1ac02fbb236f87585caf337b4953d0e151487c6f929e81", + "0xeff9a5d39305c64d5ab88182a8933483f1a4ca994bb50d8ac7059d62361e53fd", + "0x4d63fdcb4cfe8efee854e06676f595df6bea08c22d5b0946911bb3c28128301c", + "0x56d5c2d0d70ee36844bbcd6267b5e6d4e8bda112defc21d28da75675c19c32d4", + "0x6b8ed919e1063dfb2e77e758c537d6041a0b9e54d8a6fc55a019940f45205b08", + "0xb7ae373cfe4b3fdc46c090feb08fba901836a4d417f96c8459c8a831e69082ff", + "0xd346ca16a324af9799a753cdf51c8c801bf8de413426edd4bb756cd5155a5ec9", + "0x573d974a038fe12220ebb25f0457c15e9f246aab97dc09d98476bc82f76ff1e7", + "0x5b74f9ab5ef3540d4ef19479d15553a266210665ad05c821baa1c3a7b99f9789", + "0x6760dc4c7080996fb6959456e869a975c44f49f613385896307ac8841dc18e8d", + "0x42ac4ada0213289d35de7b01d3e1cc2152a152c01b920759012bcf5c4ad345fa", + "0x74c897ae54ce41010302f8c83255e96968f814b8da575985c0a83f3a918df15f", + "0x406bce7f6e9a2676f8d236bd445cf553f3c9e64116243d5c371af077e974d629", + "0x21505e4ba8983976b639a7e5690640f49da24a2b67dd7e7e2b4639bf4a1d3b75", + "0x00243fbc73db1e612b6108ee5ac4c5cb431687747af625c23f5358a3d799391d", + "0xde29adba211ab9c5e6d0967089d1b059ea9b53c03a7cbc13a183c23d1477c606", + "0xd3eae3c0784034c1752d91dab05095eac1346801df0d4284e7fd2b82228e462e", + "0xce3a03a8b03f266af8bad2eb2f186f6148d905ca62998810a12ca89d4f5f847b", + "0xa623eae3522f4a0457b3cda448f2e0bd9206b5b074460c0a026ff7b7457c7a32", + "0x97a23e575290e5aad2d78b93e84f671eaaa7ee5f65473be10bc88fa9156d38df", + "0xc662cd7da84969ab249ed4d440ac04498e6d74bb3de150a10f7b6e42f8b322ec", + "0x9213be2e41d8412c1f712fc5581f20aa776f624510fba1cfaaad795ba5507d25", + "0x0203b458e096ca9861a7bf536936a4d117557294b0eaa5821cd18235f079bedc", + "0xe8367c79b526eccb2a96293efe4b0e08e62cc05e82a55be8393d2a32a12837ae", + "0x1b0403f3e8b0bdc9780dcd633f733367d800873b7a27e30011efeb2edacd89f0", + "0xaadd56b22b88fefc64886e0300911160475acdc7d92e3ac6cf09a4672268b11a", + "0xf06f9091163b92d1a68e6c239fb74e1fa11d540c2c339d0cfcf725b43280f42f", + "0xf3cd1601bee6ac6a8310bba282596b6a1bcb57c0c72d9a09deb9a81be0336695", + "0xaf16310d9bf252732ae7aca5ac8c10542de5b4dc49b2041a213f7ca97d7dca0a", + "0x2fd6b1aae42b290311db9e75f9be5e9276feee8d2a8f9eca4fb6b2e75ae66a4e", + "0xdd7b5c8452e4a21bb8cac5e436c1e6aff78e928374bb4e8e9e4a19673fc56510", + "0xd1da323d3d2b19249c6fa7559f867df90e684489b25ed53ee8410b347024bcf3", + "0xade3a153e8d2ba1b59e0b96f52c4bbe5b291a571315b128712bede86449085c0", + "0xaed52cf75af4ffbfccc83a9a854b347af941611ae423337cb9f1ef898719f927", + "0x0f019d66b76b0d62dae7eaeaa5f869f7e8a6f2476d6445f0f7040872168fa561", + "0x8d3f6df33513f72d518e4c93b5622356052770ae3202fd2f7adad8a4d7b7c030", + "0x7f2f69c829e6a1992deb6a86fffd0bf3eff676a9c02cd9e6939809abedca3243", + "0xbbb9ad5ab2524627217d432a86eef451c4701948c753688c5fed6626c939b675", + "0x9d0f1f643bf590c15e387c26b1fc4494c3771c50fdf3b7987bb783addfec79ca", + "0xb845d8249fa4e0832921cbf66198b8e164a7bdd23f10d0e2cab2e23c885d6732", + "0x559f856decad188f75bba00c2d24a5210aabda903627f9a08e97cc57259dc7e8", + "0x4f8d21ea4029155c62b25a902388f16eb3ff8e319f406e077c684b198aab790d", + "0x319d311e01c004fbaf6c0e3eafb3e948582e8b5e0c5b909629fb53b1b5ac4163", + "0x7f1de4ab81a1f20c6288625363bfacc6194852d93676d159a65f450d75d3536a", + "0x13ddb8876e97d40684b2e350bb96e90135f2f76fbbd213cc606b4629f5f54a62", + "0x7314e13bf5586be0a2dc757bcd1cc64c4c1348668b84aab5c533b0394fb4bd6d", + "0x08515acd1338af7816f8ac11a267eb125b7278a009d8381213ab2cc0edc55eaf", + "0x07daf52e598d2ba7a5592a0955d4069f433162391e97cffbb641d016620e6646", + "0x9f1eb2e6afa6830f05c8cad7cecd956ffcbabc306d2631ea67ac82bc33dd7609", + "0x47fee8c429340314673bac10521f570b941c9fdcd3335b1105578b3ea9ca37f5", + "0xd41f88797cbede2da3eb2058591d04de13a012dcdf42dcc584c941c92e09d8ac", + "0x492eaf4ab970de91213fbb41c4be70dd2fa63a94617789fe174d3f198ad85892", + "0xa984f2ccda64be341a4aed0e45c8caab9d4f0c2c14453c2abaca8db38e86d302", + "0x89eaa5d7b2e4f25a755113856f8a433facdea3591b54e86081d72ded3cbdb47e", + "0xfb91434250b63c3d0562aa2cfd540a74a5823f30e1c9fd633957e3c298fdaa3e", + "0x9fc4e3a75b8c8028ea4ae0a5ed1882be290910c0334c388ab83c0be59cccc2bc", + "0x38ac7a20d96067449e58118a4784078734af8b28414c26510f10a2e15da6a7d9", + "0xef949424603d7fd9cefd4cd0434e8461296874f482150202f44e6260f12d98e9", + "0x8e196df72a06f804637e3b05fbe9391179e9f3ab3d0c8d739a642005d60f443a", + "0x2d80a70e5c18d28d90eeac8a5f31cc1a4fb261ded7dad8d238844ab9166e276c", + "0xbbe3c53e8d60b641eb0faf57c8d11fee96e6ebfbc91e414e390fa61bd643d077", + "0x06a05029bf624b74a096d507f41d12672901bb506841bb5c8e49f31c02db0480", + "0xdf3b45c5093d1873dbc9ee35ec6aac8d6aa2608edd8451c13a379e46a7bc8712", + "0xaab6759129db6ed011e9bb41c44c38f621361de0afca323088c2cda3a79ba5cb", + "0xfe5df948ecc96a6feac4af57a5cccc964b5b5ef1adcf4c868feebd2d24997456", + "0x3f9b5e22c7f9e306e72bad32a50cfa0ef772c808ecf18699a451d6e35196ecba", + "0x23f4b12e5936d4e423d8b824bd07418451daa9cfe978b3bb586b54ce2ea28235", + "0xf9e1f36af12c0ffb5d7b5f626326ba7a466d310bb4bc6daa19dce8c87b9e73b6", + "0xfa5c172dee9e3b2352541e3d4a1f08b8006f5a6d3b7fea134a1a1647e09deb06", + "0xc26ef40aa49b66f9fc421ca4eabb40b64488967a2f08a06466fc673a5b5148b0", + "0xb324831424669597ec1a070bfa3c9874ab02a64e981c936a261ee165fd3821a3", + "0xbf9c21cb75ac4cdb10c4e059e35f32c9db55bd9d054299d2dd60d862fa372db3", + "0x10d4aec58545f0e7fbc476df724b654f2c81b13db8dac544a6cdd07219917b62", + "0xb0b68684f8af1f5d58146f87c3d04b1ac7fdca50f85a5d3c3407c661a30a9786", + "0xa220eead08f0c93b172e41f8fcf4819443945087210ce01ae4abf1bee5d37294", + "0xb45b216f1691bf9e63a972da45142a8a2c89058d9e4538b424825f97dc578921", + "0xe119c0b318bf681c617e6b15f62355570b3c6fef17102f27c97aea51f6948c68", + "0xe787c8331da0b1e1dba6b43cd57b42703f143875741cb63dcc3e05430c204cef", + "0xc306a02a5d62f46722c5c9c4383baa6dc3511cbbe20ce7fe81c7415165e50fbe", + "0x2ed8845573fe0b77cd189f984cbf5f0258f6d3ba41ee9cd1bb0ba4000f8bcf88", + "0x87bba26fb4078653672b2b3da0ccb6bea6a9b7360e465f58406861138308c941", + "0xc31eb52682b46efaa7798549a4f096f06470cca8943f5ab622856ae06980f116", + "0x160d41c7f4f58c6ffd9188b355bbeab5a65b017f98be0ba87dd8259806968353", + "0x0b03f014e86ac6e70411f468df40e304e7bca18a67f76751dd69c2a9780f0107", + "0xe21f8e5c29adc56e559f6a060891e5e3eb6d9608733f56f8e553ea5bd403f233", + "0xcb397b9848d2cef5040b99c123ac31c075b8a2b6db81d8a4cc3b51d865010b09", + "0xb0d352fa63c2a0c8c35f475f5f8937b31c7696ec1333ec50e10aa08c8a658814", + "0x11358aa5d5d04e378bbdb49a02a45f26d2ea8879e9c1204776b77c4bf0f63838", + "0x6d1bfcab735e429080a848d332ec6d1dfaced797dab52a63b4f13ef6ff26fca6", + "0xd584f23cce095e5df0fcfc8ab703af23080e74a6527a7186a27e3045a69e780d", + "0xd4f6ad82ca87cf774868083aac566d5b58241688e62087402c72214985440943", + "0x878aad2cc3c6fdc254d6e093abd5d56d27c165119bcc8fb0b08481a5bd77d091", + "0xeefa903a16f0fb849a09b3ec7172de9a4306dd854b4318d2f74640a466ad8231", + "0xe8ca51d3159e700824e81b4f160d07e0a654b18ffb1b791ced09ba285c385aae", + "0x54e7010313777cc30a72f4a55599903403f7289280dc440118022bb4c061bd61", + "0x7db3079d43c20b8f32b9ed145d169b9bf522c4261e03172d44a04304cf09515d", + "0x29d67380376cc9b676457813ccff9858dd6d91777d64076cd2d681adade3e9a8", + "0x1ee878323479333d3798c2d6dbe919d44a613a2f3c619a6082ebf3be312ac3e9", + "0x7265d9d307c2523738ec6aaa6b95389a31c872a1f805e917421c063c3893ddc9", + "0x0dc5a1f77e0a6c590f7e47978e4f7516036d6c0155a2443663a6712ab9a94470", + "0x1aa2fb41e474bb6c1df60057c4a76e8ab624b6d0880e183d8a7d4d6aa2919a70", + "0xfb979a6b950b06a5e8eac3e6c8fd9503da1cc684ffc1e9be578a7524a94382e0", + "0xea12d6992f171c25765cd0342cbb8e29115cf95eef409cbb6b589946e40c8558", + "0x4b4c1880a8eaefae64164755b6d12f516b373db78235eb3112040509e9457633", + "0x3733b7d09d7905af183a08ec531b315ff57823619c8f17e709af48ce56aeca5f", + "0xde40698fd1b6e96309941219752b6c0629699fbc40348b6ecbe7d2412d2f120b", + "0x9d4ab6dba047cce063cbea340270d19159fb8b2b3ab7ff492bb060073c2bc38c", + "0x4381a3e42848b2df5b9359292c098d452dc3b81ad57a88a1351ec341e617b320", + "0x07e68f05bc5a05dd984dda93f04e3b76e2b8951915d49c5a583012574395bb9c", + "0x8b6d1704834e16bf4890644ee83c9819c593816a3346bb374415f23356937b84", + "0x603dd847adab6d43b842acd87a36886c5168224084390f5555aa27b411fd6ba5", + "0xea3a60a675ac3259954f2280948da8f79ba030b1ac2086ca54cc7a1b1ae05062", + "0x88adcec5060ac22fc96371e32fab5a6cc59bd0f45fe46d13c3a96c05e4e9f4f8", + "0x0752afc59cb66a0752aec51141d976ad06584b7e6775f1bbdcc9a6b29e2e2887", + "0x99a3ccdae726916f2fbad82c35f82f10b0dc7769b322d86865ca05a939e0b20b", + "0x563eeb9579dd28e4f64f7cb0b7b71981a5cb5763878bfb9d975764365f05b8a2", + "0x4e0847567d6a02037f0a7a9ff459e538710098b1224e4524805c633163c8580e", + "0xcb5f09bc055529ef2df1a64041daaa03204b18ddc719b30cbf5113ca45aa0c70", + "0x0c48d36c020fc8a6e15642e211f35d8138ef1480fbf88e9b786106f80e85821d", + "0xa2e54d1d62c584923c2d46ad6d00e13d16b4e59e2e8b924eb5bfb34e7f0d7c9f", + "0x24fa02cab5a8f0006273f68d9fae33fdd40d51c589965dbdf1c65f44cff49ca5", + "0x978393497a183ca8cd4debdf5c175d3373a444b75c9cffb20da0112aecc9c1f8", + "0xe3030f3b1ca2c2252428068d545fd6b67bde303f7ac40c5f30c7d51edd5e9407", + "0x5742297ac85a1ee04cdc04c02db6f774e677de03d21f45c22d3cb42d5cf9e29e", + "0x2cb1408296a11acc717df61a00d1c038fb3e9307d7b40654c66bef36e08550d3", + "0x248b47946bf59b8cf945bae48e608c275eaa1a550c569b1a1686a9922a8ca304", + "0x00ecceaa1d76888f2a1ff5d99df34296da46075b27c2790ceb04db3e6496f66e", + "0xdb85ebd8a14c9298e63c9e2f1d018908f58b633c62f69b07de6bbc34c117ff9c", + "0x13f0f6537d6eeac69815ce376730bf0743a6da615c93710323b175cbdc52b59d", + "0x292f4aad9f447710d8dc0435cb3d80efab8c6475423ff0291b842dbb419cb568", + "0x79eb10e98e917b3dd9f8997d0318e3879c123abf2d74ee8c1eb839ed528607fd", + "0x9952c9e3dc668bcc12ca8b4f6a9b232aca5630a5ea42eab25b1319d8b0eddf4f", + "0x5a4213765fb879e596b9e7ecb4c1c211f745f4e135e211e9a15283d8fc5da563", + "0x230fb65ac08600ef6f32a483c5b814981b800d11e77e4b6e33fce532732d7fbe", + "0xfc1a3ac838697c59c8ca2cfdd38cbe8b92ee0e8839333f3160f6234e772af769", + "0x35fb053564bcc8f740ef2f8f38d31d047b2c951249f25aaf3f8e07c44fbfa963", + "0x336743048c803609ae2881e8c48cc878885bf794b2e914719481a8eee0cd3098", + "0xe3c8c7f9d2c980ba086907199de1a73072ac6e3ef3284232238c5cf7acf5464d", + "0x54f04b874ed60e2c1fe5fd65aca94bcc81b6831436f2f9120568b69460a9471a", + "0x687f417fcda3809af70a2e977a1def9c1407cce392c43dbcbd0e0dd891be6d17" + ] +} diff --git a/shared/types/eth2/testdata/block_11544444.ssz b/shared/types/eth2/testdata/block_11544444.ssz new file mode 100644 index 000000000..725b77314 Binary files /dev/null and b/shared/types/eth2/testdata/block_11544444.ssz differ diff --git a/shared/types/eth2/testdata/hoodi_genesis.ssz.gz b/shared/types/eth2/testdata/hoodi_genesis.ssz.gz new file mode 100644 index 000000000..6b7c65f31 Binary files /dev/null and b/shared/types/eth2/testdata/hoodi_genesis.ssz.gz differ diff --git a/shared/types/eth2/types.go b/shared/types/eth2/types.go index 00a2b5e23..36f83d31a 100644 --- a/shared/types/eth2/types.go +++ b/shared/types/eth2/types.go @@ -1,35 +1,80 @@ package eth2 -// Deposit data (with no signature field) -type DepositDataNoSignature struct { - PublicKey []byte `json:"pubkey" ssz-size:"48"` - WithdrawalCredentials []byte `json:"withdrawal_credentials" ssz-size:"32"` - Amount uint64 `json:"amount"` -} +import ( + "fmt" + "strings" + + "github.com/rocket-pool/smartnode/shared/types/eth2/fork/deneb" + "github.com/rocket-pool/smartnode/shared/types/eth2/fork/electra" + "github.com/rocket-pool/smartnode/shared/types/eth2/generic" +) + +// State type assertions +var _ BeaconState = &deneb.BeaconState{} +var _ BeaconState = &electra.BeaconState{} -// Deposit data (including signature) -type DepositData struct { - PublicKey []byte `json:"pubkey" ssz-size:"48"` - WithdrawalCredentials []byte `json:"withdrawal_credentials" ssz-size:"32"` - Amount uint64 `json:"amount"` - Signature []byte `json:"signature" ssz-size:"96"` +// Block type assertions +var _ BeaconBlock = &deneb.BeaconBlock{} +var _ BeaconBlock = &electra.BeaconBlock{} + +type BeaconState interface { + GetSlot() uint64 + ValidatorWithdrawableEpochProof(index uint64) ([][]byte, error) + ValidatorCredentialsProof(index uint64) ([][]byte, error) + HistoricalSummaryProof(slot uint64) ([][]byte, error) + HistoricalSummaryBlockRootProof(slot int) ([][]byte, error) + BlockRootProof(slot uint64) ([][]byte, error) + GetValidators() []*generic.Validator } -// BLS signing root with domain -type SigningRoot struct { - ObjectRoot []byte `json:"object_root" ssz-size:"32"` - Domain []byte `json:"domain" ssz-size:"32"` +type BeaconBlock interface { + ProveWithdrawal(indexInWithdrawalsArray uint64) ([][]byte, error) + HasExecutionPayload() bool + Withdrawals() []*generic.Withdrawal } -// Voluntary exit transaction -type VoluntaryExit struct { - Epoch uint64 `json:"epoch"` - ValidatorIndex uint64 `json:"validator_index"` +func NewBeaconState(data []byte, fork string) (BeaconState, error) { + fork = strings.ToLower(fork) + + switch fork { + case "deneb": + out := &deneb.BeaconState{} + err := out.UnmarshalSSZ(data) + if err != nil { + return nil, err + } + return out, nil + case "electra": + out := &electra.BeaconState{} + err := out.UnmarshalSSZ(data) + if err != nil { + return nil, err + } + return out, nil + default: + return nil, fmt.Errorf("unsupported fork: %s", fork) + } } -// Withdrawal creds change message -type WithdrawalCredentialsChange struct { - ValidatorIndex uint64 `json:"validator_index"` - FromBLSPubkey [48]byte `json:"from_bls_pubkey" ssz-size:"48"` - ToExecutionAddress [20]byte `json:"to_execution_address" ssz-size:"20"` +func NewBeaconBlock(data []byte, fork string) (BeaconBlock, error) { + fork = strings.ToLower(fork) + + switch fork { + case "deneb": + out := &deneb.BeaconBlock{} + err := out.UnmarshalSSZ(data) + if err != nil { + return nil, err + } + return out, nil + case "electra": + out := &electra.BeaconBlock{} + err := out.UnmarshalSSZ(data) + if err != nil { + return nil, err + } + return out, nil + default: + return nil, fmt.Errorf("unsupported fork: %s", fork) + } } diff --git a/shared/types/eth2/types_encoding.go b/shared/types/eth2/types_encoding.go deleted file mode 100644 index df1a4892f..000000000 --- a/shared/types/eth2/types_encoding.go +++ /dev/null @@ -1,446 +0,0 @@ -// Code generated by fastssz. DO NOT EDIT. -// Hash: 8334e51dc7fef48f4bfcdf131ce2d53118dc8c0cba4ed032b43e8a8e83319297 -// Version: 0.1.3 -package eth2 - -import ( - ssz "github.com/ferranbt/fastssz" -) - -// MarshalSSZ ssz marshals the DepositDataNoSignature object -func (d *DepositDataNoSignature) MarshalSSZ() ([]byte, error) { - return ssz.MarshalSSZ(d) -} - -// MarshalSSZTo ssz marshals the DepositDataNoSignature object to a target array -func (d *DepositDataNoSignature) MarshalSSZTo(buf []byte) (dst []byte, err error) { - dst = buf - - // Field (0) 'PublicKey' - if size := len(d.PublicKey); size != 48 { - err = ssz.ErrBytesLengthFn("DepositDataNoSignature.PublicKey", size, 48) - return - } - dst = append(dst, d.PublicKey...) - - // Field (1) 'WithdrawalCredentials' - if size := len(d.WithdrawalCredentials); size != 32 { - err = ssz.ErrBytesLengthFn("DepositDataNoSignature.WithdrawalCredentials", size, 32) - return - } - dst = append(dst, d.WithdrawalCredentials...) - - // Field (2) 'Amount' - dst = ssz.MarshalUint64(dst, d.Amount) - - return -} - -// UnmarshalSSZ ssz unmarshals the DepositDataNoSignature object -func (d *DepositDataNoSignature) UnmarshalSSZ(buf []byte) error { - var err error - size := uint64(len(buf)) - if size != 88 { - return ssz.ErrSize - } - - // Field (0) 'PublicKey' - if cap(d.PublicKey) == 0 { - d.PublicKey = make([]byte, 0, len(buf[0:48])) - } - d.PublicKey = append(d.PublicKey, buf[0:48]...) - - // Field (1) 'WithdrawalCredentials' - if cap(d.WithdrawalCredentials) == 0 { - d.WithdrawalCredentials = make([]byte, 0, len(buf[48:80])) - } - d.WithdrawalCredentials = append(d.WithdrawalCredentials, buf[48:80]...) - - // Field (2) 'Amount' - d.Amount = ssz.UnmarshallUint64(buf[80:88]) - - return err -} - -// SizeSSZ returns the ssz encoded size in bytes for the DepositDataNoSignature object -func (d *DepositDataNoSignature) SizeSSZ() (size int) { - size = 88 - return -} - -// HashTreeRoot ssz hashes the DepositDataNoSignature object -func (d *DepositDataNoSignature) HashTreeRoot() ([32]byte, error) { - return ssz.HashWithDefaultHasher(d) -} - -// HashTreeRootWith ssz hashes the DepositDataNoSignature object with a hasher -func (d *DepositDataNoSignature) HashTreeRootWith(hh ssz.HashWalker) (err error) { - indx := hh.Index() - - // Field (0) 'PublicKey' - if size := len(d.PublicKey); size != 48 { - err = ssz.ErrBytesLengthFn("DepositDataNoSignature.PublicKey", size, 48) - return - } - hh.PutBytes(d.PublicKey) - - // Field (1) 'WithdrawalCredentials' - if size := len(d.WithdrawalCredentials); size != 32 { - err = ssz.ErrBytesLengthFn("DepositDataNoSignature.WithdrawalCredentials", size, 32) - return - } - hh.PutBytes(d.WithdrawalCredentials) - - // Field (2) 'Amount' - hh.PutUint64(d.Amount) - - hh.Merkleize(indx) - return -} - -// GetTree ssz hashes the DepositDataNoSignature object -func (d *DepositDataNoSignature) GetTree() (*ssz.Node, error) { - return ssz.ProofTree(d) -} - -// MarshalSSZ ssz marshals the DepositData object -func (d *DepositData) MarshalSSZ() ([]byte, error) { - return ssz.MarshalSSZ(d) -} - -// MarshalSSZTo ssz marshals the DepositData object to a target array -func (d *DepositData) MarshalSSZTo(buf []byte) (dst []byte, err error) { - dst = buf - - // Field (0) 'PublicKey' - if size := len(d.PublicKey); size != 48 { - err = ssz.ErrBytesLengthFn("DepositData.PublicKey", size, 48) - return - } - dst = append(dst, d.PublicKey...) - - // Field (1) 'WithdrawalCredentials' - if size := len(d.WithdrawalCredentials); size != 32 { - err = ssz.ErrBytesLengthFn("DepositData.WithdrawalCredentials", size, 32) - return - } - dst = append(dst, d.WithdrawalCredentials...) - - // Field (2) 'Amount' - dst = ssz.MarshalUint64(dst, d.Amount) - - // Field (3) 'Signature' - if size := len(d.Signature); size != 96 { - err = ssz.ErrBytesLengthFn("DepositData.Signature", size, 96) - return - } - dst = append(dst, d.Signature...) - - return -} - -// UnmarshalSSZ ssz unmarshals the DepositData object -func (d *DepositData) UnmarshalSSZ(buf []byte) error { - var err error - size := uint64(len(buf)) - if size != 184 { - return ssz.ErrSize - } - - // Field (0) 'PublicKey' - if cap(d.PublicKey) == 0 { - d.PublicKey = make([]byte, 0, len(buf[0:48])) - } - d.PublicKey = append(d.PublicKey, buf[0:48]...) - - // Field (1) 'WithdrawalCredentials' - if cap(d.WithdrawalCredentials) == 0 { - d.WithdrawalCredentials = make([]byte, 0, len(buf[48:80])) - } - d.WithdrawalCredentials = append(d.WithdrawalCredentials, buf[48:80]...) - - // Field (2) 'Amount' - d.Amount = ssz.UnmarshallUint64(buf[80:88]) - - // Field (3) 'Signature' - if cap(d.Signature) == 0 { - d.Signature = make([]byte, 0, len(buf[88:184])) - } - d.Signature = append(d.Signature, buf[88:184]...) - - return err -} - -// SizeSSZ returns the ssz encoded size in bytes for the DepositData object -func (d *DepositData) SizeSSZ() (size int) { - size = 184 - return -} - -// HashTreeRoot ssz hashes the DepositData object -func (d *DepositData) HashTreeRoot() ([32]byte, error) { - return ssz.HashWithDefaultHasher(d) -} - -// HashTreeRootWith ssz hashes the DepositData object with a hasher -func (d *DepositData) HashTreeRootWith(hh ssz.HashWalker) (err error) { - indx := hh.Index() - - // Field (0) 'PublicKey' - if size := len(d.PublicKey); size != 48 { - err = ssz.ErrBytesLengthFn("DepositData.PublicKey", size, 48) - return - } - hh.PutBytes(d.PublicKey) - - // Field (1) 'WithdrawalCredentials' - if size := len(d.WithdrawalCredentials); size != 32 { - err = ssz.ErrBytesLengthFn("DepositData.WithdrawalCredentials", size, 32) - return - } - hh.PutBytes(d.WithdrawalCredentials) - - // Field (2) 'Amount' - hh.PutUint64(d.Amount) - - // Field (3) 'Signature' - if size := len(d.Signature); size != 96 { - err = ssz.ErrBytesLengthFn("DepositData.Signature", size, 96) - return - } - hh.PutBytes(d.Signature) - - hh.Merkleize(indx) - return -} - -// GetTree ssz hashes the DepositData object -func (d *DepositData) GetTree() (*ssz.Node, error) { - return ssz.ProofTree(d) -} - -// MarshalSSZ ssz marshals the SigningRoot object -func (s *SigningRoot) MarshalSSZ() ([]byte, error) { - return ssz.MarshalSSZ(s) -} - -// MarshalSSZTo ssz marshals the SigningRoot object to a target array -func (s *SigningRoot) MarshalSSZTo(buf []byte) (dst []byte, err error) { - dst = buf - - // Field (0) 'ObjectRoot' - if size := len(s.ObjectRoot); size != 32 { - err = ssz.ErrBytesLengthFn("SigningRoot.ObjectRoot", size, 32) - return - } - dst = append(dst, s.ObjectRoot...) - - // Field (1) 'Domain' - if size := len(s.Domain); size != 32 { - err = ssz.ErrBytesLengthFn("SigningRoot.Domain", size, 32) - return - } - dst = append(dst, s.Domain...) - - return -} - -// UnmarshalSSZ ssz unmarshals the SigningRoot object -func (s *SigningRoot) UnmarshalSSZ(buf []byte) error { - var err error - size := uint64(len(buf)) - if size != 64 { - return ssz.ErrSize - } - - // Field (0) 'ObjectRoot' - if cap(s.ObjectRoot) == 0 { - s.ObjectRoot = make([]byte, 0, len(buf[0:32])) - } - s.ObjectRoot = append(s.ObjectRoot, buf[0:32]...) - - // Field (1) 'Domain' - if cap(s.Domain) == 0 { - s.Domain = make([]byte, 0, len(buf[32:64])) - } - s.Domain = append(s.Domain, buf[32:64]...) - - return err -} - -// SizeSSZ returns the ssz encoded size in bytes for the SigningRoot object -func (s *SigningRoot) SizeSSZ() (size int) { - size = 64 - return -} - -// HashTreeRoot ssz hashes the SigningRoot object -func (s *SigningRoot) HashTreeRoot() ([32]byte, error) { - return ssz.HashWithDefaultHasher(s) -} - -// HashTreeRootWith ssz hashes the SigningRoot object with a hasher -func (s *SigningRoot) HashTreeRootWith(hh ssz.HashWalker) (err error) { - indx := hh.Index() - - // Field (0) 'ObjectRoot' - if size := len(s.ObjectRoot); size != 32 { - err = ssz.ErrBytesLengthFn("SigningRoot.ObjectRoot", size, 32) - return - } - hh.PutBytes(s.ObjectRoot) - - // Field (1) 'Domain' - if size := len(s.Domain); size != 32 { - err = ssz.ErrBytesLengthFn("SigningRoot.Domain", size, 32) - return - } - hh.PutBytes(s.Domain) - - hh.Merkleize(indx) - return -} - -// GetTree ssz hashes the SigningRoot object -func (s *SigningRoot) GetTree() (*ssz.Node, error) { - return ssz.ProofTree(s) -} - -// MarshalSSZ ssz marshals the VoluntaryExit object -func (v *VoluntaryExit) MarshalSSZ() ([]byte, error) { - return ssz.MarshalSSZ(v) -} - -// MarshalSSZTo ssz marshals the VoluntaryExit object to a target array -func (v *VoluntaryExit) MarshalSSZTo(buf []byte) (dst []byte, err error) { - dst = buf - - // Field (0) 'Epoch' - dst = ssz.MarshalUint64(dst, v.Epoch) - - // Field (1) 'ValidatorIndex' - dst = ssz.MarshalUint64(dst, v.ValidatorIndex) - - return -} - -// UnmarshalSSZ ssz unmarshals the VoluntaryExit object -func (v *VoluntaryExit) UnmarshalSSZ(buf []byte) error { - var err error - size := uint64(len(buf)) - if size != 16 { - return ssz.ErrSize - } - - // Field (0) 'Epoch' - v.Epoch = ssz.UnmarshallUint64(buf[0:8]) - - // Field (1) 'ValidatorIndex' - v.ValidatorIndex = ssz.UnmarshallUint64(buf[8:16]) - - return err -} - -// SizeSSZ returns the ssz encoded size in bytes for the VoluntaryExit object -func (v *VoluntaryExit) SizeSSZ() (size int) { - size = 16 - return -} - -// HashTreeRoot ssz hashes the VoluntaryExit object -func (v *VoluntaryExit) HashTreeRoot() ([32]byte, error) { - return ssz.HashWithDefaultHasher(v) -} - -// HashTreeRootWith ssz hashes the VoluntaryExit object with a hasher -func (v *VoluntaryExit) HashTreeRootWith(hh ssz.HashWalker) (err error) { - indx := hh.Index() - - // Field (0) 'Epoch' - hh.PutUint64(v.Epoch) - - // Field (1) 'ValidatorIndex' - hh.PutUint64(v.ValidatorIndex) - - hh.Merkleize(indx) - return -} - -// GetTree ssz hashes the VoluntaryExit object -func (v *VoluntaryExit) GetTree() (*ssz.Node, error) { - return ssz.ProofTree(v) -} - -// MarshalSSZ ssz marshals the WithdrawalCredentialsChange object -func (w *WithdrawalCredentialsChange) MarshalSSZ() ([]byte, error) { - return ssz.MarshalSSZ(w) -} - -// MarshalSSZTo ssz marshals the WithdrawalCredentialsChange object to a target array -func (w *WithdrawalCredentialsChange) MarshalSSZTo(buf []byte) (dst []byte, err error) { - dst = buf - - // Field (0) 'ValidatorIndex' - dst = ssz.MarshalUint64(dst, w.ValidatorIndex) - - // Field (1) 'FromBLSPubkey' - dst = append(dst, w.FromBLSPubkey[:]...) - - // Field (2) 'ToExecutionAddress' - dst = append(dst, w.ToExecutionAddress[:]...) - - return -} - -// UnmarshalSSZ ssz unmarshals the WithdrawalCredentialsChange object -func (w *WithdrawalCredentialsChange) UnmarshalSSZ(buf []byte) error { - var err error - size := uint64(len(buf)) - if size != 76 { - return ssz.ErrSize - } - - // Field (0) 'ValidatorIndex' - w.ValidatorIndex = ssz.UnmarshallUint64(buf[0:8]) - - // Field (1) 'FromBLSPubkey' - copy(w.FromBLSPubkey[:], buf[8:56]) - - // Field (2) 'ToExecutionAddress' - copy(w.ToExecutionAddress[:], buf[56:76]) - - return err -} - -// SizeSSZ returns the ssz encoded size in bytes for the WithdrawalCredentialsChange object -func (w *WithdrawalCredentialsChange) SizeSSZ() (size int) { - size = 76 - return -} - -// HashTreeRoot ssz hashes the WithdrawalCredentialsChange object -func (w *WithdrawalCredentialsChange) HashTreeRoot() ([32]byte, error) { - return ssz.HashWithDefaultHasher(w) -} - -// HashTreeRootWith ssz hashes the WithdrawalCredentialsChange object with a hasher -func (w *WithdrawalCredentialsChange) HashTreeRootWith(hh ssz.HashWalker) (err error) { - indx := hh.Index() - - // Field (0) 'ValidatorIndex' - hh.PutUint64(w.ValidatorIndex) - - // Field (1) 'FromBLSPubkey' - hh.PutBytes(w.FromBLSPubkey[:]) - - // Field (2) 'ToExecutionAddress' - hh.PutBytes(w.ToExecutionAddress[:]) - - hh.Merkleize(indx) - return -} - -// GetTree ssz hashes the WithdrawalCredentialsChange object -func (w *WithdrawalCredentialsChange) GetTree() (*ssz.Node, error) { - return ssz.ProofTree(w) -} 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/math/math.go b/shared/utils/math/math.go index ca03ba4e4..69628d362 100644 --- a/shared/utils/math/math.go +++ b/shared/utils/math/math.go @@ -2,6 +2,7 @@ package math import ( "math" + "math/bits" ) // Round a float64 down to a number of places @@ -13,3 +14,19 @@ func RoundDown(val float64, places int) float64 { func RoundUp(val float64, places int) float64 { return math.Ceil(val*math.Pow10(places)) / math.Pow10(places) } + +func GetPowerOfTwoCeil(x uint64) uint64 { + // Base case + if x <= 1 { + return 1 + } + + // Check if already a power of two + if x&(x-1) == 0 { + return x + } + + // Find the most significant bit + msb := bits.Len64(x) - 1 + return 1 << (msb + 1) +} 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 48722f717..1162a6cfd 100644 --- a/shared/utils/rp/node.go +++ b/shared/utils/rp/node.go @@ -9,12 +9,12 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" - node131 "github.com/rocket-pool/rocketpool-go/legacy/v1.3.1/node" - "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" + node131 "github.com/rocket-pool/smartnode/bindings/legacy/v1.3.1/node" + "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/deposit-data.go b/shared/utils/validator/deposit-data.go index 678c2eab6..98c4a2152 100644 --- a/shared/utils/validator/deposit-data.go +++ b/shared/utils/validator/deposit-data.go @@ -2,17 +2,17 @@ package validator import ( "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/smartnode/shared/types/eth2" + "github.com/rocket-pool/smartnode/shared/types/eth2/generic" eth2types "github.com/wealdtech/go-eth2-types/v2" "github.com/rocket-pool/smartnode/shared/services/beacon" ) // Get deposit data & root for a given validator key and withdrawal credentials -func GetDepositData(validatorKey *eth2types.BLSPrivateKey, withdrawalCredentials common.Hash, eth2Config beacon.Eth2Config, depositAmount uint64) (eth2.DepositData, common.Hash, error) { +func GetDepositData(validatorKey *eth2types.BLSPrivateKey, withdrawalCredentials common.Hash, eth2Config beacon.Eth2Config, depositAmount uint64) (generic.DepositData, common.Hash, error) { // Build deposit data - dd := eth2.DepositDataNoSignature{ + dd := generic.DepositDataNoSignature{ PublicKey: validatorKey.PublicKey().Marshal(), WithdrawalCredentials: withdrawalCredentials[:], Amount: depositAmount, @@ -21,10 +21,10 @@ func GetDepositData(validatorKey *eth2types.BLSPrivateKey, withdrawalCredentials // Get signing root or, err := dd.HashTreeRoot() if err != nil { - return eth2.DepositData{}, common.Hash{}, err + return generic.DepositData{}, common.Hash{}, err } - sr := eth2.SigningRoot{ + sr := generic.SigningRoot{ ObjectRoot: or[:], Domain: eth2types.Domain(eth2types.DomainDeposit, eth2Config.GenesisForkVersion, eth2types.ZeroGenesisValidatorsRoot), } @@ -32,11 +32,11 @@ func GetDepositData(validatorKey *eth2types.BLSPrivateKey, withdrawalCredentials // Get signing root with domain srHash, err := sr.HashTreeRoot() if err != nil { - return eth2.DepositData{}, common.Hash{}, err + return generic.DepositData{}, common.Hash{}, err } // Build deposit data struct (with signature) - var depositData = eth2.DepositData{ + var depositData = generic.DepositData{ PublicKey: dd.PublicKey, WithdrawalCredentials: dd.WithdrawalCredentials, Amount: dd.Amount, @@ -46,7 +46,7 @@ func GetDepositData(validatorKey *eth2types.BLSPrivateKey, withdrawalCredentials // Get deposit data root depositDataRoot, err := depositData.HashTreeRoot() if err != nil { - return eth2.DepositData{}, common.Hash{}, err + return generic.DepositData{}, common.Hash{}, err } // Return diff --git a/shared/utils/validator/set-withdrawal-creds.go b/shared/utils/validator/set-withdrawal-creds.go index 2b482cb91..2579d3b8b 100644 --- a/shared/utils/validator/set-withdrawal-creds.go +++ b/shared/utils/validator/set-withdrawal-creds.go @@ -6,8 +6,8 @@ import ( "strings" "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/rocketpool-go/types" - "github.com/rocket-pool/smartnode/shared/types/eth2" + "github.com/rocket-pool/smartnode/bindings/types" + "github.com/rocket-pool/smartnode/shared/types/eth2/generic" eth2types "github.com/wealdtech/go-eth2-types/v2" ) @@ -39,7 +39,7 @@ func GetSignedWithdrawalCredsChangeMessage(withdrawalKey *eth2types.BLSPrivateKe } // Build withdrawal creds change message - message := eth2.WithdrawalCredentialsChange{ + message := generic.WithdrawalCredentialsChange{ ValidatorIndex: indexNum, FromBLSPubkey: withdrawalPubkeyBuffer, ToExecutionAddress: newWithdrawalAddress, @@ -52,7 +52,7 @@ func GetSignedWithdrawalCredsChangeMessage(withdrawalKey *eth2types.BLSPrivateKe } // Get signing root - sr := eth2.SigningRoot{ + sr := generic.SigningRoot{ ObjectRoot: or[:], Domain: signatureDomain, } diff --git a/shared/utils/validator/voluntary-exit.go b/shared/utils/validator/voluntary-exit.go index c7123db82..69b466453 100644 --- a/shared/utils/validator/voluntary-exit.go +++ b/shared/utils/validator/voluntary-exit.go @@ -4,8 +4,8 @@ import ( "fmt" "strconv" - "github.com/rocket-pool/rocketpool-go/types" - "github.com/rocket-pool/smartnode/shared/types/eth2" + "github.com/rocket-pool/smartnode/bindings/types" + "github.com/rocket-pool/smartnode/shared/types/eth2/generic" eth2types "github.com/wealdtech/go-eth2-types/v2" ) @@ -19,7 +19,7 @@ func GetSignedExitMessage(validatorKey *eth2types.BLSPrivateKey, validatorIndex } // Build voluntary exit message - exitMessage := eth2.VoluntaryExit{ + exitMessage := generic.VoluntaryExit{ Epoch: epoch, ValidatorIndex: indexNum, } @@ -31,7 +31,7 @@ func GetSignedExitMessage(validatorKey *eth2types.BLSPrivateKey, validatorIndex } // Get signing root - sr := eth2.SigningRoot{ + sr := generic.SigningRoot{ ObjectRoot: or[:], Domain: signatureDomain, } 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/shared/version.go b/shared/version.go index cb099326c..13f6cc3a2 100644 --- a/shared/version.go +++ b/shared/version.go @@ -1,10 +1,20 @@ package shared -const RocketPoolVersion string = "1.16.0-dev" - -const Logo string = `______ _ _ ______ _ -| ___ \ | | | | | ___ \ | | -| |_/ /___ ___| | _____| |_ | |_/ /__ ___ | | -| // _ \ / __| |/ / _ \ __| | __/ _ \ / _ \| | -| |\ \ (_) | (__| < __/ |_ | | | (_) | (_) | | -\_| \_\___/ \___|_|\_\___|\__| \_| \___/ \___/|_|` +import ( + _ "embed" + "strings" +) + +//go:embed version.txt +var rocketPoolVersion string + +func RocketPoolVersion() string { + return strings.TrimSpace(rocketPoolVersion) +} + +//go:embed logo.txt +var logo string + +func Logo() string { + return strings.TrimSpace(logo) +} diff --git a/shared/version.txt b/shared/version.txt new file mode 100644 index 000000000..71bd5d9ee --- /dev/null +++ b/shared/version.txt @@ -0,0 +1 @@ +1.16.0 \ No newline at end of file diff --git a/sszgen.sh b/sszgen.sh index 10811c0c2..333e7f97e 100755 --- a/sszgen.sh +++ b/sszgen.sh @@ -2,5 +2,8 @@ # Generates the ssz encoding methods for eth2 types with fastssz # Install sszgen with `go get github.com/ferranbt/fastssz/sszgen` -rm -f ./shared/types/eth2/types_encoding.go -sszgen --path ./shared/types/eth2 --exclude-objs Uint256 +SSZGEN_CMD="go run github.com/ferranbt/fastssz/sszgen@v0.1.4" +find ./shared/types/eth2 -name "*_encoding.go" -exec sh -c 'head -1 {} | grep -q "Code generated by fastssz"' \; -exec rm {} \; +$SSZGEN_CMD --path ./shared/types/eth2/fork/deneb --include ./shared/types/eth2/generic +$SSZGEN_CMD --path ./shared/types/eth2/fork/electra --include ./shared/types/eth2/generic +$SSZGEN_CMD --path ./shared/types/eth2/generic --exclude-objs Uint256 diff --git a/treegen/.gitignore b/treegen/.gitignore new file mode 100644 index 000000000..c39268da4 --- /dev/null +++ b/treegen/.gitignore @@ -0,0 +1,3 @@ +*.json +treegen +Releases/ \ No newline at end of file diff --git a/treegen/Dockerfile b/treegen/Dockerfile new file mode 100644 index 000000000..8d246f2fc --- /dev/null +++ b/treegen/Dockerfile @@ -0,0 +1,4 @@ +FROM debian:bullseye-slim + +ARG TARGETARCH +COPY ./Releases/treegen-linux-${TARGETARCH} /treegen diff --git a/treegen/LICENSE b/treegen/LICENSE new file mode 100644 index 000000000..261eeb9e9 --- /dev/null +++ b/treegen/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/treegen/README.md b/treegen/README.md new file mode 100644 index 000000000..319920900 --- /dev/null +++ b/treegen/README.md @@ -0,0 +1,69 @@ +# Rocket Pool Rewards Tree Generation Tool + +This is a standalone tool for creating the rewards tree and minipool attestation files for rewards intervals on the Rocket Pool network. +It can recreate trees for past rewards intervals, or it can "simulate" the tree for the current interval ending at the latest finalized block (for testing purposes). +It uses the same codebase as the Smartnode, so you can be assured that `treegen` will generate the same trees as the Smartnode stack. + + +## Running Treegen + +There are currently three ways to run `treegen`: + +1. Run the precompiled binaries locally (Linux only, using `glibc`) +2. Run the Docker image (Linux, Windows, and macOS) +3. Build from source and run locally + + +### Running the Binary Locally + +Run the binary as follows: + +``` +$ ./treegen-linux-amd64 [options] +``` + +Options: + +``` + --bn-endpoint value, -b value The URL of the Beacon Node's REST API. Note that for past interval generation, this must have Archive capability (ability to replay arbitrary historical states). (default: "http://localhost:5052") + --ec-endpoint value, -e value The URL of the Execution Client's JSON-RPC API. Note that for past interval generation, this must be an Archive EC. (default: "http://localhost:8545") + --interval value, -i value The rewards interval to generate the artifacts for. A value of -1 indicates that you want to do a "dry run" of generating the tree for the current (active) interval, using the current latest finalized block as the interval end. (default: -1) + --output-dir value, -o value Optional output directory to save generated files (default is the current working directory). + --pretty-print, -p Toggle for saving the files in pretty-print format so they're human readable. (default: true) + --ruleset value, -r value The ruleset to use during generation. If not included, treegen will use the default ruleset for the network based on the rewards interval at the chosen block. Default of 0 will use whatever the ruleset specified by the network based on which block is being targeted. (default: 0) + --network-info, -n If provided, this will simply print out info about the network being used, the current rewards interval, and the current ruleset. (default: false) + --approximate-only, -a Approximates the rETH stakers' share of the Smoothing Pool at the current block instead of generating the entire rewards tree. Ignores -i. (default: false) + --use-rolling-records, -rr Enable the rolling record capability of the Smartnode tree generator. Use this to store and load record caches instead of recalculating attestation performance each time you run treegen. (default: false) +``` + + +### Running via the Docker Image + +`treegen` is also available in a Docker image for users not on Linux systems, or on systems incompatible with the precompiled binaries. +To run it, use the following command: + +``` +$ ./treegen.sh -e -b [options] +``` + +Options: + +``` + --bn-endpoint value, -b value The URL of the Beacon Node's REST API. Note that for past interval generation, this must have Archive capability (ability to replay arbitrary historical states). (default: "http://localhost:5052") + --ec-endpoint value, -e value The URL of the Execution Client's JSON-RPC API. Note that for past interval generation, this must be an Archive EC. (default: "http://localhost:8545") + --interval value, -i value The rewards interval to generate the artifacts for. A value of -1 indicates that you want to do a "dry run" of generating the tree for the current (active) interval, using the current latest finalized block as the interval end. (default: -1) + --pretty-print, -p Toggle for saving the files in pretty-print format so they're human readable. (default: true) + --ruleset value, -r value The ruleset to use during generation. If not included, treegen will use the default ruleset for the network based on the rewards interval at the chosen block. Default of 0 will use whatever the ruleset specified by the network based on which block is being targeted. (default: 0) + --network-info, -n If provided, this will simply print out info about the network being used, the current rewards interval, and the current ruleset. (default: false) + --approximate-only, -a Approximates the rETH stakers' share of the Smoothing Pool at the current block instead of generating the entire rewards tree. Ignores -i. (default: false) + --use-rolling-records, -rr Enable the rolling record capability of the Smartnode tree generator. Use this to store and load record caches instead of recalculating attestation performance each time you run treegen. (default: false) +``` + +NOTE: Do *not* use the `-o` flag if you are using this script, as it is already built into the script. +Output files will be stored in the `out` directory. + + +## Building + +To build the binary locally, simply enter this folder and run `go build`. +You will need to have Go v1.19 or higher set up already. diff --git a/treegen/build-release.sh b/treegen/build-release.sh new file mode 100755 index 000000000..7fb3b47a2 --- /dev/null +++ b/treegen/build-release.sh @@ -0,0 +1,74 @@ +#!/bin/bash + +# Builds the binaries +build_binary() { + echo -n "Building binaries... " + docker run --rm -v $PWD:/treegen rocketpool/smartnode-builder:latest /treegen/build_binaries.sh + echo "done!" +} + +# Build the images +build_docker() { + echo -n "Building Docker images... " + docker buildx build --platform=linux/amd64 -t rocketpool/treegen:$VERSION-amd64 --load . + docker buildx build --platform=linux/arm64 -t rocketpool/treegen:$VERSION-arm64 --load . + docker push rocketpool/treegen:$VERSION-amd64 + docker push rocketpool/treegen:$VERSION-arm64 + echo "done!" +} + +# Build the manifest +build_manifest() { + echo -n "Building Docker manifest... " + rm -f ~/.docker/manifests/docker.io_rocketpool_treegen-$VERSION + rm -f ~/.docker/manifests/docker.io_rocketpool_treegen-latest + docker manifest create rocketpool/treegen:$VERSION --amend rocketpool/treegen:$VERSION-amd64 --amend rocketpool/treegen:$VERSION-arm64 + docker manifest create rocketpool/treegen:latest --amend rocketpool/treegen:$VERSION-amd64 --amend rocketpool/treegen:$VERSION-arm64 + echo "done!" + echo -n "Pushing to Docker Hub... " + docker manifest push --purge rocketpool/treegen:$VERSION + docker manifest push --purge rocketpool/treegen:latest + echo "done!" +} + +# Print usage +usage() { + echo "Usage: build-release.sh [options] -v " + echo "This script builds treegen's artifacts." + echo "Options:" + echo $'\t-a\tBuild all of the artifacts.' + echo $'\t-b\tBuild the native binaries for most Linux distributions' + echo $'\t-d\tBuild the Alpine-based Docker containers' + echo $'\t-m\tBuild the Docker manifest' + exit 0 +} + +# ================= +# === Main Body === +# ================= + +# Get the version +while getopts "abdmv:" FLAG; do + case "$FLAG" in + a) BINARIES=true DOCKER=true MANIFEST=true ;; + b) BINARIES=true ;; + d) DOCKER=true ;; + m) MANIFEST=true ;; + v) VERSION="$OPTARG" ;; + *) usage ;; + esac +done +if [ -z "$VERSION" ]; then + usage +fi + +# Build the artifacts +if [ "$BINARIES" = true ]; then + build_binary +fi +if [ "$DOCKER" = true ]; then + build_docker +fi +if [ "$MANIFEST" = true ]; then + build_manifest +fi diff --git a/rocketpool/build.sh b/treegen/build_binaries.sh similarity index 53% rename from rocketpool/build.sh rename to treegen/build_binaries.sh index 1e94d2e1c..59a44c30b 100755 --- a/rocketpool/build.sh +++ b/treegen/build_binaries.sh @@ -1,10 +1,11 @@ #!/bin/bash +cd /treegen + export CGO_ENABLED=1 -cd /smartnode/rocketpool # Build x64 version -CGO_CFLAGS="-O -D__BLST_PORTABLE__" GOARCH=amd64 GOOS=linux go build -o rocketpool-daemon-linux-amd64 rocketpool.go +CGO_CFLAGS="-O -D__BLST_PORTABLE__" GOARCH=amd64 GOOS=linux go build -o Releases/treegen-linux-amd64 -buildvcs=false # Build the arm64 version -CC=aarch64-linux-gnu-gcc CXX=aarch64-linux-gnu-cpp CGO_CFLAGS="-O -D__BLST_PORTABLE__" GOARCH=arm64 GOOS=linux go build -o rocketpool-daemon-linux-arm64 rocketpool.go \ No newline at end of file +CC=aarch64-linux-gnu-gcc CXX=aarch64-linux-gnu-cpp CGO_CFLAGS="-O -D__BLST_PORTABLE__" GOARCH=arm64 GOOS=linux go build -o Releases/treegen-linux-arm64 -buildvcs=false diff --git a/treegen/go.mod b/treegen/go.mod new file mode 100644 index 000000000..65baf0bd4 --- /dev/null +++ b/treegen/go.mod @@ -0,0 +1,121 @@ +module github.com/rocket-pool/smartnode/treegen + +go 1.21.8 + +require ( + github.com/ethereum/go-ethereum v1.13.5 + github.com/fatih/color v1.14.1 + github.com/felixge/fgprof v0.9.5 + github.com/goccy/go-json v0.10.2 + github.com/urfave/cli/v2 v2.26.0 +) + +require ( + github.com/Microsoft/go-winio v0.6.1 // indirect + github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect + github.com/alessio/shellescape v1.4.1 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/bits-and-blooms/bitset v1.11.0 // indirect + github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // 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/crackcomm/go-gitignore v0.0.0-20170627025303-887ab5e44cc3 // 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/ethereum/c-kzg-4844 v0.4.0 // indirect + github.com/ferranbt/fastssz v0.1.3 // indirect + github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/glendc/go-external-ip v0.1.0 // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect + github.com/go-stack/stack v1.8.1 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/gorilla/websocket v1.5.3 // indirect + github.com/hashicorp/go-version v1.6.0 // indirect + github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d // indirect + github.com/herumi/bls-eth-go-binary v1.28.1 // indirect + github.com/holiman/uint256 v1.2.4 // indirect + github.com/ipfs/bbloom v0.0.4 // indirect + github.com/ipfs/boxo v0.8.0 // indirect + github.com/ipfs/go-bitfield v1.1.0 // indirect + github.com/ipfs/go-block-format v0.1.2 // indirect + github.com/ipfs/go-cid v0.4.1 // indirect + github.com/ipfs/go-datastore v0.6.0 // indirect + github.com/ipfs/go-ipfs-util v0.0.2 // indirect + github.com/ipfs/go-ipld-cbor v0.0.6 // indirect + github.com/ipfs/go-ipld-format v0.4.0 // indirect + github.com/ipfs/go-ipld-legacy v0.1.1 // indirect + github.com/ipfs/go-log v1.0.5 // indirect + github.com/ipfs/go-log/v2 v2.5.1 // indirect + github.com/ipfs/go-metrics-interface v0.0.1 // indirect + github.com/ipld/go-codec-dagpb v1.6.0 // indirect + github.com/ipld/go-ipld-prime v0.20.0 // indirect + github.com/jbenet/goprocess v0.1.4 // indirect + github.com/klauspost/compress v1.17.9 // indirect + github.com/klauspost/cpuid/v2 v2.2.8 // indirect + github.com/libp2p/go-buffer-pool v0.1.0 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // 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/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect + github.com/mr-tron/base58 v1.2.0 // indirect + github.com/multiformats/go-base32 v0.1.0 // indirect + github.com/multiformats/go-base36 v0.2.0 // indirect + github.com/multiformats/go-multibase v0.2.0 // indirect + github.com/multiformats/go-multihash v0.2.3 // indirect + github.com/multiformats/go-varint v0.0.7 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/opentracing/opentracing-go v1.2.0 // indirect + github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/polydawn/refmt v0.89.0 // indirect + github.com/prometheus/client_golang v1.20.0 // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/common v0.55.0 // indirect + github.com/prometheus/procfs v0.15.1 // indirect + github.com/prysmaticlabs/fastssz v0.0.0-20221107182844-78142813af44 // indirect + github.com/prysmaticlabs/go-bitfield v0.0.0-20240328144219-a1caa50c3a1e // indirect + github.com/prysmaticlabs/gohashtree v0.0.3-alpha // indirect + github.com/russross/blackfriday/v2 v2.1.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/supranational/blst v0.3.11 // indirect + github.com/thomaso-mirodin/intmath v0.0.0-20160323211736-5dc6d854e46e // indirect + github.com/tklauser/go-sysconf v0.3.13 // indirect + github.com/tklauser/numcpus v0.7.0 // indirect + github.com/wealdtech/go-eth2-types/v2 v2.8.1-0.20230131115251-b93cf60cee26 // indirect + github.com/wealdtech/go-merkletree v1.0.1-0.20190605192610-2bb163c2ea2a // indirect + github.com/whyrusleeping/cbor-gen v0.0.0-20230126041949-52956bd4c9aa // indirect + github.com/whyrusleeping/chunker v0.0.0-20181014151217-fe64bd25879f // indirect + github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect + github.com/yusufpapurcu/wmi v1.2.3 // indirect + go.opentelemetry.io/otel v1.29.0 // indirect + go.opentelemetry.io/otel/metric v1.29.0 // indirect + go.opentelemetry.io/otel/trace v1.29.0 // indirect + go.uber.org/atomic v1.10.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.27.0 // 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/sync v0.8.0 // indirect + golang.org/x/sys v0.24.0 // indirect + golang.org/x/term v0.23.0 // indirect + golang.org/x/text v0.17.0 // indirect + golang.org/x/tools v0.24.0 // indirect + golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect + google.golang.org/protobuf v1.34.2 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + lukechampine.com/blake3 v1.3.0 // indirect + rsc.io/tmplfunc v0.0.3 // indirect +) diff --git a/treegen/main.go b/treegen/main.go new file mode 100644 index 000000000..b5e8cf041 --- /dev/null +++ b/treegen/main.go @@ -0,0 +1,182 @@ +package main + +import ( + "fmt" + "os" + "runtime" + "runtime/pprof" + + "github.com/felixge/fgprof" + "github.com/urfave/cli/v2" +) + +const ( + version string = "1.5.2" + colorReset string = "\033[0m" + colorRed string = "\033[31m" +) + +func main() { + + // Initialise application + app := cli.NewApp() + + // Set application info + app.Name = "treegen" + app.Usage = "This application can be used to generate past Merkle rewards trees for the Rocket Pool network, or preview / test generation of the tree for the current interval." + app.Version = version + app.Authors = []*cli.Author{ + { + Name: "Joe Clapis", + Email: "joe@rocketpool.net", + }, + { + Name: "Jacob Shufro", + Email: "jacob@shuf.ro", + }, + } + app.Copyright = "(c) 2023 Rocket Pool Pty Ltd" + + // Set application flags + app.Flags = []cli.Flag{ + &cli.Int64Flag{ + Name: "interval", + Aliases: []string{"i"}, + Usage: "The rewards interval to generate the artifacts for. A value of -1 indicates that you want to do a \"dry run\" of generating the tree for the current (active) interval, using the current latest finalized block as the interval end (unless -t is passed).", + Value: -1, + }, + &cli.StringFlag{ + Name: "ec-endpoint", + Aliases: []string{"e"}, + Usage: "The URL of the Execution Client's JSON-RPC API. Note that for past interval generation, this must be an Archive EC.", + Value: "http://localhost:8545", + }, + &cli.StringFlag{ + Name: "bn-endpoint", + Aliases: []string{"b"}, + Usage: "The URL of the Beacon Node's REST API. Note that for past interval generation, this must have Archive capability (ability to replay arbitrary historical states).", + Value: "http://localhost:5052", + }, + &cli.StringFlag{ + Name: "output-dir", + Aliases: []string{"o"}, + Usage: "Output directory to save generated files.", + }, + &cli.BoolFlag{ + Name: "pretty-print", + Aliases: []string{"p"}, + Usage: "Toggle for saving the files in pretty-print format so they're human readable.", + Value: true, + }, + &cli.Uint64Flag{ + Name: "target-epoch", + Aliases: []string{"t"}, + Usage: "If provided, this flag will be used to override the last epoch of an interval, current or past. If passed with -i, the epoch must be part of the provided interval.", + }, + &cli.Uint64Flag{ + Name: "ruleset", + Aliases: []string{"r"}, + Usage: "The ruleset to use during generation. If not included, treegen will use the default ruleset for the network based on the rewards interval at the chosen block. Default of 0 will use whatever the ruleset specified by the network based on which block is being targeted.", + }, + &cli.BoolFlag{ + Name: "network-info", + Aliases: []string{"n"}, + Usage: "If provided, this will simply print out info about the network being used, the current or targeted interval, and the current or targeted ruleset.", + Value: false, + }, + &cli.BoolFlag{ + Name: "approximate-only", + Aliases: []string{"a"}, + Usage: "Approximates the rETH stakers' share of the Smoothing Pool at the current or target block instead of generating the entire rewards tree.", + Value: false, + }, + &cli.BoolFlag{ + Name: "use-rolling-records", + Aliases: []string{"rr"}, + Usage: "Enable the rolling record capability of the Smartnode tree generator. Use this to store and load record caches instead of recalculating attestation performance each time you run treegen.", + Value: false, + }, + &cli.BoolFlag{ + Name: "generate-voting-power", + Aliases: []string{"gvp"}, + Usage: "If Enabled, a file containing the voting power breakdown of all nodes will be saved to the output directory.", + Value: false, + }, + &cli.StringFlag{ + Name: "cpuprofile", + Aliases: []string{"c"}, + Usage: "Path to which to save a pprof cpu profile, e.g. ./treegen.pprof. If unset, profiling is disabled.", + }, + &cli.StringFlag{ + Name: "memprofile", + Aliases: []string{"m"}, + Usage: "Path to which to save a pprof heap profile, e.g. ./treegen.pprof. If unset, profiling is disabled.", + }, + &cli.StringFlag{ + Name: "fgprof", + Aliases: []string{"f"}, + Usage: "Path to which to save a fgprof profile, e.g. ./treegen.pprof. If unset, profiling is disabled.", + }, + } + + app.Action = func(c *cli.Context) error { + cpuprofile := c.String("cpuprofile") + if cpuprofile != "" { + f, err := os.Create(cpuprofile) + if err != nil { + fmt.Printf("%sError generating tree: %s%s\n", colorRed, err.Error(), colorReset) + os.Exit(1) + } + defer f.Close() + if err := pprof.StartCPUProfile(f); err != nil { + fmt.Printf("%sError generating tree: %s%s\n", colorRed, err.Error(), colorReset) + os.Exit(1) + } + defer pprof.StopCPUProfile() + } + + memprofile := c.String("memprofile") + if memprofile != "" { + defer func() { + f, err := os.Create(memprofile) + if err != nil { + fmt.Printf("%sError saving heap profile: %s%s\n", colorRed, err.Error(), colorReset) + os.Exit(1) + } + defer f.Close() + runtime.GC() + if err := pprof.WriteHeapProfile(f); err != nil { + fmt.Printf("%sError saving heap profile: %s%s\n", colorRed, err.Error(), colorReset) + } + }() + } + + fgprofile := c.String("fgprof") + if fgprofile != "" { + f, err := os.Create(fgprofile) + if err != nil { + fmt.Printf("%sError saving heap profile: %s%s\n", colorRed, err.Error(), colorReset) + os.Exit(1) + } + closure := fgprof.Start(f, fgprof.FormatPprof) + defer func() { + err := closure() + if err != nil { + fmt.Fprintf(os.Stderr, "Error stopping fgprof: %s\n", err.Error()) + } + }() + } + + return GenerateTree(c) + } + + // Run application + fmt.Println("") + err := app.Run(os.Args) + if err != nil { + fmt.Printf("%sError generating tree: %s%s\n", colorRed, err.Error(), colorReset) + os.Exit(1) + } + fmt.Println("") + +} diff --git a/treegen/tree-gen.go b/treegen/tree-gen.go new file mode 100644 index 000000000..76afd1e3d --- /dev/null +++ b/treegen/tree-gen.go @@ -0,0 +1,790 @@ +package main + +import ( + "bytes" + "context" + "fmt" + "math/big" + "net/http" + "os" + "path/filepath" + "time" + + "github.com/goccy/go-json" + + "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/ethclient" + "github.com/fatih/color" + "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" + rprewards "github.com/rocket-pool/smartnode/shared/services/rewards" + "github.com/rocket-pool/smartnode/shared/services/state" + cfgtypes "github.com/rocket-pool/smartnode/shared/types/config" + "github.com/rocket-pool/smartnode/shared/utils/log" + "github.com/urfave/cli/v2" +) + +const ( + MaxConcurrentEth1Requests = 200 +) + +// Details about the snapshot block / timestamp for a treegen target +type snapshotDetails struct { + index uint64 + startTime time.Time + endTime time.Time + startSlot uint64 + snapshotBeaconBlock uint64 + snapshotElBlockHeader *types.Header + intervalsPassed uint64 +} + +// targets holds information about the span we wish to gather data for. +// It could be an entire interval, or it could be a portion of one. +type targets struct { + // If generating a whole interval, we use the rewardsEvent + rewardsEvent *rewards.RewardsEvent + + // For preview related functions, we use snapshotDetails + snapshotDetails *snapshotDetails + + // Cached beacon block - not necessarily the last block in the last epoch, + // as it may not have been proposed + block *beacon.BeaconBlock +} + +// Arguments that will be passed down to the actual treegen routine +type treegenArguments struct { + // Header of the starting EL block + elBlockHeader *types.Header + + // Number of intervals elapsed + intervalsPassed uint64 + + // End time for the rewards tree (may not align on a full interval) + endTime time.Time + + // Start time for the rewards tree + startTime time.Time + + // Index of the rewards period + index uint64 + + // The first slot in the period + startSlot uint64 + + // Consensus end block + block *beacon.BeaconBlock + + // Network State at end EL block + state *state.NetworkState +} + +// Treegen holder for the requested execution metadata and necessary artifacts +type treeGenerator struct { + log *log.ColorLogger + errLog *log.ColorLogger + rp rprewards.RewardsExecutionClient + rpNative *rocketpool.RocketPool + cfg *config.RocketPoolConfig + mgr *state.NetworkStateManager + bn beacon.Client + beaconConfig beacon.Eth2Config + targets targets + outputDir string + prettyPrint bool + ruleset uint64 + generateVotingPower bool +} + +// Generates a new rewards tree based on the command line flags +func GenerateTree(c *cli.Context) error { + // Configure + configureHTTP() + + // Initialization + interval := c.Int64("interval") + targetEpoch := c.Uint64("target-epoch") + logger := log.NewColorLogger(color.FgHiWhite) + errLogger := log.NewColorLogger(color.FgRed) + + // URL acquisiton + ecUrl := c.String("ec-endpoint") + if ecUrl == "" { + return fmt.Errorf("ec-endpoint must be provided") + } + bnUrl := c.String("bn-endpoint") + if ecUrl == "" { + return fmt.Errorf("bn-endpoint must be provided") + } + + // Create the EC and BN clients + ec, err := ethclient.Dial(ecUrl) + if err != nil { + return fmt.Errorf("error connecting to the EC: %w", err) + } + bn := client.NewStandardHttpClient(bnUrl) + beaconConfig, err := bn.GetEth2Config() + if err != nil { + return fmt.Errorf("error getting beacon config from the BN at %s - %w", bnUrl, err) + } + + // Check which network we're on via the BN + depositContract, err := bn.GetEth2DepositContract() + if err != nil { + return fmt.Errorf("error getting deposit contract from the BN: %w", err) + } + var network cfgtypes.Network + switch depositContract.ChainID { + case 1: + network = cfgtypes.Network_Mainnet + logger.Printlnf("Beacon node is configured for Mainnet.") + case 560048: + network = cfgtypes.Network_Testnet + logger.Printlnf("Beacon node is configured for Testnet.") + default: + return fmt.Errorf("your Beacon node is configured for an unknown network with Chain ID [%d]", depositContract.ChainID) + } + + // Create a new config on the proper network + cfg := config.NewRocketPoolConfig("", true) + cfg.Smartnode.Network.Value = network + + // Create the RP wrapper + storageContract := cfg.Smartnode.GetStorageAddress() + rp, err := rocketpool.NewRocketPool(ec, common.HexToAddress(storageContract)) + if err != nil { + return fmt.Errorf("error creating Rocket Pool wrapper: %w", err) + } + + // Create the NetworkStateManager + mgr := state.NewNetworkStateManager(rp, cfg.Smartnode.GetStateManagerContracts(), bn, &logger) + + // Create the generator + generator := treeGenerator{ + log: &logger, + errLog: &errLogger, + rp: rprewards.NewRewardsExecutionClient(rp), + rpNative: rp, + cfg: cfg, + bn: bn, + mgr: mgr, + beaconConfig: beaconConfig, + outputDir: c.String("output-dir"), + prettyPrint: c.Bool("pretty-print"), + ruleset: c.Uint64("ruleset"), + generateVotingPower: c.Bool("generate-voting-power"), + } + + // initialize the generator targets + if err := generator.setTargets(interval, targetEpoch); err != nil { + return fmt.Errorf("error setting the targeted consensus epoch and block: %w", err) + } + + // Run the tree generation or the rETH SP approximation + if c.Bool("approximate-only") { + return generator.approximateRethSpRewards() + } + + // Print the network info and exit if requested + if c.Bool("network-info") { + return generator.printNetworkInfo() + } + + return generator.generateTree() +} + +func (g *treeGenerator) getTreegenArgs() (*treegenArguments, error) { + + // Cache the network state at the time of the targeted epoch for later use + state, err := g.mgr.GetStateForSlot(g.targets.block.Slot) + if err != nil { + return nil, fmt.Errorf("unable to get state at slot %d: %w", g.targets.block.Slot, err) + } + + // If we have a rewardsEvent, we're generating a full interval + if g.targets.rewardsEvent != nil { + index := g.targets.rewardsEvent.Index.Uint64() + startSlot := uint64(0) + if index > 0 { + // Get the start slot for this interval + previousRewardsEvent, err := g.rp.GetRewardSnapshotEvent( + g.cfg.Smartnode.GetPreviousRewardsPoolAddresses(), + uint64(index-1), + nil, + ) + if err != nil { + return nil, fmt.Errorf("error getting event for interval %d: %w", index-1, err) + } + startSlot, err = getStartSlotForInterval(previousRewardsEvent, g.bn, g.beaconConfig) + if err != nil { + return nil, fmt.Errorf("error getting start slot for interval %d: %w", index, err) + } + } + + elBlockHeader, err := g.rp.HeaderByNumber(context.Background(), g.targets.rewardsEvent.ExecutionBlock) + if err != nil { + return nil, fmt.Errorf("error getting el block header %d: %w", g.targets.rewardsEvent.ExecutionBlock.Uint64(), err) + } + return &treegenArguments{ + startTime: g.targets.rewardsEvent.IntervalStartTime, + endTime: g.targets.rewardsEvent.IntervalEndTime, + index: index, + intervalsPassed: g.targets.rewardsEvent.IntervalsPassed.Uint64(), + startSlot: startSlot, + block: g.targets.block, + elBlockHeader: elBlockHeader, + state: state, + }, nil + } + + // Partial interval + return &treegenArguments{ + startTime: g.targets.snapshotDetails.startTime, + endTime: g.targets.snapshotDetails.endTime, + index: g.targets.snapshotDetails.index, + intervalsPassed: g.targets.snapshotDetails.intervalsPassed, + startSlot: g.targets.snapshotDetails.startSlot, + block: g.targets.block, + elBlockHeader: g.targets.snapshotDetails.snapshotElBlockHeader, + state: state, + }, nil +} + +func (d *snapshotDetails) log(l *log.ColorLogger) { + l.Printlnf("Snapshot Beacon block = %d, EL block = %d, running from %s to %s\n", + d.snapshotBeaconBlock, + d.snapshotElBlockHeader.Number.Uint64(), + d.startTime, + d.endTime) +} + +func (g *treeGenerator) lastBlockInEpoch(epoch uint64) (*beacon.BeaconBlock, error) { + + // Get the last block proposed in the targeted epoch. + // If the targeted epoch has no proposals, return nil, nil + end := epoch * g.beaconConfig.SlotsPerEpoch + start := end + g.beaconConfig.SlotsPerEpoch - 1 + for slot := start; slot >= end; slot-- { + block, exists, err := g.bn.GetBeaconBlock(fmt.Sprint(slot)) + if err != nil { + return nil, err + } + + if exists { + // We found it, so set it and exit + return &block, nil + } + } + + return nil, nil +} + +func (g *treeGenerator) setTargets(interval int64, targetEpoch uint64) error { + var err error + + // Validate that the target epoch is finalized + if targetEpoch > 0 { + beaconHead, err := g.bn.GetBeaconHead() + if err != nil { + return fmt.Errorf("unable to query beacon head: %w", err) + } + + if targetEpoch > beaconHead.FinalizedEpoch { + return fmt.Errorf("targeted epoch has not yet been finalized") + } + } + + // If interval isn't set, we're generating a preview of the current interval + if interval < 0 { + var block *beacon.BeaconBlock + if targetEpoch == 0 { + // No targetEpoch was passed, so set it to the latest finalized epoch + b, err := g.mgr.GetLatestFinalizedBeaconBlock() + if err != nil { + return err + } + + block = &b + } else { + // A target epoch was passed, so find its last block + block, err = g.lastBlockInEpoch(targetEpoch) + if err != nil { + return err + } + if block == nil { + return fmt.Errorf("Unable to find any valid blocks in epoch %d. Was your BN checkpoint synced against a slot that occurred after this epoch?", targetEpoch) + } + } + + g.targets.block = block + g.targets.snapshotDetails, err = g.getSnapshotDetails() + if err != nil { + return err + } + + // Ensure the target block is in the current interval + if g.slotToTime(block.Slot).Before(g.targets.snapshotDetails.startTime) { + return fmt.Errorf("selected epoch precedes current interval. use -i to generate previous intervals") + } + + // Inform the user of the range they're querying + g.log.Printlnf("Targeting a portion of the current interval (%d)", g.targets.snapshotDetails.index) + g.targets.snapshotDetails.log(g.log) + + return nil + } + + // We're generating a previous interval (full or partial) + // Get the corresponding rewards event for that interval + rewardsEvent, err := g.rp.GetRewardSnapshotEvent( + g.cfg.Smartnode.GetPreviousRewardsPoolAddresses(), + uint64(interval), + nil, + ) + if err != nil { + return err + } + + // If targetEpoch isn't set, we're generating a full interval + if targetEpoch == 0 { + g.log.Printlnf("Targeting full interval %d", interval) + g.targets.rewardsEvent = &rewardsEvent + + // Cache the last block of the rewards period + g.targets.block, err = g.lastBlockInEpoch(rewardsEvent.ConsensusBlock.Uint64() / g.beaconConfig.SlotsPerEpoch) + if err != nil { + return err + } + if g.targets.block == nil { + return fmt.Errorf("unable to find any valid blocks in epoch %d. Was your BN checkpoint synced against a slot that occurred after this epoch?", targetEpoch) + } + + return nil + } + + // We're generating a partial interval + // Ensure the target slot happens *before* the end of the interval + eventBlock := rewardsEvent.ConsensusBlock.Uint64() + finalEpochOfInterval := eventBlock / g.beaconConfig.SlotsPerEpoch + if targetEpoch == finalEpochOfInterval { + return fmt.Errorf("target epoch %d was the end of the targeted interval %d.\nRerun without -t", targetEpoch, interval) + } + if targetEpoch > finalEpochOfInterval { + return fmt.Errorf("target epoch %d was after targeted interval %d", targetEpoch, interval) + } + + // Ensure the target epoch started *after* the start of the interval, which should land on the start of an epoch boundary + epochStartTime := g.slotToTime(targetEpoch * g.beaconConfig.SlotsPerEpoch) + if epochStartTime.Before(rewardsEvent.IntervalStartTime) { + return fmt.Errorf("target epoch %d was before targeted interval %d", targetEpoch, interval) + } + + // Cache the target block for later use + block, found, err := g.bn.GetBeaconBlock(fmt.Sprint(eventBlock)) + if err != nil { + return err + } + if !found { + return fmt.Errorf("Unable to find the ending block for interval %d (slot %d). Was your BN checkpoint synced against a slot that occurred after this epoch?", interval, eventBlock) + } + g.targets.block = &block + + g.targets.snapshotDetails, err = g.getSnapshotDetails() + if err != nil { + return err + } + + // Inform the user of the range they're querying + g.log.Printlnf("Targeting a portion of a previous interval (%d)", g.targets.snapshotDetails.index) + g.targets.snapshotDetails.log(g.log) + + return nil +} + +// Gets the timestamp for a Beacon slot +func (g *treeGenerator) slotToTime(slot uint64) time.Time { + genesisTime := time.Unix(int64(g.beaconConfig.GenesisTime), 0) + secondsForSlot := time.Duration(slot*g.beaconConfig.SecondsPerSlot) * time.Second + return genesisTime.Add(secondsForSlot) +} + +// Generates the rewards file for the given generator +func (g *treeGenerator) generateRewardsFile(treegen *rprewards.TreeGenerator) (*rprewards.GenerateTreeResult, error) { + if g.ruleset == 0 { + return treegen.GenerateTree() + } + + return treegen.GenerateTreeWithRuleset(g.ruleset) +} + +func (g *treeGenerator) serializeVotingPower(votingPowerFile *VotingPowerFile) ([]byte, error) { + if g.prettyPrint { + return json.MarshalIndent(votingPowerFile, "", "\t") + } + + return json.Marshal(votingPowerFile) +} + +// Serializes the minipool performance file into JSON +func (g *treeGenerator) serializeMinipoolPerformance(result *rprewards.GenerateTreeResult) ([]byte, error) { + perfFile := result.MinipoolPerformanceFile + + if g.prettyPrint { + return perfFile.SerializeHuman() + } + + return perfFile.Serialize() +} + +// Serializes the rewards tree file in to JSON +func (g *treeGenerator) serializeRewardsTree(rewardsFile rprewards.IRewardsFile) ([]byte, error) { + if g.prettyPrint { + return json.MarshalIndent(rewardsFile, "", "\t") + } + + return json.Marshal(rewardsFile) +} + +// Writes both the performance file and the rewards file to disk +func (g *treeGenerator) writeFiles(result *rprewards.GenerateTreeResult, votingPowerFile *VotingPowerFile) error { + g.log.Printlnf("Saving JSON files...") + rewardsFile := result.RewardsFile + index := rewardsFile.GetIndex() + + // Get the output paths + rewardsTreePath := filepath.Join( + g.outputDir, + g.cfg.Smartnode.GetRewardsTreeFilename(index, config.RewardsExtensionJSON), + ) + minipoolPerformancePath := filepath.Join( + g.outputDir, + g.cfg.Smartnode.GetMinipoolPerformanceFilename(index), + ) + + // Serialize the minipool performance file + minipoolPerformanceBytes, err := g.serializeMinipoolPerformance(result) + if err != nil { + return fmt.Errorf("error serializing minipool performance file into JSON: %w", err) + } + + // Write it to disk + err = os.WriteFile(minipoolPerformancePath, minipoolPerformanceBytes, 0644) + if err != nil { + return fmt.Errorf("error saving minipool performance file to %s: %w", minipoolPerformancePath, err) + } + + g.log.Printlnf("Saved minipool performance file to %s", minipoolPerformancePath) + rewardsFile.SetMinipoolPerformanceFileCID("---") + + // Serialize the rewards tree to JSON + wrapperBytes, err := g.serializeRewardsTree(rewardsFile) + if err != nil { + return fmt.Errorf("error serializing proof wrapper into JSON: %w", err) + } + g.log.Printlnf("Generation complete! Saving tree...") + + // Write the rewards tree to disk + err = os.WriteFile(rewardsTreePath, wrapperBytes, 0644) + if err != nil { + return fmt.Errorf("error saving rewards tree file to %s: %w", rewardsTreePath, err) + } + + g.log.Printlnf("Saved rewards snapshot file to %s", rewardsTreePath) + + // Write the voting power file to disk + if votingPowerFile != nil { + votingPowerFileBytes, err := g.serializeVotingPower(votingPowerFile) + if err != nil { + return fmt.Errorf("error serializing voting power file into JSON: %w", err) + } + + votingFilePath := filepath.Join(g.outputDir, fmt.Sprintf("rp-voting-power-%s-%d.json", string(g.cfg.Smartnode.Network.Value.(cfgtypes.Network)), votingPowerFile.ConsensusSlot)) + err = os.WriteFile(votingFilePath, votingPowerFileBytes, 0644) + if err != nil { + return fmt.Errorf("error saving voting power file to %s: %w", votingFilePath, err) + } + g.log.Printlnf("Saved voting power file to %s", votingFilePath) + } + g.log.Printlnf("Successfully generated rewards snapshot for interval %d", index) + + return nil +} + +// Creates a tree generator using the provided arguments +func (g *treeGenerator) getGenerator(args *treegenArguments) (*rprewards.TreeGenerator, error) { + + // Create the tree generator + out, err := rprewards.NewTreeGenerator( + g.log, "", g.rp, g.cfg, g.bn, args.index, + args.startTime, args.endTime, + &rprewards.SnapshotEnd{ + Slot: args.state.BeaconConfig.FirstSlotAtLeast(args.endTime.Unix()), + ConsensusBlock: args.block.Slot, + ExecutionBlock: args.elBlockHeader.Number.Uint64(), + }, args.elBlockHeader, + args.intervalsPassed, args.state) + if err != nil { + return nil, fmt.Errorf("error creating tree generator: %w", err) + } + + return out, nil +} + +// Approximates the rETH stakers' share of the Smoothing Pool's current balance +func (g *treeGenerator) approximateRethSpRewards() error { + args, err := g.getTreegenArgs() + if err != nil { + return fmt.Errorf("error compiling treegen arguments: %w", err) + } + + opts := &bind.CallOpts{ + BlockNumber: args.elBlockHeader.Number, + } + + // Log + g.log.Printlnf("Approximating rETH rewards for the current interval (%d)", args.index) + g.log.Printlnf("Snapshot Beacon block = %d, EL block = %d, running from %s to %s\n", + args.block.Slot, opts.BlockNumber.Uint64(), args.startTime, args.endTime) + + // Get the Smoothing Pool contract's balance + smoothingPoolContract, err := g.rpNative.GetContract("rocketSmoothingPool", opts) + if err != nil { + return fmt.Errorf("error getting smoothing pool contract: %w", err) + } + smoothingPoolBalance, err := g.rpNative.Client.BalanceAt(context.Background(), *smoothingPoolContract.Address, opts.BlockNumber) + if err != nil { + return fmt.Errorf("error getting smoothing pool balance: %w", err) + } + + // Create the tree generator + treegen, err := g.getGenerator(args) + if err != nil { + return err + } + + // Approximate the balance + var rETHShare *big.Int + if g.ruleset == 0 { + rETHShare, err = treegen.ApproximateStakerShareOfSmoothingPool() + } else { + rETHShare, err = treegen.ApproximateStakerShareOfSmoothingPoolWithRuleset(g.ruleset) + } + if err != nil { + return fmt.Errorf("error approximating rETH stakers' share of the Smoothing Pool: %w", err) + } + g.log.Printlnf("Total ETH in the Smoothing Pool: %s wei (%.6f ETH)", smoothingPoolBalance.String(), eth.WeiToEth(smoothingPoolBalance)) + g.log.Printlnf("rETH stakers's share: %s wei (%.6f ETH)", rETHShare.String(), eth.WeiToEth(rETHShare)) + + return nil +} + +// Generate a complete rewards tree +func (g *treeGenerator) generateTree() error { + var votingPowerFile *VotingPowerFile + args, err := g.getTreegenArgs() + if err != nil { + return fmt.Errorf("error compiling treegen arguments: %w", err) + } + + // Create the tree generator + treegen, err := g.getGenerator(args) + if err != nil { + return err + } + + // If a voting power file was requested, generate it now. + if g.generateVotingPower { + votingPowerFile = g.GenerateVotingPower(args.state) + votingPowerFile.Time = args.endTime + } + + // Generate the rewards file + start := time.Now() + result, err := g.generateRewardsFile(treegen) + if err != nil { + return fmt.Errorf("error generating Merkle tree: %w", err) + } + + for address, network := range result.InvalidNetworkNodes { + g.log.Printlnf("WARNING: Node %s has invalid network %d assigned! Using 0 (mainnet) instead.", address.Hex(), network) + } + g.log.Printlnf("Finished in %s", time.Since(start).String()) + + // Validate the Merkle root + if g.targets.rewardsEvent != nil { + rootString := result.RewardsFile.GetMerkleRoot() + root := common.HexToHash(rootString) + if !bytes.Equal(root[:], g.targets.rewardsEvent.MerkleRoot[:]) { + g.log.Printlnf("WARNING: your Merkle tree had a root of %s, but the canonical Merkle tree's root was %s. This file will not be usable for claiming rewards.", root.Hex(), g.targets.rewardsEvent.MerkleRoot.Hex()) + } else { + g.log.Printlnf("Your Merkle tree's root of %s matches the canonical root! You will be able to use this file for claiming rewards.", rootString) + } + } + + err = g.writeFiles(result, votingPowerFile) + if err != nil { + return err + } + + return nil + +} + +// Create a rewards snapshot at the target block +func (g *treeGenerator) getSnapshotDetails() (*snapshotDetails, error) { + var err error + var opts bind.CallOpts + + endTime := g.slotToTime(g.targets.block.Slot) + + // Get the number of the EL block matching the CL snapshot block + var snapshotElBlockHeader *types.Header + if g.targets.block.ExecutionBlockNumber == 0 { + return nil, fmt.Errorf("slot %d was pre-merge", g.targets.block.Slot) + } + opts.BlockNumber = big.NewInt(0).SetUint64(g.targets.block.ExecutionBlockNumber) + snapshotElBlockHeader, err = g.rp.HeaderByNumber(context.Background(), opts.BlockNumber) + if err != nil { + return nil, fmt.Errorf("error getting EL block %d: %w", opts.BlockNumber.Uint64(), err) + } + + // Get the interval index + indexBig, err := g.rpNative.GetRewardIndex(&opts) + if err != nil { + return nil, fmt.Errorf("error getting current reward index: %w", err) + } + index := indexBig.Uint64() + + // Get the start slot + startSlot := uint64(0) + if index > 0 { + // Get the start slot for this interval + previousRewardsEvent, err := g.rp.GetRewardSnapshotEvent( + g.cfg.Smartnode.GetPreviousRewardsPoolAddresses(), + uint64(index-1), + nil, + ) + if err != nil { + return nil, fmt.Errorf("error getting event for interval %d: %w", index-1, err) + } + startSlot, err = getStartSlotForInterval(previousRewardsEvent, g.bn, g.beaconConfig) + if err != nil { + return nil, fmt.Errorf("error getting start slot for interval %d: %w", index, err) + } + } + + // Get the start time for the interval, and how long an interval is supposed to take + startTime, err := rewards.GetClaimIntervalTimeStart(g.rpNative, &opts) + if err != nil { + return nil, fmt.Errorf("error getting claim interval start time: %w", err) + } + intervalTime, err := rewards.GetClaimIntervalTime(g.rpNative, &opts) + if err != nil { + return nil, fmt.Errorf("error getting claim interval time: %w", err) + } + + // Calculate the intervals passed + blockTime := time.Unix(int64(snapshotElBlockHeader.Time), 0) + timeSinceStart := blockTime.Sub(startTime) + intervalsPassed := uint64(timeSinceStart / intervalTime) + + return &snapshotDetails{ + index: index, + startTime: startTime, + endTime: endTime, + startSlot: startSlot, + snapshotBeaconBlock: g.targets.block.Slot, + snapshotElBlockHeader: snapshotElBlockHeader, + intervalsPassed: intervalsPassed, + }, nil +} + +// Gets the start slot for the given interval +func getStartSlotForInterval(previousIntervalEvent rewards.RewardsEvent, bc beacon.Client, beaconConfig beacon.Eth2Config) (uint64, error) { + // Sanity check to confirm the BN can access the block from the previous interval + _, exists, err := bc.GetBeaconBlock(previousIntervalEvent.ConsensusBlock.String()) + if err != nil { + return 0, fmt.Errorf("error verifying block from previous interval: %w", err) + } + if !exists { + return 0, fmt.Errorf("couldn't retrieve CL block from previous interval (slot %d); this likely means you checkpoint sync'd your Beacon Node and it has not backfilled to the previous interval yet so it cannot be used for tree generation", previousIntervalEvent.ConsensusBlock.Uint64()) + } + + previousEpoch := previousIntervalEvent.ConsensusBlock.Uint64() / beaconConfig.SlotsPerEpoch + nextEpoch := previousEpoch + 1 + consensusStartBlock := nextEpoch * beaconConfig.SlotsPerEpoch + + // Get the first block that isn't missing + for { + _, exists, err := bc.GetBeaconBlock(fmt.Sprint(consensusStartBlock)) + if err != nil { + return 0, fmt.Errorf("error getting EL data for BC slot %d: %w", consensusStartBlock, err) + } + if !exists { + consensusStartBlock++ + } else { + break + } + } + + return consensusStartBlock, nil +} + +// Print information about the current network and interval info +func (g *treeGenerator) printNetworkInfo() error { + args, err := g.getTreegenArgs() + if err != nil { + return fmt.Errorf("error compiling treegen arguments: %w", err) + } + + // Generate the rewards file + generator, err := g.getGenerator(args) + if err != nil { + return err + } + + g.log.Println() + g.log.Println("=== Network Details ===") + g.log.Printlnf("Current index: %d", args.index) + g.log.Printlnf("Start Time: %s", args.startTime) + + // Find the event for the previous interval + if args.index > 0 { + rewardsEvent, err := g.rp.GetRewardSnapshotEvent( + g.cfg.Smartnode.GetPreviousRewardsPoolAddresses(), + args.index-1, + nil, + ) + if err != nil { + return fmt.Errorf("error getting rewards submission event for previous interval (%d): %w", args.index-1, err) + } + g.log.Printlnf("Start Beacon Slot: %d", rewardsEvent.ConsensusBlock.Uint64()+1) + g.log.Printlnf("Start EL Block: %d", rewardsEvent.ExecutionBlock.Uint64()+1) + } + + g.log.Printlnf("End Time: %s", args.endTime) + g.log.Printlnf("Snapshot Beacon Slot: %d", args.block.Slot) + g.log.Printlnf("Snapshot EL Block: %s", args.elBlockHeader.Number.String()) + g.log.Printlnf("Intervals Passed: %d", args.intervalsPassed) + g.log.Printlnf("Tree Ruleset: v%d", generator.GetGeneratorRulesetVersion()) + g.log.Printlnf("Approximator Ruleset: v%d", generator.GetApproximatorRulesetVersion()) + + return nil +} + +// Configure HTTP transport settings +func configureHTTP() { + + // The watchtower daemon makes a large number of concurrent RPC requests to the Eth1 client + // The HTTP transport is set to cache connections for future re-use equal to the maximum expected number of concurrent requests + // This prevents issues related to memory consumption and address allowance from repeatedly opening and closing connections + http.DefaultTransport.(*http.Transport).MaxIdleConnsPerHost = MaxConcurrentEth1Requests + +} diff --git a/treegen/treegen.sh b/treegen/treegen.sh new file mode 100755 index 000000000..10373549e --- /dev/null +++ b/treegen/treegen.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +docker run --rm -v $PWD/out:/out rocketpool/treegen /treegen -o /out "$@" diff --git a/treegen/voting-power.go b/treegen/voting-power.go new file mode 100644 index 000000000..0c37c657b --- /dev/null +++ b/treegen/voting-power.go @@ -0,0 +1,91 @@ +package main + +import ( + "math/big" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/rocket-pool/smartnode/shared/services/rewards" + "github.com/rocket-pool/smartnode/shared/services/state" + cfgtypes "github.com/rocket-pool/smartnode/shared/types/config" +) + +var ( + oneEth = big.NewInt(1e18) + _1_5_Eth = big.NewInt(15e17) +) + +type VotingPowerFile struct { + Network string `json:"network"` + Time time.Time `json:"time"` + ConsensusSlot uint64 `json:"consensusSlot"` + ExecutionBlock uint64 `json:"executionBlock"` + TotalPower *rewards.QuotedBigInt `json:"totalPower"` + NodePower map[common.Address]*rewards.QuotedBigInt `json:"nodePower"` +} + +func getNodeVotingPower(s *state.NetworkState, ethProvided *big.Int, nodeStake *big.Int) *big.Int { + rplPrice := s.NetworkDetails.RplPrice + + // No RPL staked means no voting power + if nodeStake.Sign() == 0 { + return big.NewInt(0) + } + + // First calculate the maximum rpl that can be used as input + // maxVotingRpl := (eligibleBondedEth * 1.5 Eth / RplPrice) + maxVotingRpl := big.NewInt(0) + maxVotingRpl.Mul(ethProvided, _1_5_Eth) + maxVotingRpl.Quo(maxVotingRpl, rplPrice) + + // Determine the voting RPL + // votingRpl := min(maxVotingRpl, nodeStake) + var votingRpl *big.Int + if maxVotingRpl.Cmp(nodeStake) <= 0 { + votingRpl = maxVotingRpl + } else { + votingRpl = nodeStake + } + + // Now take the square root + // Because the units are in wei, we need to multiply votingRpl by 1 Eth before square rooting. + votingPower := big.NewInt(0) + votingPower.Mul(votingRpl, oneEth) + votingPower.Sqrt(votingPower) + return votingPower + +} + +func (g *treeGenerator) GenerateVotingPower(s *state.NetworkState) *VotingPowerFile { + out := new(VotingPowerFile) + + out.Network = string(g.cfg.Smartnode.Network.Value.(cfgtypes.Network)) + out.ConsensusSlot = s.BeaconSlotNumber + out.ExecutionBlock = s.ElBlockNumber + out.TotalPower = rewards.NewQuotedBigInt(0) + out.NodePower = make(map[common.Address]*rewards.QuotedBigInt, len(s.NodeDetails)) + for _, node := range s.NodeDetails { + activeMinipoolCount := int64(0) + for _, mpd := range s.MinipoolDetailsByNode[node.NodeAddress] { + // Ignore finalised + if mpd.Finalised { + continue + } + + activeMinipoolCount += 1 + } + + // Get provided ETH (32 * minipoolCount - matched) + ethProvided := big.NewInt(activeMinipoolCount * 32) + ethProvided.Mul(ethProvided, oneEth) + ethProvided.Sub(ethProvided, node.EthMatched) + + // Calculate the Voting Power + nodeVotingPower := rewards.NewQuotedBigInt(0) + nodeVotingPower.Set(getNodeVotingPower(s, ethProvided, node.RplStake)) + out.TotalPower.Add(&out.TotalPower.Int, &nodeVotingPower.Int) + out.NodePower[node.NodeAddress] = nodeVotingPower + } + + return out +}