diff --git a/.travis.yml b/.travis.yml index 448a530..3f1c9b4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: scala scala: - - 2.12.8 + - 2.13.1 script: - - sbt compile - - sbt test \ No newline at end of file + - sbt +compile + - sbt +test \ No newline at end of file diff --git a/LICENSE b/LICENSE index 94a9ed0..1a9893b 100644 --- a/LICENSE +++ b/LICENSE @@ -1,674 +1,176 @@ - 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 -. + 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 diff --git a/README.md b/README.md index 6b7aefd..233daa9 100644 --- a/README.md +++ b/README.md @@ -1,98 +1,95 @@ ## eth-abi -[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.github.lbqds/eth-abi_2.12/badge.svg)](https://search.maven.org/artifact/com.github.lbqds/eth-abi_2.12/0.1/jar) +[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.github.lbqds/ethabi_2.13/badge.svg)](https://search.maven.org/artifact/com.github.lbqds/ethabi_2.13/0.3.0/jar) [![Build Status](https://travis-ci.com/Lbqds/eth-abi.svg?token=iUBC3d9KBxXjFrs9989Y&branch=master)](https://travis-ci.com/Lbqds/eth-abi) -generate scala code from solidity contract +jdk 11+ because of [http4s-jdk-http-client](https://github.com/http4s/http4s-jdk-http-client) -### Sonatype +`eth-abi` is currently available for scala 2.12 and scala2.13 + +### Getting Start to begin using `eth-abi`, add the following to your `build.sbt`: ```scala libraryDependencies ++= Seq( - "com.github.lbqds" %% "eth-abi" % "0.1", - "com.typesafe.akka" %% "akka-actor" % "2.5.19", - "com.typesafe.akka" %% "akka-stream" % "2.5.19" + "com.github.lbqds" %% "ethabi" % "0.3.0", + "org.typelevel" %% "cats-core" % "2.1.1", + "org.typelevel" %% "cats-effect" % "2.1.3" ) ``` -### codegen +### Code Generator + +`eth-abi` have a tool which can generate scala code by solidity contract abi and bin code. -download latest version `abi-codegen.tar.gz` from the [release](https://github.com/Lbqds/eth-abi/releases) page, then execute: +download latest version `abi-codegen-0.3.0` from the [release](https://github.com/Lbqds/eth-abi/releases) page, and execute: ```shell -$ tar -xf abi-codegen.tar.gz -$ scala abi-codegen.jar --help +$ abi-codegen-0.3.0 --help ``` -it will show usage as follow: - -```text -abi-codegen 0.1 -Usage: abi-codegen [options] +use [KVStore](https://github.com/Lbqds/eth-abi/blob/master/examples/src/main/resources/KVStore.abi) contract as an example, execute: - -a, --abi contract abi file - -b, --bin contract bin file - -p, --package package name e.g. "examples.token" - -c, --className class name - -o, --output output directory - -h, --help show usage +```shell +$ abi-codegen-0.3.0 gen -a KVStore.abi -b KVStore.bin -p "examples.kvstore" -c "KVStore" -o ./ ``` -a trivial example as follow: - -```solidity -pragma solidity ^0.4.24; -pragma experimental ABIEncoderV2; - -contract Trivial { - event TestEvent(uint256 indexed a, bytes b, uint256 indexed c, bytes d); - struct T { uint256 a; bytes b; uint256 c; bytes d; } - function Trivial() public {} - function trigger(T t) public { - emit TestEvent(t.a, t.b, t.c, t.d); - } -} -``` +would generate scala code at the current directory. you can also dive into generated code at [here](https://github.com/Lbqds/eth-abi/blob/master/examples/src/main/scala/examples/kvstore/KVStore.scala). -this contract use solidity [experimental ABIEncoderV2](https://solidity.readthedocs.io/en/latest/abi-spec.html#handling-tuple-types) feature, -when we call `trigger` method, it just emit a log, the generated code at [here](https://github.com/Lbqds/eth-abi/blob/master/examples/src/main/scala/examples/trivial/Trivial.scala). -now you can interact with ethereum use the generated scala code: +now we can call generated scala method instead of execute contract method by `eth_sendTransaction` or `eth_sendRawTransaction`: ```scala -implicit val system = ActorSystem() -implicit val materializer = ActorMaterializer() -import system.dispatcher - -// creator of contract -val sender = Address("0xe538b17ebf20efcf1c426cf1480e8a2a4b87cb1b") -val contract = new Trivial("ws://127.0.0.1:8546") -val opt = TransactionOpt(Some(BigInt(1000000)), Some(BigInt(10000)), None, None) - -contract.deploy(sender, opt) - -// waiting to contract deployed -while (!contract.isDeployed) { - Thread.sleep(1000) -} -println("deploy succeed, address is: " + contract.contractAddress) - -// subscribe TestEvent -contract.subscribeTestEvent.runForeach(println) - -val a = Uint256(BigInt(1000)) -val b = DynamicBytes(Array[Byte](0x01, 0x02, 0x03, 0x04)) -val c = Uint256(BigInt(3333)) -val d = DynamicBytes(Array[Byte](0x11, 0x22, 0x33, 0x44)) -val t = TupleType4[Uint256, DynamicBytes, Uint256, DynamicBytes](a, b, c, d) - -// call contract trigger method -contract.trigger(t, sender, opt) onComplete { - case Success(txHash) => - println("call trigger succeed: " + txHash) - system.terminate() - case Failure(exception) => println("call trigger failed: " + exception) +object Main extends IOApp { + + private def log(str: String): IO[Unit] = IO.delay(println(s"${Thread.currentThread.getName}, $str")) + + override def run(args: List[String]): IO[ExitCode] = { + val sender = Address("60f7947aef8bbc9bc314a9b8db8096099345fba3") + val transactionOpt = TransactionOpt(Some(400000), Some(1000), None, None) + val retryPolicy = retry.RetryPolicies.limitRetries[IO](5).join(RetryPolicies.constantDelay[IO](5 seconds)) + KVStore[IO]("ws://127.0.0.1:8546").use { kvStore => + val task = for { + client <- kvStore.client + peerCount <- client.peerCount.flatMap(_.get) + _ <- log(s"peer count: $peerCount") + cliVersion <- client.clientVersion.flatMap(_.get) + _ <- log(s"client version: $cliVersion") + work <- client.getWork.flatMap(_.get) + _ <- log(s"work response: $work") + protocolV <- client.protocolVersion.flatMap(_.get) + _ <- log(s"protocol version: $protocolV") + coinbase <- client.coinbase.flatMap(_.get) + _ <- log(s"coinbase address: $coinbase") + syncStatus <- client.syncing.flatMap(_.get) + _ <- log(s"sync status: $syncStatus") + deployHash <- kvStore.deploy(sender, transactionOpt) + address <- retryUntil[IO, Option[Address]]("wait contract deployed", retryPolicy, kvStore.address, _.isDefined).map(_.get) + _ <- log(s"contract deploy succeed, address: $address") + contractTx <- deployHash.get.flatMap(client.getTransactionByHash).flatMap(_.get) + _ <- log(s"contract deploy tx: ${contractTx.get}") + result <- kvStore.subscribeRecord + _ <- log(s"subscription id: ${result.id}") + fiber <- result.stream.forall { event => + println(event) + true + }.compile.drain.start + txHash <- kvStore.set(Uint16(12), DynamicBytes.from("0x010203040506070809"), sender, transactionOpt).flatMap(_.get) + receipt <- retryUntil[IO, Option[TransactionReceipt]]( + "wait tx receipt", + retryPolicy, + client.getTransactionReceipt(txHash).flatMap(_.get), + _.isDefined + ).map(_.get) + _ <- log(s"tx receipt: $receipt") + result <- kvStore.get(Uint16(12), sender, transactionOpt) + _ <- log(s"key: 12, value: $result") + _ <- fiber.cancel + _ <- log("quit now") + } yield () + task.handleErrorWith(exp => IO.delay(exp.printStackTrace())) *> IO.delay(ExitCode.Success) + } + } } ``` @@ -100,7 +97,7 @@ contract.trigger(t, sender, opt) onComplete { * the generated code use websocket client rather than http, because it will subscribe solidity event with [ethereum RPC PUB/SUB](https://github.com/ethereum/go-ethereum/wiki/RPC-PUB-SUB) * you need to assure the account have been unlocked before deploy and call contract method -* every `event` will have a generated `subscribeEventName` method, which just return `Source[EventValue, NotUsed]`, the subscription start only after have been [materialized](https://doc.akka.io/docs/akka/2.5.3/scala/stream/stream-flows-and-basics.html#defining-and-running-streams) +* every `event` will have a generated `subscribeEventName` method, which return `F[Stream[F, Event]]` ### ABIEncoderV2 @@ -113,25 +110,51 @@ use this feature heavily. `eth-abi` can also be used to interact directly with ethereum: ```scala -import ethabi.protocol.http.Client -import akka.actor.ActorSystem -import akka.stream.ActorMaterializer -import scala.util.{Failure, Success} - -implicit val system = ActorSystem() -implicit val materializer = ActorMaterializer() -import system.dispatcher - -val client = Client("http://127.0.0.1:8545") -client.blockNumber onComplete { - case Failure(exception) => throw exception - case Success(resp) => resp match { - case Left(responseError) => println(s"response error: ${responseError.message}") - case Right(number) => - println(s"current block number: $number") - system.terminate() +object HttpClientTest extends IOApp { + override def run(args: List[String]): IO[ExitCode] = { + import HttpClient._ + import scala.concurrent.duration._ + + apply[IO]("http://127.0.0.1:8545").use { client => + for { + cVersion <- client.clientVersion.flatMap(_.get) + _ <- IO.delay(println(s"client version: $cVersion")) + nVersion <- client.netVersion.flatMap(_.get) + _ <- IO.delay(println(s"net version: $nVersion")) + filterId <- client.newBlockFilter.flatMap(_.get) + _ <- IO.delay(println(s"filter id: $filterId")) + changes1 <- IO.sleep(3 seconds) *> client.getFilterChangeHashes(filterId).flatMap(_.get) + _ <- IO.delay(println(s"changes: $changes1")) + changes2 <- IO.sleep(3 seconds) *> client.getFilterChangeHashes(filterId).flatMap(_.get) + _ <- IO.delay(println(s"changes: $changes2")) + } yield ExitCode.Success + } } } ``` -all supported JSONRPC api list at [here](https://github.com/Lbqds/eth-abi/blob/master/src/main/scala/ethabi/protocol/Service.scala). +all supported JSONRPC api list at [here](https://github.com/Lbqds/eth-abi/blob/master/ethabi/src/main/scala/ethabi/protocol/Client.scala). + +### Functional Programming + +`eth-abi` use [cats](https://github.com/typelevel/cats) and [cats-effect](https://github.com/typelevel/cats-effect), +although the above example use `cats-effect` IO, you can also choose [ZIO](https://github.com/zio/zio) + +and all generated scala code are functional style. + +## License + +Copyright (c) 2020 Lbqds + +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/build.sbt b/build.sbt index 0f8f7c4..6fb04e3 100644 --- a/build.sbt +++ b/build.sbt @@ -2,26 +2,56 @@ import sbtassembly.AssemblyPlugin.defaultShellScript lazy val scala212 = "2.12.8" lazy val scala213 = "2.13.1" -lazy val ver = "0.2.0" +lazy val ethAbiVersion = "0.3.0" -val commonSettings = Seq( - organization := "com.github.lbqds", - crossScalaVersions := Seq(scala212, scala213), - version := ver, - scalacOptions ++= Seq( - "-encoding", "utf8", -// "-Xfatal-warnings", +def scalacOptionByVersion(version: String): Seq[String] = { + val optional: Seq[String] = + if (priorTo213(version)) Seq("-Ypartial-unification") + else Seq("-Ymacro-annotations") + + Seq( + "-encoding", + "utf8", + "-Xfatal-warnings", + "-Xlint", "-deprecation", -// "-unchecked", + "-unchecked", "-language:implicitConversions", "-language:higherKinds", "-language:existentials", -// "-Xlog-implicits", -// "-Xlog-implicit-conversions", - "-language:postfixOps"), + //"-Xlog-implicits", + //"-Xlog-implicit-conversions", + "-language:postfixOps") ++ optional +} + +def priorTo213(version: String): Boolean = { + CrossVersion.partialVersion(version) match { + case Some((2, minor)) if minor < 13 => true + case _ => false + } +} + +val commonSettings = Seq( + organization := "com.github.lbqds", + scalaVersion := scala213, + crossScalaVersions := Seq(scala212, scala213), + version := ethAbiVersion, + scalacOptions ++= scalacOptionByVersion(scalaVersion.value), test in assembly := {} ) +val macroSettings = Seq( + libraryDependencies ++= (Seq( + "org.scala-lang" % "scala-reflect" % scalaVersion.value % Provided, + "org.scala-lang" % "scala-compiler" % scalaVersion.value % Provided + ) ++ ( + if (priorTo213(scalaVersion.value)) Seq( + compilerPlugin("org.scalamacros" % "paradise" % "2.1.1").cross(CrossVersion.patch) + ) else Nil + )) + +) + import xerial.sbt.Sonatype._ val publishSettings = Seq( publishTo := sonatypePublishTo.value, @@ -43,12 +73,30 @@ val publishSettings = Seq( publishLocalConfiguration := publishLocalConfiguration.value.withOverwrite(true) ) +lazy val root = + Project(id = "root", base = file(".")) + .settings(commonSettings) + .settings(macroSettings) + .settings(name := "root") + .settings(publishSettings) + .aggregate(ethabi, codegen, examples) + .disablePlugins(sbtassembly.AssemblyPlugin) + lazy val ethabi = - Project(id = "eth-abi", base = file(".")) + Project(id = "ethabi", base = file("ethabi")) .settings(commonSettings) - .settings(name := "eth-abi") + .settings(macroSettings) + .settings(name := "ethabi") .settings(Dependencies.deps) .settings(publishSettings) + .settings( + unmanagedSourceDirectories in Compile += { + CrossVersion.partialVersion(scalaVersion.value) match { + case Some((2, n)) if n >= 13 => (scalaSource in Compile).value.getParentFile / "scala-2.13+" + case _ => (scalaSource in Compile).value.getParentFile / "scala-2.13-" + } + } + ) .enablePlugins(spray.boilerplate.BoilerplatePlugin) .disablePlugins(sbtassembly.AssemblyPlugin) @@ -60,14 +108,13 @@ lazy val codegen = .settings( name := "codegen", assemblyOption in assembly := (assemblyOption in assembly).value.copy(prependShellScript = Some(defaultShellScript)), - assemblyJarName := s"abi-codegen-$ver", + assemblyJarName := s"abi-codegen-$ethAbiVersion", skip in publish := true ) -lazy val example = +lazy val examples = Project(id = "examples", base = file("examples")) .settings(commonSettings) - .settings(Dependencies.examplesDpes) .dependsOn(ethabi) .disablePlugins(sbtassembly.AssemblyPlugin) .settings( diff --git a/codegen/src/main/scala/codegen/AbiDefinition.scala b/codegen/src/main/scala/codegen/AbiDefinition.scala index b2fa100..932c766 100644 --- a/codegen/src/main/scala/codegen/AbiDefinition.scala +++ b/codegen/src/main/scala/codegen/AbiDefinition.scala @@ -3,7 +3,7 @@ package codegen import io.circe.jawn.decode import io.circe.generic.auto._ import scala.meta._ -import ethabi.util.{Hash, Hex} +import ethabi.util._ // fallback function have no name and inputs final case class AbiDefinition(`type`: String, name: Option[String], inputs: Option[Seq[Param]], outputs: Option[Seq[Param]], @@ -67,16 +67,21 @@ final case class AbiDefinition(`type`: String, name: Option[String], inputs: Opt private [codegen] def genConstructor: Defn = { assert(isConstructor) val (args, encodeStat) = ctorArgsAndEncodeStat - val body = Term.Block(encodeStat.stats :+ q"impl.deploy(encoded, sender, opt)") - Defn.Def(List.empty, Term.Name("deploy"), List.empty, List(args :+ sender :+ opt), Some(Type.Name("Unit")), body) + q"""def deploy(..${args :+ sender :+ opt}): F[Deferred[F, Hash]] = { + ..${encodeStat.stats} + impl.deploy(CallArgs(encoded, sender, opt)) + }""" } private def callAndDecodeStat(retTpe: Option[Type]) = { if (retTpe.isDefined) { Term.Block(List( q""" - impl.call(encoded, sender, opt).map { bytes => - val result = ${decodeParams(retTpe.get, Term.Name("bytes"))} + for { + promise <- impl.call(CallArgs(encoded, sender, opt)) + data <- promise.get + } yield { + val result = ${decodeParams(retTpe.get, Term.Name("data"))} result._1 } """ @@ -91,38 +96,60 @@ final case class AbiDefinition(`type`: String, name: Option[String], inputs: Opt private def genConstantFunction(retTpe: Option[Type]): Defn = { val (args, encodeStat) = funcArgsAndEncodeStat val body = Term.Block(encodeStat.stats ++ callAndDecodeStat(retTpe).stats) - Defn.Def(List.empty, Term.Name(name.get), List.empty, List(args :+ sender :+ opt), retTpe.map(t => Type.Apply(Type.Name("Future"), List(t))), body) + val returnType = retTpe.fold[Type](t"F[Deferred[F, Array[Byte]]]")(t => t"F[$t]") + q""" + def ${Term.Name(name.get)}(..${args :+ sender :+ opt}): $returnType = { + ..${body.stats} + } + """ } - private def genTransactionFunction(retTpe: Option[Type]): Defn = { + private def genTransactionFunction(): Defn = { val (args, encodeStat) = funcArgsAndEncodeStat - val body = Term.Block(encodeStat.stats :+ q"impl.sendTransaction(encoded, sender, opt)") - Defn.Def(List.empty, Term.Name(name.get), List.empty, List(args :+ sender :+ opt), retTpe, body) + q""" + def ${Term.Name(name.get)}(..${args :+ sender :+ opt}): $defaultRetTpe = { + ..${encodeStat.stats} + impl.sendTransaction(CallArgs(encoded, sender, opt)) + } + """ } private [codegen] def genFunction: Defn = { assert(isFunction && name.isDefined) if (isConstant) genConstantFunction(returnType) - else genTransactionFunction(Some(defaultRetTpe)) + else genTransactionFunction() } // FIXME: only support indexed event first, then non-indexed event private [codegen] def genEventDecodeFunc: Defn.Def = { assert(isEvent && !isAnonymous && name.isDefined) - val typeInfosDecl = q"""var typeInfos = Seq.empty[TypeInfo[SolType]]""" - val indexedTypeInfos = inputs.map(_.filter(_.isIndexed).map(p => q"""typeInfos = typeInfos :+ implicitly[TypeInfo[${p.tpe}]]""")) - val nonIndexTypeInfo = inputs.flatMap { params => - val tpes = params.filter(!_.isIndexed).map(_.tpe).toList - if (tpes.nonEmpty) { - val tupleType = Type.Name(s"TupleType${tpes.length}") - Some(q"""typeInfos = typeInfos :+ implicitly[TypeInfo[${Type.Apply(tupleType, tpes)}]]""") - } else None + + var decls = Seq.empty[String] + def nextName: Term.Name = { + val name = Term.fresh("typeInfo") + decls = decls :+ name.value + name } - var stats: List[Stat] = List(typeInfosDecl) - if (indexedTypeInfos.isDefined) stats = stats ++ indexedTypeInfos.get - if (nonIndexTypeInfo.isDefined) stats = stats :+ nonIndexTypeInfo.get - stats = stats :+ q"""EventValue.decodeEvent(typeInfos, log)""" - Defn.Def(List.empty, Term.Name(s"decode${name.get.capitalize}"), List.empty, List(List(log)), Some(Type.Name("EventValue")), Term.Block(stats)) + + val (indexedTypes, nonIndexedTypes) = inputs.toList.flatten.partition(_.isIndexed) + val indexedTypeInfos = indexedTypes.filter(_.isIndexed).map(p => q"""val ${Pat.Var(nextName)} = TypeInfo[${p.tpe}]""") + val nonIndexTypeInfo = if (nonIndexedTypes.nonEmpty) { + val tupleType = Type.Name(s"TupleType${nonIndexedTypes.length}") + Some(q"""val ${Pat.Var(nextName)} = TypeInfo[${Type.Apply(tupleType, nonIndexedTypes.map(_.tpe))}]""") + } else None + + val typeInfoDecls: List[Stat] = indexedTypeInfos ++ nonIndexTypeInfo.toList + val declCodeString = s"""val typeInfos: List[TypeInfo[SolType]] = List${decls.mkString("(", ", ", ")")}""" + val typeInfoDecl: Stat = declCodeString.parse[Stat].get + val methodName = Term.Name(s"decode${name.get.capitalize}") + + q""" + private def $methodName($log): Event = { + ..$typeInfoDecls + $typeInfoDecl + Event.decode(typeInfos, log) + } + """ } private [codegen] def genSubscribeEventFunc: Defn.Def = { @@ -131,10 +158,11 @@ final case class AbiDefinition(`type`: String, name: Option[String], inputs: Opt val decodeFunc = Term.Name(s"decode${name.get.capitalize}") val topic = signatureHash.get.toString q""" - def $funcName: Source[EventValue, NotUsed] = { - val query = LogQuery.from(contractAddress, Hash(${Lit.String(topic)})) - impl.subscribeLogs(query).map($decodeFunc) - } + def $funcName: F[SubscriptionResult[F, Event]] = { + for { + result <- impl.subscribeLogs(Bytes32.from(${Lit.String(topic)})) + } yield SubscriptionResult[F, Event](result.id, result.stream.map($decodeFunc)) + } """ } @@ -142,7 +170,7 @@ final case class AbiDefinition(`type`: String, name: Option[String], inputs: Opt } object AbiDefinition { - private val defaultRetTpe = Type.Apply(Type.Name("Future"), List(Type.Name("Hash"))) + private val defaultRetTpe = t"F[Deferred[F, Hash]]" private val opt = Term.Param(List.empty, Term.Name("opt"), Some(Type.Name("TransactionOpt")), None) private val sender = Term.Param(List.empty, Term.Name("sender"), Some(Type.Name("Address")), None) private val log = Term.Param(List.empty, Term.Name("log"), Some(Type.Name("Log")), None) @@ -161,14 +189,12 @@ object AbiDefinition { val termName = Term.Name(name) val applyFunc = Term.Select(termName, Term.Name("apply")) val bundle = Term.Apply(Term.ApplyType(applyFunc, params.map(_.decltpe.get)), inputs) - val typeInfo = q"implicitly[TypeInfo[${Type.Apply(typeName, paramsTpe)}]]" - val encodeFunc = Term.Select(typeInfo, Term.Name("encode")) + val encodeFunc = Term.Select(q"TypeInfo[${Type.Apply(typeName, paramsTpe)}]", Term.Name("encode")) Term.Apply(encodeFunc, List(bundle)) } private def decodeParams(paramsTpe: Type, input: Term.Name): Term.Apply = { - val typeInfo = q"implicitly[TypeInfo[$paramsTpe]]" - val decodeFunc = Term.Select(typeInfo, Term.Name("decode")) + val decodeFunc = Term.Select(q"TypeInfo[$paramsTpe]", Term.Name("decode")) Term.Apply(decodeFunc, List(input, Lit.Int(0))) } diff --git a/codegen/src/main/scala/codegen/Codegen.scala b/codegen/src/main/scala/codegen/Codegen.scala index 841bc71..e0e97d3 100644 --- a/codegen/src/main/scala/codegen/Codegen.scala +++ b/codegen/src/main/scala/codegen/Codegen.scala @@ -1,58 +1,68 @@ package codegen -import scala.io.Source import io.circe.jawn.decode import io.circe.generic.auto._ import scala.meta._ +import scala.io.Source object Codegen { - private def genImpl: List[Stat] = List( - q"private val impl = Contract(endpoint)", - q"import impl.dispatcher", - q"def service = impl.service" - ) - private def genBinary(code: String): List[Stat] = List(q"""private val binary = ${Lit.String(code)}""") + private def genFunctions(abiDefinitions: Seq[AbiDefinition]): List[Stat] = abiDefinitions.filter(_.isFunction).map(_.genFunction).toList + private def genCtor(abiDefinitions: Seq[AbiDefinition]): List[Stat] = abiDefinitions.filter(_.isConstructor).map(_.genConstructor).toList + private def genEvent(abiDefinitions: Seq[AbiDefinition]): List[Stat] = abiDefinitions.filter(_.isEvent).flatMap(_.genEvent).toList - private def genSupMethods: List[Stat] = { - val isDeployed = q"def isDeployed: Boolean = impl.isDeployed" - val contractAddress = q"def contractAddress: Address = impl.address.get" - val loadFrom = q"def loadFrom(contractAddress: Address) = impl.load(contractAddress)" - List(isDeployed, contractAddress, loadFrom) - } - private def stats(abiFile: String, binFile: Option[String]) = { - val source = Source.fromFile(abiFile).getLines().mkString - val abiDefs = decode[Seq[AbiDefinition]](source).getOrElse(throw new RuntimeException("invalid abi format")) - val code = binFile.map(f => Source.fromFile(f).getLines().mkString) - genImpl ::: genBinary(code.getOrElse("")) ::: genFunctions(abiDefs) ::: genCtor(abiDefs) ::: genEvent(abiDefs) ::: genSupMethods - } + private[codegen] def codeGen(abiFile: String, binFile: Option[String], packages: List[String], className: String): Pkg = { + + val abiFileHandler = Source.fromFile(abiFile) + val abiDefs = decode[Seq[AbiDefinition]](abiFileHandler.getLines.mkString).getOrElse(throw new RuntimeException("invalid abi format")) + val binFileHandler = binFile.map(f => Source.fromFile(f)) + val binCodeStr = binFileHandler.map(_.getLines.mkString).getOrElse("") + + abiFileHandler.close() + binFileHandler.foreach(_.close()) - def codeGen(abiFile: String, binFile: Option[String], packages: List[String], className: String): Pkg = { - val contents = stats(abiFile, binFile) - val template = Template(List.empty, List.empty, Self(Term.Name("self"), None), contents) - val primary = Ctor.Primary(List.empty, Term.Name(className), List(List(Term.Param(List.empty, Term.Name("endpoint"), Some(Type.Name("String")), None)))) - val classDef = Defn.Class(List(Mod.Final()), Type.Name(className), List.empty, primary, template) + val contents = genFunctions(abiDefs) ::: genCtor(abiDefs) ::: genEvent(abiDefs) val selector: (Term.Ref, Term.Name) => Term.Ref = (p, c) => Term.Select(p, c) val packagesDef = packages.map(pkg => Term.Name(pkg)).reduceLeft(selector) + val typeName = Type.Name(className) + val termName = Term.Name(className) + q""" package $packagesDef { - import akka.NotUsed - import akka.stream.scaladsl.Source - import ethabi.util.{Hex, Hash} + import ethabi.util._ import ethabi.types._ import ethabi.types.generated._ - import ethabi.protocol.{Contract, EventValue} + import ethabi.protocol._ import ethabi.protocol.Request._ import ethabi.protocol.Response.Log - import scala.concurrent.Future + import ethabi.protocol.Subscription.SubscriptionResult + import cats.implicits._ + import cats.Applicative + import cats.effect._ + import cats.effect.concurrent._ + + final class $typeName[F[_]: ConcurrentEffect: Timer] private (private val impl: Contract[F]) { self => + private val binary = ${Lit.String(binCodeStr)} + + def client: F[Client[F]] = impl.client + def subscriber: F[Subscriber[F]] = impl.subscriber + def isDeployed: F[Boolean] = impl.isDeployed + def address: F[Option[Address]] = impl.address + def loadFrom(address: Address): F[Unit] = impl.load(address) + ..$contents + } - $classDef + object $termName { + def apply[F[_]: ConcurrentEffect: Timer](endpoint: String)(implicit CS: ContextShift[F]): Resource[F, $typeName[F]] = { + Contract[F](endpoint).flatMap(impl => Resource.liftF(Applicative[F].pure(new $typeName(impl)))) + } + } } """ } diff --git a/codegen/src/main/scala/codegen/Main.scala b/codegen/src/main/scala/codegen/Main.scala index a291023..e27e5fa 100644 --- a/codegen/src/main/scala/codegen/Main.scala +++ b/codegen/src/main/scala/codegen/Main.scala @@ -3,6 +3,8 @@ package codegen import ethabi.util import scopt.OParser +import scala.util.control.NonFatal + object Main extends App { final case class Params(interactive: Boolean = false, abiFile: String = "", binFile: Option[String] = None, packages: String = "", className: String = "", output: String = "") @@ -10,8 +12,8 @@ object Main extends App { val cmdParser = { import builder._ OParser.sequence( - programName("abi-codegen"), - head("abi-codegen", "0.2.0"), + programName("abi-codegen-0.3.0"), + head("abi-codegen-0.3.0"), opt[Unit]('i', "interactive") .optional() .action((_, params) => params.copy(interactive = true)) @@ -50,11 +52,15 @@ object Main extends App { } def generate(params: Params): Unit = { - val header = "// AUTO GENERATED, DO NOT EDIT\n\n" - val code = Codegen.codeGen(params.abiFile, params.binFile, params.packages.split('.').toList, params.className) - val path = params.output + "/" + params.packages.split('.').mkString("/") - val fileName = s"${params.className}.scala" - util.writeToFile(path, fileName, header + code.syntax) + try { + val header = "// AUTO GENERATED, DO NOT EDIT\n\n" + val code = Codegen.codeGen(params.abiFile, params.binFile, params.packages.split('.').toList, params.className) + val path = params.output + "/" + params.packages.split('.').mkString("/") + val fileName = s"${params.className}.scala" + util.writeToFile(path, fileName, header + code.syntax) + } catch { + case NonFatal(exp) => println(s"ERROR: $exp") + } } OParser.parse(cmdParser, args, Params()) match { diff --git a/codegen/src/test/scala/codegen/AbiDefinitionSpec.scala b/codegen/src/test/scala/codegen/AbiDefinitionSpec.scala index 4705dab..14ad634 100644 --- a/codegen/src/test/scala/codegen/AbiDefinitionSpec.scala +++ b/codegen/src/test/scala/codegen/AbiDefinitionSpec.scala @@ -1,9 +1,10 @@ package codegen -import org.scalatest.{WordSpec, Matchers} +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpec import ethabi.util.Hex -class AbiDefinitionSpec extends WordSpec with Matchers { +class AbiDefinitionSpec extends AnyWordSpec with Matchers { "test gen const function" in { val json = """ diff --git a/codegen/src/test/scala/codegen/ParamSpec.scala b/codegen/src/test/scala/codegen/ParamSpec.scala index bafcdd7..e40fd1c 100644 --- a/codegen/src/test/scala/codegen/ParamSpec.scala +++ b/codegen/src/test/scala/codegen/ParamSpec.scala @@ -1,14 +1,15 @@ package codegen -import org.scalatest.{WordSpec, Matchers} +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpec import io.circe.jawn.decode import io.circe.generic.auto._ import io.circe.syntax._ -class ParamSpec extends WordSpec with Matchers { +class ParamSpec extends AnyWordSpec with Matchers { "test deserialize argument(without components)" in { val json = """{"name":"balance","type":"uint256"}""" - val argument = decode[Param](json).right.get + val argument = decode[Param](json).getOrElse(null) argument.name shouldBe "balance" argument.`type` shouldBe "uint256" argument.components shouldBe None @@ -23,7 +24,7 @@ class ParamSpec extends WordSpec with Matchers { "test deserialize argument(with components)" in { val json = """{"name":"account","type":"tuple","components":[{"name":"name","type":"string"}],"indexed":null}""" - val argument = decode[Param](json).right.get + val argument = decode[Param](json).getOrElse(null) argument.name shouldBe "account" argument.`type` shouldBe "tuple" argument.components.get.head.name shouldBe "name" @@ -69,7 +70,7 @@ class ParamSpec extends WordSpec with Matchers { | ] |} """.stripMargin - val result = decode[Param](json).right.get + val result = decode[Param](json).getOrElse(null) val components = result.components.get components.length shouldBe 3 val nested = components(2) diff --git a/src/main/boilerplate/ethabi/types/generated/TupleTypes.scala.template b/ethabi/src/main/boilerplate/ethabi/types/generated/TupleTypes.scala.template similarity index 93% rename from src/main/boilerplate/ethabi/types/generated/TupleTypes.scala.template rename to ethabi/src/main/boilerplate/ethabi/types/generated/TupleTypes.scala.template index 608137b..18d97fb 100644 --- a/src/main/boilerplate/ethabi/types/generated/TupleTypes.scala.template +++ b/ethabi/src/main/boilerplate/ethabi/types/generated/TupleTypes.scala.template @@ -3,9 +3,6 @@ package ethabi package types package generated -import scala.collection.mutable -import scala.language.implicitConversions - /* object TupleType0 extends SolType { def apply() = TupleType0 @@ -44,7 +41,6 @@ object TupleType0 extends SolType { override def decode(bytes: Array[Byte], position: Int): (TupleType1[[#T1#]], Int) = { val typeInfos = List([#typeInfo1#]) - var index, staticOffset, totalConsumed = ##0 val (results, consumed) = TupleType.decode(bytes, position, typeInfos) (TupleType1[[#T1#]]([#results(0).asInstanceOf[T1]#]), consumed) } diff --git a/ethabi/src/main/scala-2.13+/ethabi/compat.scala b/ethabi/src/main/scala-2.13+/ethabi/compat.scala new file mode 100644 index 0000000..2183173 --- /dev/null +++ b/ethabi/src/main/scala-2.13+/ethabi/compat.scala @@ -0,0 +1,7 @@ +package ethabi + +object compat { + private[ethabi] def mapValues[K, V, R](map: Map[K, V])(f: V => R): Map[K, R] = { + map.view.mapValues(f).toMap + } +} diff --git a/ethabi/src/main/scala-2.13-/ethabi/compat.scala b/ethabi/src/main/scala-2.13-/ethabi/compat.scala new file mode 100644 index 0000000..c040776 --- /dev/null +++ b/ethabi/src/main/scala-2.13-/ethabi/compat.scala @@ -0,0 +1,7 @@ +package ethabi + +object compat { + private[ethabi] def mapValues[K, V, R](map: Map[K, V])(f: V => R): Map[K, R] = { + map.mapValues(f) + } +} diff --git a/ethabi/src/main/scala/ethabi/implicits/package.scala b/ethabi/src/main/scala/ethabi/implicits/package.scala new file mode 100644 index 0000000..d27e7f2 --- /dev/null +++ b/ethabi/src/main/scala/ethabi/implicits/package.scala @@ -0,0 +1,74 @@ +package ethabi + +import io.circe.Decoder +import io.circe.Encoder +import io.circe.Json +import io.circe.HCursor +import ethabi.util._ +import ethabi.types.Address +import ethabi.types.generated.Bytes32 + +package object implicits { + implicit val hashDecoder: Decoder[Hash] = (c: HCursor) => { + c.value.as[String].map(Hash.apply) + } + + implicit val bytesDecoder: Decoder[Array[Byte]] = (c: HCursor) => { + c.value.as[String].map(Hex.hex2Bytes) + } + + implicit val bytes32Decoder: Decoder[Bytes32] = (c: HCursor) => { + c.value.as[String].map(Bytes32.from) + } + + // decode hex string to int e.g. "0x10" => 16 + implicit val intDecoder: Decoder[Int] = (c: HCursor) => { + c.value.as[String].map(Hex.hex2Int) + } + + implicit val longDecoder: Decoder[Long] = (c: HCursor) => { + c.value.as[String].map(Hex.hex2Long) + } + + implicit val addressDecoder: Decoder[Address] = (c: HCursor) => { + c.value.as[String].map(Address.from) + } + + implicit val bigIntDecoder: Decoder[BigInt] = (c: HCursor) => { + c.value.as[String].map(Hex.hex2BigInt) + } + + implicit val addrListDecoder: Decoder[List[Address]] = (c: HCursor) => { + c.value.as[List[String]].map(_.map(Address.from)) + } + + implicit val hashListDecoder: Decoder[List[Hash]] = (c: HCursor) => { + c.value.as[List[String]].map(_.map(Hash.apply)) + } + + implicit val addressEncoder: Encoder[Address] = (addr: Address) => Json.fromString(addr.toString) + + implicit val hashEncoder: Encoder[Hash] = (h: Hash) => Json.fromString(h.toString) + + implicit val bytesEncoder: Encoder[Array[Byte]] = (b: Array[Byte]) => + Json.fromString(Hex.bytes2Hex(b, withPrefix = true)) + + implicit val bytes32Encoder: Encoder[Bytes32] = (b: Bytes32) => + Json.fromString(Hex.bytes2Hex(b.value, withPrefix = true)) + + implicit val bigIntEncoder: Encoder[BigInt] = (v: BigInt) => Json.fromString(Hex.bigInt2Hex(v, withPrefix = true)) + + implicit val intEncoder: Encoder[Int] = (v: Int) => Json.fromString(Hex.int2Hex(v, withPrefix = true)) + + implicit val longEncoder: Encoder[Long] = (v: Long) => Json.fromString(Hex.long2Hex(v, withPrefix = true)) + + implicit val listAddrEncoder: Encoder[List[Address]] = (addrs: List[Address]) => + Json.fromValues(addrs.map(addr => Json.fromString(addr.toString))) + + implicit val topicEncoder: Encoder[Either[Option[Bytes32], List[Bytes32]]] = { + case Left(v) => encode(v) + case Right(v) => encode(v) + } + + implicit def encode[T: Encoder](value: T): Json = Encoder[T].apply(value) +} diff --git a/ethabi/src/main/scala/ethabi/protocol/Client.scala b/ethabi/src/main/scala/ethabi/protocol/Client.scala new file mode 100644 index 0000000..52ba011 --- /dev/null +++ b/ethabi/src/main/scala/ethabi/protocol/Client.scala @@ -0,0 +1,433 @@ +package ethabi +package protocol + +import cats.effect.concurrent.Deferred +import io.circe.Decoder +import ethabi.util.Hash +import ethabi.implicits._ +import ethabi.types.Address +import Request._ + +trait Client[F[_]] { + + /** + * send request to ethereum jsonrpc server + * + * @param request refer to [[Request]] + * @tparam R result type for this request + * @return a computation with effect type F + */ + def doRequest[R: Decoder](request: Request): F[Deferred[F, R]] + + /** + * @return ethereum client version, refer to https://eth.wiki/json-rpc/API#web3_clientversion + */ + final def clientVersion: F[Deferred[F, String]] = doRequest[String](Request.clientVersion()) + + /** + * compute sha3 hash for bytes, refer to https://eth.wiki/json-rpc/API#web3_sha3 + * + * @param data the data to convert into sha3 hash + * @return sha3 hash of giving data + */ + final def sha3(data: Array[Byte]): F[Deferred[F, Hash]] = doRequest[Hash](Request.sha3(data)) + + /** + * @return current network id, refer to https://eth.wiki/json-rpc/API#net_version + */ + final def netVersion: F[Deferred[F, String]] = doRequest[String](Request.netVersion()) + + /** + * @return number of peers currently connected to the client, refer to https://eth.wiki/json-rpc/API#net_peercount + */ + final def peerCount: F[Deferred[F, Int]] = doRequest[Int](Request.netPeerCount()) + + /** + * @return the current ethereum protocol version, refer to https://eth.wiki/json-rpc/API#eth_protocolversion + */ + final def protocolVersion: F[Deferred[F, String]] = doRequest[String](Request.protocolVersion()) + + /** + * @return [[Response.Syncing]] if is syncing, otherwise false, refer to https://eth.wiki/json-rpc/API#eth_syncing + */ + final def syncing: F[Deferred[F, Either[Boolean, Response.Syncing]]] = + doRequest[Either[Boolean, Response.Syncing]](Request.syncing())(Decoder[Boolean].either(Decoder[Response.Syncing])) + + /** + * @return client coinbase address, refer to https://eth.wiki/json-rpc/API#eth_coinbase + */ + final def coinbase: F[Deferred[F, Address]] = doRequest[Address](Request.coinbase()) + + /** + * @return if client is actively mining, refer to https://eth.wiki/json-rpc/API#eth_mining + */ + final def mining: F[Deferred[F, Boolean]] = doRequest[Boolean](Request.mining()) + + /** + * @return number of hash per second, refer to https://eth.wiki/json-rpc/API#eth_hashrate + */ + final def hashrate: F[Deferred[F, Int]] = doRequest[Int](Request.hashrate()) + + /** + * @return current gas price in wei, refer to https://eth.wiki/json-rpc/API#eth_gasPrice + */ + final def gasPrice: F[Deferred[F, BigInt]] = doRequest[BigInt](Request.gasPrice()) + + /** + * @return account list owned by client, refer to https://eth.wiki/json-rpc/API#eth_accounts + */ + final def accounts: F[Deferred[F, List[Address]]] = doRequest[List[Address]](Request.accounts()) + + /** + * @return current block number, refer to https://eth.wiki/json-rpc/API#eth_blockNumber + */ + final def blockNumber: F[Deferred[F, Long]] = doRequest[Long](Request.blockNumber()) + + /** + * @param address account address + * @param blockTag refer to [[Request.BlockTag]] + * @return account balance in wei, refer to https://eth.wiki/json-rpc/API#eth_getBalance + */ + final def getBalance(address: Address, blockTag: BlockTag = Latest): F[Deferred[F, BigInt]] = doRequest[BigInt](Request.balance(address, blockTag)) + + final def getBalance(address: Address, height: Long): F[Deferred[F, BigInt]] = getBalance(address, BlockNumber(height)) + + /** + * @param address address of the storage e.g. contract address + * @param position index of the position in the storage + * @param blockTag refer to [[Request.BlockTag]] + * @return data in the storage position, refer to [[https://eth.wiki/json-rpc/API#eth_getStorageAt]] + */ + final def getStorageAt(address: Address, position: Int, blockTag: BlockTag = Latest): F[Deferred[F, Array[Byte]]] = + doRequest[Array[Byte]](Request.storageAt(address, position, blockTag)) + + final def getStorageAt(address: Address, position: Int, height: Long): F[Deferred[F, Array[Byte]]] = + getStorageAt(address, position, BlockNumber(height)) + + /** + * @param address account address + * @param blockTag refer to [[Request.BlockTag]] + * @return number of transaction send by `address`, refer to https://eth.wiki/json-rpc/API#eth_getTransactionCount + */ + final def getTransactionCount(address: Address, blockTag: BlockTag = Latest): F[Deferred[F, Int]] = + doRequest[Int](Request.transactionCount(address, blockTag)) + + final def getTransactionCount(address: Address, height: Long): F[Deferred[F, Int]] = getTransactionCount(address, BlockNumber(height)) + + /** + * @param hash block hash + * @return number of transactions in this block, refer to https://eth.wiki/json-rpc/API#eth_getBlockTransactionCountByHash + */ + final def getBlockTransactionCountByHash(hash: Hash): F[Deferred[F, Int]] = doRequest[Int](Request.blockTransactionCountByHash(hash)) + + /** + * @param blockTag refer to [[Request.BlockTag]] + * @return number of transactions in this block, refer to https://eth.wiki/json-rpc/API#eth_getBlockTransactionCountsByNumber + */ + final def getBlockTransactionCountByNumber(blockTag: BlockTag = Latest): F[Deferred[F, Int]] = doRequest[Int](Request.blockTransactionCountByNumber(blockTag)) + + final def getBlockTransactionCountByNumber(height: Long): F[Deferred[F, Int]] = getBlockTransactionCountByNumber(BlockNumber(height)) + + /** + * @param hash block hash + * @return number of uncles in this block, refer to https://eth.wiki/json-rpc/API#eth_getUncleCountByBlockHash + */ + final def getUncleCountByBlockHash(hash: Hash): F[Deferred[F, Int]] = doRequest[Int](Request.uncleCountByHash(hash)) + + /** + * @param blockTag refer to [[Request.BlockTag]] + * @return number of uncles in this block, refer to https://eth.wiki/json-rpc/API#eth_getUncleCountByBlockNumber + */ + final def getUncleCountByBlockNumber(blockTag: BlockTag = Latest): F[Deferred[F, Int]] = doRequest[Int](Request.uncleCountByNumber(blockTag)) + + final def getUncleCountByBlockNumber(height: Long): F[Deferred[F, Int]] = getUncleCountByBlockNumber(BlockNumber(height)) + + /** + * @param address contract address + * @param blockTag refer to [[Request.BlockTag]] + * @return contract code, refer to https://eth.wiki/json-rpc/API#eth_getCode + */ + final def getCode(address: Address, blockTag: BlockTag = Latest): F[Deferred[F, Array[Byte]]] = doRequest[Array[Byte]](Request.code(address, blockTag)) + + final def getCode(address: Address, height: Long): F[Deferred[F, Array[Byte]]] = getCode(address, BlockNumber(height)) + + /** + * calculate signature for `data` with account private key + * + * @param address account address + * @param data data to sign + * @return signature of data, refer to https://eth.wiki/json-rpc/API#eth_sign + * @note account MUST be unlocked + */ + final def sign(address: Address, data: Array[Byte]): F[Deferred[F, Array[Byte]]] = doRequest[Array[Byte]](Request.sign(address, data)) + + /** + * sign a unsigned transaction that can be submit to network with [[sendRawTransaction]] + * + * @param tx unsigned transaction + * @return signed transaction, refer to https://eth.wiki/json-rpc/API#eth_signTransaction + * @note account MUST be unlocked + */ + final def signTransaction(tx: Transaction): F[Deferred[F, Array[Byte]]] = doRequest[Array[Byte]](Request.signTransaction(tx)) + + /** + * @param transaction unsigned transaction + * @return transaction hash, refer to https://eth.wiki/json-rpc/API#eth_sendTransaction + * @note account MUST be unlocked + */ + final def sendTransaction(transaction: Transaction): F[Deferred[F, Hash]] = doRequest[Hash](Request.sendTransaction(transaction)) + + /** + * @param data signed transaction data + * @return transaction hash, refer to https://eth.wiki/json-rpc/API#eth_sendRawTransaction + */ + final def sendRawTransaction(data: Array[Byte]): F[Deferred[F, Hash]] = doRequest[Hash](Request.sendRawTransaction(data)) + + /** + * execute contract method without create a transaction, therefore won't change world state + * + * @param callData same as [[Request.Transaction]] except for nonce + * @param blockTag refer to [[Request.BlockTag]] + * @return result of executed contract, https://eth.wiki/json-rpc/API#eth_call + */ + final def call(callData: Transaction, blockTag: BlockTag = Latest): F[Deferred[F, Array[Byte]]] = doRequest[Array[Byte]](Request.call(callData, blockTag)) + + final def call(callData: Transaction, height: Long): F[Deferred[F, Array[Byte]]] = call(callData, BlockNumber(height)) + + /** + * returns an estimate of how much gas is necessary to allow the transaction to complete + * + * @param callData same with Client.call + * @param blockTag refer to [[Request.BlockTag]] + * @return the estimate amount of gas, refer to https://eth.wiki/json-rpc/API#eth_estimateGas + */ + final def estimateGas(callData: Transaction, blockTag: BlockTag = Latest): F[Deferred[F, Long]] = doRequest[Long](Request.estimateGas(callData, blockTag)) + + final def estimateGas(callData: Transaction, height: Long): F[Deferred[F, Long]] = estimateGas(callData, BlockNumber(height)) + + /** + * return information about a block by hash + * + * @param hash block hash + * @return [[Response.Block]], refer to https://eth.wiki/json-rpc/API#eth_getBlockByHash + */ + final def getBlockByHash(hash: Hash): F[Deferred[F, Option[Response.Block]]] = + doRequest[Option[Response.Block]](Request.blockByHash(hash)) + + /** + * return information about a block by hash + * + * @param hash block hash + * @return [[Response.BlockWithTransactions]], refer to https://eth.wiki/json-rpc/API#eth_getBlockByHash + */ + final def getBlockByHashWithTransactions(hash: Hash): F[Deferred[F, Option[Response.BlockWithTransactions]]] = + doRequest[Option[Response.BlockWithTransactions]](Request.blockByHash(hash, detail = true)) + + /** + * return information about block by height + * + * @param blockTag refer to [[Request.BlockTag]] + * @return [[Response.Block]], refer to https://eth.wiki/json-rpc/API#eth_getBlockByNumber + */ + final def getBlockByNumber(blockTag: BlockTag = Latest): F[Deferred[F, Option[Response.Block]]] = + doRequest[Option[Response.Block]](Request.blockByNumber(blockTag)) + + /** + * return information about block by height + * + * @param height block height + * @return [[Response.Block]], refer to https://eth.wiki/json-rpc/API#eth_getBlockByNumber + */ + final def getBlockByNumber(height: Long): F[Deferred[F, Option[Response.Block]]] = + getBlockByNumber(BlockNumber(height)) + + /** + * return information about block by height + * + * @param blockTag refer to [[Request.BlockTag]] + * @return [[Response.Block]], refer to https://eth.wiki/json-rpc/API#eth_getBlockByNumber + */ + final def getBlockByNumberWithTransactions(blockTag: BlockTag = Latest): F[Deferred[F, Option[Response.BlockWithTransactions]]] = + doRequest[Option[Response.BlockWithTransactions]](Request.blockByNumber(blockTag, detail = true)) + + /** + * return information about block by height + * + * @param height block height + * @return [[Response.BlockWithTransactions]], refer to https://eth.wiki/json-rpc/API#eth_getBlockByNumber + */ + final def getBlockByNumberWithTransactions(height: Long): F[Deferred[F, Option[Response.BlockWithTransactions]]] = + getBlockByNumberWithTransactions(BlockNumber(height)) + + /** + * returns the information about a transaction requested by transaction hash + * + * @param hash transaction hash + * @return [[Response.Transaction]], refer to https://eth.wiki/json-rpc/API#eth_getTransactionByHash + */ + final def getTransactionByHash(hash: Hash): F[Deferred[F, Option[Response.Transaction]]] = + doRequest(Request.transactionByHash(hash)) + + /** + * returns information about a transaction by block hash and transaction index position + * + * @param hash block hash + * @param index transaction index in block + * @return [[Response.Transaction]], refer to https://eth.wiki/json-rpc/API#eth_getTransactionBlockHashAndIndex + */ + final def getTransactionByBlockHashAndIndex(hash: Hash, index: Int): F[Deferred[F, Option[Response.Transaction]]] = + doRequest(Request.transactionByBlockHashAndIndex(hash, index)) + + /** + * returns information about a transaction by block number and transaction index position + * + * @param blockTag refer to [[Request.BlockTag]] + * @param index transaction index in block + * @return [[Response.Transaction]], refer to https://eth.wiki/json-rpc/API#eth_getTransactionByBlockNumberAndIndex + */ + final def getTransactionByBlockNumberAndIndex(blockTag: BlockTag, index: Int): F[Deferred[F, Option[Response.Transaction]]] = + doRequest(Request.transactionByBlockNumberAndIndex(blockTag, index)) + + /** + * returns information about a transaction by block number and transaction index position + * + * @param height block number + * @param index transaction index in block + * @return [[Response.Transaction]], refer to https://eth.wiki/json-rpc/API#eth_getTransactionByBlockNumberAndIndex + */ + final def getTransactionByBlockNumberAndIndex(height: Long, index: Int): F[Deferred[F, Option[Response.Transaction]]] = + doRequest(Request.transactionByBlockNumberAndIndex(BlockNumber(height), index)) + + /** + * returns the receipt of a transaction by transaction hash + * + * @param txHash transaction hash + * @return [[Response.TransactionReceipt]], refer to https://eth.wiki/json-rpc/API#eth_getTransactionReceipt + * @note receipt is not available for pending transaction + */ + final def getTransactionReceipt(txHash: Hash): F[Deferred[F, Option[Response.TransactionReceipt]]] = + doRequest[Option[Response.TransactionReceipt]](Request.transactionReceipt(txHash)) + + /** + * returns information about a uncle of a block by hash and uncle index position + * + * @param hash block hash + * @param index uncle index in block + * @return [[Response.Header]], refer to https://eth.wiki/json-rpc/API#eth_getUncleByBlockHashAndIndex + */ + final def getUncleByBlockHashAndIndex(hash: Hash, index: Int): F[Deferred[F, Option[Response.Header]]] = + doRequest[Option[Response.Header]](Request.uncleByBlockHashAndIndex(hash, index)) + + /** + * returns information about a uncle of a block by hash and uncle index position + * + * @param blockTag refer to [[Request.BlockTag]] + * @param index uncle index in block + * @return [[Response.Header]], refer to https://eth.wiki/json-rpc/API#eth_getUncleByBlockNumberAndIndex + */ + final def getUncleByBlockNumberAndIndex(blockTag: BlockTag, index: Int): F[Deferred[F, Option[Response.Header]]] = + doRequest[Option[Response.Header]](Request.uncleByBlockNumberAndIndex(blockTag, index)) + + /** + * returns information about a uncle of a block by hash and uncle index position + * + * @param height block number + * @param index uncle index in block + * @return [[Response.Header]], refer to https://eth.wiki/json-rpc/API#eth_getUncleByBlockNumberAndIndex + */ + final def getUncleByBlockNumberAndIndex(height: Long, index: Int): F[Deferred[F, Option[Response.Header]]] = + getUncleByBlockNumberAndIndex(BlockNumber(height), index) + + /** + * creates a filter object, based on filter options, to notify when the state changes + * + * @param filter refer to [[Request.LogFilter]] + * @return filter id, which can be used with eth_getFilterChanges, refer to https://eth.wiki/json-rpc/API#eth_newFilter + */ + final def newFilter(filter: LogFilter): F[Deferred[F, Response.FilterId]] = doRequest[Response.FilterId](Request.newFilter(filter)) + + /** + * creates a filter in the node, to notify when a new block arrives + * + * @return filter id, which can be used with eth_getFilterChanges, refer to https://eth.wiki/json-rpc/API#eth_newBlockFilter + */ + final def newBlockFilter: F[Deferred[F, Response.FilterId]] = doRequest[Response.FilterId](Request.newBlockFilter()) + + /** + * creates a filter in the node, to notify when new pending transactions arrive + * + * @return filter id, which can be used with eth_getFilterChanges, refer to https://eth.wiki/json-rpc/API#eth_newPendingTransactionFilter + */ + final def newPendingTransactionFilter: F[Deferred[F, Response.FilterId]] = doRequest[Response.FilterId](Request.newPendingTransactionFilter()) + + /** + * uninstalls a filter with given id + * + * @param filterId filter id which return by [[newFilter]], [[newBlockFilter]] and [[newPendingTransactionFilter]] + * @return true if succeed, false otherwise, refer to https://eth.wiki/json-rpc/API#eth_uninstallFilter + */ + final def uninstallFilter(filterId: Response.FilterId): F[Deferred[F, Boolean]] = doRequest[Boolean](Request.uninstallFilter(filterId)) + + /** + * refer to [[getFilterChanges]] + * @note `filterId` MUST be returned by [[newBlockFilter]] or [[newPendingTransactionFilter]] + */ + final def getFilterChangeHashes(filterId: Response.FilterId): F[Deferred[F, List[Hash]]] = + doRequest[List[Hash]](Request.filterChanges(filterId)) + + /** + * polling method for a filter, which returns an array of logs which occurred since last poll. + * + * @param filterId filter id which return by [[newFilter]], [[newBlockFilter]] and [[newPendingTransactionFilter]] + * @return [[Response.Log]], refer to https://eth.wiki/json-rpc/API#eth_getFilterChanges + */ + final def getFilterChanges(filterId: Response.FilterId): F[Deferred[F, List[Response.Log]]] = + doRequest[List[Response.Log]](Request.filterChanges(filterId)) + + /** + * refer to [[getFilterLogs]] + * @note `fliterId` MUST be returned by [[newBlockFilter]] or [[newPendingTransactionFilter]] + */ + final def getFilterLogHashes(filterId: Response.FilterId): F[Deferred[F, List[Hash]]] = + doRequest[List[Hash]](Request.filterLogs(filterId)) + + /** + * returns an array of all logs matching filter with given id + * + * @param filterId filter id which return by [[newFilter]], [[newBlockFilter]] and [[newPendingTransactionFilter]] + * @return [[Response.Log]], refer to https://eth.wiki/json-rpc/API#eth_getFilterLogs + */ + final def getFilterLogs(filterId: Response.FilterId): F[Deferred[F, List[Response.Log]]] = + doRequest[List[Response.Log]](Request.filterLogs(filterId)) + + /** + * returns an array of all logs matching a given filter object + * + * @param logQuery refer to [[Request.LogQuery]] + * @return [[Response.Log]], refer to https://eth.wiki/json-rpc/API#eth_getLogs + */ + final def getLogs(logQuery: LogQuery): F[Deferred[F, List[Response.Log]]] = + doRequest[List[Response.Log]](Request.logs(logQuery)) + + /** + * mining api, return [[Response.Work]], refer to https://eth.wiki/json-rpc/API#eth_getWork + */ + final def getWork: F[Deferred[F, Response.Work]] = doRequest[Response.Work](Request.work) + + /** + * used for submitting a proof-of-work solution + * + * @param work pow work result from miner + * @return true if succeed, false otherwise, refer to https://eth.wiki/json-rpc/API#eth_submitWork + */ + final def submitWork(work: Work): F[Deferred[F, Boolean]] = doRequest[Boolean](Request.submitWork(work)) + + /** + * used for submitting mining hashrate + * + * @param hashrate miner hashrate + * @return true if succeed, false otherwise, refer to https://eth.wiki/json-rpc/API#eth_submitHashrate + */ + final def submitHashrate(hashrate: Hashrate): F[Deferred[F, Boolean]] = + doRequest[Boolean](Request.submitHashrate(hashrate)) +} diff --git a/ethabi/src/main/scala/ethabi/protocol/Contract.scala b/ethabi/src/main/scala/ethabi/protocol/Contract.scala new file mode 100644 index 0000000..31c36f6 --- /dev/null +++ b/ethabi/src/main/scala/ethabi/protocol/Contract.scala @@ -0,0 +1,116 @@ +package ethabi +package protocol + +import cats.Applicative +import cats.implicits._ +import cats.effect.concurrent._ +import cats.effect._ +import ethabi.types._ +import ethabi.types.generated.Bytes32 +import ethabi.util._ +import ws.WebsocketClient +import Request._ +import Response._ +import Subscription.SubscriptionResult +import retry.RetryPolicies +import scala.concurrent.duration._ + +trait Contract[F[_]] { + + def isDeployed: F[Boolean] + + // get underling client, which can used to call all ethereum jsonrpc api + def client: F[Client[F]] + + def subscriber: F[Subscriber[F]] + + // get current contract creator if exist + def creator: F[Option[Address]] + + // get current contract address, it will be set after contract deploy succeed + def address: F[Option[Address]] + + /** + * deploy contract to ethereum, [[address]] will be set after deploy succeed, + * and [[creator]] will be set with CallArgs.sender + * + * @param args deploy transaction data + * @return deploy transaction hash, which can used to get receipt if deploy failed + */ + def deploy(args: CallArgs): F[Deferred[F, Hash]] + + // load contract with address, `address` will be set with `contractAddress` + def load(contractAddress: Address): F[Unit] + + // call contract method by eth_sendTransaction + def sendTransaction(args: CallArgs): F[Deferred[F, Hash]] + + // call contract method by eth_call + def call(args: CallArgs): F[Deferred[F, Array[Byte]]] + + // subscribe contract event logs + def subscribeLogs(topic: Bytes32): F[SubscriptionResult[F, Log]] +} + +object Contract { + + def apply[F[_]: ConcurrentEffect: Timer](endpoint: String)(implicit CS: ContextShift[F]): Resource[F, Contract[F]] = { + + val newContract: Subscriber[F] with Client[F] => F[Contract[F]] = cli => for { + contractorCreatorR <- Ref.of[F, Option[Address]](None) + contractAddressR <- Ref.of[F, Option[Address]](None) + } yield new Contract[F] { + + override def isDeployed: F[Boolean] = contractAddressR.get.map(_.isDefined) + + override def creator: F[Option[Address]] = contractorCreatorR.get + + override def address: F[Option[Address]] = contractAddressR.get + + override def client: F[Client[F]] = Applicative[F].pure(cli) + + override def subscriber: F[Subscriber[F]] = Applicative[F].pure(cli) + + override def load(contractAddress: Address): F[Unit] = contractAddressR.set(Some(contractAddress)) + + override def deploy(args: CallArgs): F[Deferred[F, Hash]] = for { + _ <- contractorCreatorR.set(Some(args.sender)) + txHashP <- cli.sendTransaction(Request.Transaction.deployTransaction(args)) + txHash <- txHashP.get + retryPolicies = RetryPolicies.limitRetries[F](5).join(RetryPolicies.constantDelay[F](5 seconds)) + receipt <- retryUntil[F, Option[TransactionReceipt]]( + "wait deploy contract", + retryPolicies, + cli.getTransactionReceipt(txHash).flatMap(_.get), + _.isDefined + ) + address <- assertNotNone[F, Address]("contract address", receipt.get.contractAddress) + _ <- contractAddressR.set(Some(address)) + } yield txHashP + + override def sendTransaction(args: CallArgs): F[Deferred[F, Hash]] = for { + address <- contractAddressR.get + _ <- assertNotNone[F, Address]("call contract by send transaction", address) + transaction = Request.Transaction(from = args.sender, data = args.data, to = address, opt = args.opt) + promise <- cli.sendTransaction(transaction) + } yield promise + + override def call(args: CallArgs): F[Deferred[F, Array[Byte]]] = for { + address <- contractAddressR.get + _ <- assertNotNone[F, Address]("call contract by eth call", address) + callData = Request.Transaction(from = args.sender, data = args.data, to = address, opt = args.opt) + promise <- cli.call(callData) + } yield promise + + override def subscribeLogs(topic: Bytes32): F[SubscriptionResult[F, Log]] = for { + addrOpt <- contractAddressR.get + address <- assertNotNone[F, Address]("subscribe logs", addrOpt) + result <- cli.subscribeLogs(LogQuery.from(address, topic)) + } yield result + } + + WebsocketClient.apply[F](endpoint).flatMap { wsClient => + Resource.liftF(newContract(wsClient)) + } + } +} diff --git a/ethabi/src/main/scala/ethabi/protocol/Event.scala b/ethabi/src/main/scala/ethabi/protocol/Event.scala new file mode 100644 index 0000000..4148daa --- /dev/null +++ b/ethabi/src/main/scala/ethabi/protocol/Event.scala @@ -0,0 +1,31 @@ +package ethabi +package protocol + +import Response._ +import ethabi.types._ + +final case class Event(indexedValues: Seq[SolType], nonIndexedValues: Seq[SolType]) { + override def toString: String = { + s""" + |{ + | indexedValues: ${indexedValues.mkString("[", ", ", "]")}, + | nonIndexedValues: ${nonIndexedValues.mkString("[", ", ", "]")} + |} + """.stripMargin + } +} + +object Event { + def decode(typeInfos: Seq[TypeInfo[_ <: SolType]], log: Log): Event = { + val topics = log.topics.slice(1, log.topics.length) + val indexedValues = topics.zip(typeInfos).map { + case (bytes, typeInfo) => + if (typeInfo.isStatic) typeInfo.decode(bytes.value, 0)._1 + else bytes + } + val nonIndexedTypeInfo = typeInfos.slice(topics.length, typeInfos.length).headOption + val nonIndexedValues = nonIndexedTypeInfo.map(_.decode(log.data, 0)._1.asInstanceOf[TupleType].toSeq) + Event(indexedValues, nonIndexedValues.getOrElse(Seq.empty)) + } +} + diff --git a/ethabi/src/main/scala/ethabi/protocol/Id.scala b/ethabi/src/main/scala/ethabi/protocol/Id.scala new file mode 100644 index 0000000..6e20211 --- /dev/null +++ b/ethabi/src/main/scala/ethabi/protocol/Id.scala @@ -0,0 +1,26 @@ +package ethabi +package protocol + +import io.circe._ + +/** + * [[Request.id]] and [[Response.id]], [[Id]] at here + * because of we overwrite decoder for Long at ethabi.implicits + * + * @param value the id value + */ +final case class Id(value: Long) extends AnyVal + +object Id { + + val zero: Id = Id(0) + + implicit val idDecoder: Decoder[Id] = (c: HCursor) => { + c.value.asNumber match { + case None => Left(DecodingFailure("decode to id failed", Nil)) + case Some(number) => Right(Id(number.toLong.get)) + } + } + + implicit val idEncoder: Encoder[Id] = (id: Id) => Json.fromLong(id.value) +} \ No newline at end of file diff --git a/ethabi/src/main/scala/ethabi/protocol/Request.scala b/ethabi/src/main/scala/ethabi/protocol/Request.scala new file mode 100644 index 0000000..49295fb --- /dev/null +++ b/ethabi/src/main/scala/ethabi/protocol/Request.scala @@ -0,0 +1,257 @@ +package ethabi.protocol + +import io.circe.Json +import io.circe.syntax._ +import io.circe.Encoder +import io.circe.generic.JsonCodec +import ethabi.util._ +import ethabi.types.Address +import ethabi.implicits._ +import scala.collection.mutable +import ethabi.types.generated.Bytes32 + +@JsonCodec(encodeOnly = true) +final case class Request(jsonrpc: String, id: Id, params: Seq[Json], method: String) { + def withId(id: Long): Request = copy(id = Id(id)) + def toJson: String = encode(this).toString +} + +object Request { + + private val jsonrpcVersion = "2.0" + + def apply(method: String, params: Seq[Json] = Seq.empty[Json]): Request = { + Request(jsonrpcVersion, Id.zero, params, method) + } + + // call args for eth_sendTransaction & eth_call + final case class CallArgs(data: Array[Byte], sender: Address, opt: TransactionOpt) + + sealed trait BlockTag + + object BlockTag { + implicit val encoder: Encoder[BlockTag] = (v: BlockTag) => Json.fromString(v.toString) + } + + case object Latest extends BlockTag { + override def toString = "latest" + } + + case object Earliest extends BlockTag { + override def toString = "earliest" + } + + case object Pending extends BlockTag { + override def toString = "pending" + } + + final case class BlockNumber(height: Long) extends BlockTag { + override def toString: String = Hex.long2Hex(height, withPrefix = true) + } + + @JsonCodec(encodeOnly = true) + final case class TransactionOpt(gas: Option[BigInt], gasPrice: Option[BigInt], value: Option[BigInt], nonce: Option[Long]) { + def withGas(gas: BigInt): TransactionOpt = copy(gas = Some(gas)) + def withGasPrice(gasPrice: BigInt): TransactionOpt = copy(gasPrice = Some(gasPrice)) + def withValue(value: BigInt): TransactionOpt = copy(value = Some(value)) + def withNonce(nonce: Long): TransactionOpt = copy(nonce = Some(nonce)) + } + + final case class Transaction(from: Address, to: Option[Address], data: Array[Byte], opt: TransactionOpt) { + def toJson: Json = encode(this) + override def toString: String = toJson.spaces2 + } + + object Transaction { + implicit val encoder: Encoder[Transaction] = (tx: Transaction) => { + val opts = encode(tx.opt).asObject.get.toMap + val json = mutable.Map.empty[String, Json] + json("from") = encode(tx.from) + if (tx.to.isDefined) json("to") = encode(tx.to.get) + if (tx.data.nonEmpty) json("data") = encode(tx.data) + (json ++ opts).asJson + } + + def deployTransaction(callArgs: CallArgs): Transaction = { + Transaction(from = callArgs.sender, to = None, data = callArgs.data, opt = callArgs.opt) + } + } + + /** + * [] “anything” => List[Bytes32] + * [A] “A in first position (and anything after)” => List[Bytes32] + * [null, B] “anything in first position AND B in second position (and anything after)” => `List[Option[Bytes32]]` + * [A, B] “A in first position AND B in second position (and anything after)” => List[Bytes32] + * `[[A, B], [A, B]]` “(A OR B) in first position AND (A OR B) in second position (and anything after)” => `List[List[Bytes32]]` + * + * therefore, the type of [[topics]] is `List[Either[Option[Bytes32], List[Bytes32]]]` + */ + @JsonCodec(encodeOnly = true) + final case class LogFilter(fromBlock: Option[BlockTag], toBlock: Option[BlockTag], addresses: List[Address], topics: List[Either[Option[Bytes32], List[Bytes32]]]) { + def toJson: Json = encode(this) + override def toString: String = toJson.spaces2 + } + + @JsonCodec(encodeOnly = true) + final case class LogQuery(fromBlock: Option[BlockTag], toBlock: Option[BlockTag], addresses: List[Address], topics: List[Either[Option[Bytes32], List[Bytes32]]], blockHash: Option[Hash]) { + def toJson: Json = encode(this) + override def toString: String = toJson.spaces2 + } + + object LogQuery { + def from(address: Address, topic: Bytes32): LogQuery = + LogQuery(None, None, List(address), List(Left(Some(topic))), None) + } + + final case class Work(nonce: Long, hash: Hash, mixHash: Hash) + + final case class Hashrate(rate: Bytes32, id: Bytes32) + + def sha3(data: Array[Byte]): Request = + Request(method = "web3_sha3", params = Seq(encode(data))) + + def clientVersion(): Request = Request(method = "web3_clientVersion") + def netVersion(): Request = Request(method = "net_version") + def netListening(): Request = Request(method = "net_listening") + def netPeerCount(): Request = Request(method = "net_peerCount") + def protocolVersion(): Request = Request(method = "eth_protocolVersion") + def syncing(): Request = Request(method = "eth_syncing") + def coinbase(): Request = Request(method = "eth_coinbase") + def mining(): Request = Request(method = "eth_mining") + def hashrate(): Request = Request(method = "eth_hashrate") + def gasPrice(): Request = Request(method = "eth_gasPrice") + def accounts(): Request = Request(method = "eth_accounts") + def blockNumber(): Request = Request(method = "eth_blockNumber") + + def balance(address: Address, blockTag: BlockTag): Request = + Request(method = "eth_getBalance", params = Seq(encode(address), encode(blockTag))) + + def storageAt(address: Address, position: Int, blockTag: BlockTag): Request = + Request( + method = "eth_getStorageAt", + params = Seq(encode(address), encode(position), encode(blockTag)) + ) + + def transactionCount(address: Address, blockTag: BlockTag): Request = + Request(method = "eth_getTransactionCount", params = Seq(encode(address), encode(blockTag))) + + def blockTransactionCountByHash(hash: Hash): Request = + Request(method = "eth_getBlockTransactionCountByHash", params = Seq(encode(hash))) + + def blockTransactionCountByNumber(blockTag: BlockTag = Latest): Request = + Request(method = "eth_getBlockTransactionCountByNumber", params = Seq(encode(blockTag))) + + def blockTransactionCountByNumber(height: Long): Request = + blockTransactionCountByNumber(BlockNumber(height)) + + def uncleCountByHash(hash: Hash): Request = + Request(method = "eth_getUncleCountByBlockHash", params = Seq(encode(hash))) + + def uncleCountByNumber(blockTag: BlockTag = Latest): Request = + Request(method = "eth_getUncleCountByBlockNumber", params = Seq(encode(blockTag))) + + def uncleCountByNumber(height: Long): Request = + uncleCountByNumber(BlockNumber(height)) + + def code(address: Address, blockTag: BlockTag = Latest): Request = + Request(method = "eth_getCode", params = Seq(encode(address), encode(blockTag))) + + def code(address: Address, height: Long): Request = + code(address, BlockNumber(height)) + + def sign(address: Address, data: Array[Byte]): Request = + Request(method = "eth_sign", params = Seq(encode(address), encode(data))) + + def signTransaction(tx: Transaction): Request = + Request(method = "eth_signTransaction", params = Seq(tx.toJson)) + + def sendTransaction(transaction: Transaction): Request = + Request(method = "eth_sendTransaction", params = Seq(transaction.toJson)) + + def sendRawTransaction(rawTx: Array[Byte]): Request = + Request(method = "eth_sendRawTransaction", params = Seq(encode(rawTx))) + + def call(callData: Transaction, blockTag: BlockTag): Request = + Request(method = "eth_call", params = Seq(callData.toJson, encode(blockTag))) + + def estimateGas(callData: Transaction, blockTag: BlockTag = Latest): Request = + Request(method = "eth_estimateGas", params = Seq(callData.toJson, encode(blockTag))) + + def estimateGas(callData: Transaction, height: Long): Request = estimateGas(callData, BlockNumber(height)) + + def blockByHash(hash: Hash, detail: Boolean = false): Request = + Request(method = "eth_getBlockByHash", params = Seq(encode(hash), encode(detail))) + + def blockByNumber(blockTag: BlockTag = Latest, detail: Boolean = false): Request = + Request(method = "eth_getBlockByNumber", params = Seq(encode(blockTag), encode(detail))) + + def transactionByHash(hash: Hash): Request = Request(method = "eth_getTransactionByHash", params = Seq(encode(hash))) + + def transactionByBlockHashAndIndex(hash: Hash, index: Int): Request = + Request( + method = "eth_getTransactionByBlockHashAndIndex", + params = Seq(encode(hash), encode(index)) + ) + + def transactionByBlockNumberAndIndex(blockTag: BlockTag = Latest, index: Int): Request = + Request( + method = "eth_getTransactionByBlockNumberAndIndex", + params = Seq(encode(blockTag), encode(index)) + ) + + def transactionReceipt(hash: Hash): Request = Request(method = "eth_getTransactionReceipt", params = Seq(encode(hash))) + + def uncleByBlockHashAndIndex(hash: Hash, index: Int): Request = + Request( + method = "eth_getUncleByBlockHashAndIndex", + params = Seq(encode(hash), encode(index)) + ) + + def uncleByBlockNumberAndIndex(blockTag: BlockTag = Latest, index: Int): Request = + Request( + method = "eth_getUncleByBlockNumberAndIndex", + params = Seq(encode(blockTag), encode(index)) + ) + + def newFilter(logFilter: LogFilter): Request = Request(method = "eth_newFilter", params = Seq(logFilter.toJson)) + def newBlockFilter(): Request = Request(method = "eth_newBlockFilter") + def newPendingTransactionFilter(): Request = Request(method = "eth_newPendingTransactionFilter") + def uninstallFilter(filterId: Response.FilterId): Request = + Request(method = "eth_uninstallFilter", params = Seq(encode(filterId))) + + def filterChanges(filterId: Response.FilterId): Request = + Request(method = "eth_getFilterChanges", params = Seq(encode(filterId))) + + def filterLogs(filterId: Response.FilterId): Request = + Request(method = "eth_getFilterLogs", params = Seq(encode(filterId))) + + def logs(logQuery: LogQuery): Request = Request(method = "eth_getLogs", params = Seq(logQuery.toJson)) + + def work: Request = Request(method = "eth_getWork") + + def submitWork(work: Work): Request = + Request( + method = "eth_submitWork", + params = Seq(encode(work.nonce), encode(work.hash), encode(work.mixHash)) + ) + + def submitHashrate(hashrate: Hashrate): Request = + Request( + method = "eth_submitHashrate", + params = Seq(encode(hashrate.rate), encode(hashrate.id)) + ) + + def subscribeNewHeader(): Request = + Request(method = "eth_subscribe", params = Seq(Json.fromString("newHeads"))) + + def subscribeLogs(logQuery: LogQuery): Request = + Request(method = "eth_subscribe", params = Seq(Json.fromString("logs"), logQuery.toJson)) + + def subscribeNewPendingTransactions(): Request = + Request(method = "eth_subscribe", params = Seq(Json.fromString("newPendingTransactions"))) + + def subscribeSyncStatus(): Request = Request(method = "eth_subscribe", params = Seq(Json.fromString("syncing"))) + + def unsubscribe(subscriptionId: String): Request = + Request(method = "eth_unsubscribe", params = Seq(Json.fromString(subscriptionId))) +} diff --git a/ethabi/src/main/scala/ethabi/protocol/Response.scala b/ethabi/src/main/scala/ethabi/protocol/Response.scala new file mode 100644 index 0000000..95f7bcf --- /dev/null +++ b/ethabi/src/main/scala/ethabi/protocol/Response.scala @@ -0,0 +1,108 @@ +package ethabi.protocol + +import io.circe._ +import io.circe.generic.JsonCodec +import cats.ApplicativeError +import cats.Applicative +import cats.effect.Sync +import Response.ResponseError +import ethabi.implicits._ +import ethabi.util._ +import ethabi.types.Address +import ethabi.types.generated.Bytes32 + +@JsonCodec(decodeOnly = true) +final case class Response(jsonrpc: String, id: Id, result: Json, error: Option[ResponseError]) { + private val response: Either[ResponseError, Json] = if (error.isDefined) Left(error.get) else Right(result) + + def convertTo[T: Decoder, F[_]: Sync]: F[T] = response match { + case Right(json) => + json.as[T].fold(ApplicativeError[F, Throwable].raiseError, x => Applicative[F].pure(x)) + case Left(e) => ApplicativeError[F, Throwable].raiseError(new RuntimeException(e.message)) + } +} + +object Response { + type FilterId = BigInt + + @JsonCodec(decodeOnly = true) + final case class ResponseError(code: Id, message: String, data: Option[Json]) + + @JsonCodec(decodeOnly = true) + final case class Syncing(startingBlock: Long, currentBlock: Long, highestBlock: Long) + + // different with `Syncing`, all block number is Long rather than hex-string, refer to https://github.com/ethereum/go-ethereum/wiki/RPC-PUB-SUB#syncing + @JsonCodec(decodeOnly = true) + final case class SyncProgress(startingBlock: Long, currentBlock: Long, highestBlock: Long, pulledStates: Long, knownStates: Long) + + @JsonCodec(decodeOnly = true) + final case class SyncStatus(syncing: Boolean, status: Option[SyncProgress]) + + // `logsBloom` and `extraData` keep hex string format + // TODO: `mixHash` is Option??? + @JsonCodec(decodeOnly = true) final case class Header( + parentHash: Hash, sha3Uncles: Hash, miner: Address, stateRoot: Hash, transactionsRoot: Hash, size: Int, + receiptsRoot: Hash, logsBloom: String, difficulty: BigInt, number: Long, totalDifficulty: BigInt, + gasLimit: Long, gasUsed: Long, timestamp: Long, extraData: String, mixHash: Hash, nonce: Long, hash: Hash + ) + + final case class Block(header: Header, transactionsHash: List[Hash], uncles: Option[List[Hash]]) + + object Block { + implicit val decoder: Decoder[Block] = (c: HCursor) => { + for { + header <- Decoder[Header].apply(c) + txsHash <- c.downField("transactions").as[List[Hash]] + uncles <- c.downField("uncles").as[Option[List[Hash]]] + } yield Block(header, txsHash, uncles) + } + } + + final case class BlockWithTransactions(header: Header, transactions: List[Transaction], uncles: Option[List[Hash]]) + + object BlockWithTransactions { + implicit val decoder: Decoder[BlockWithTransactions] = (c: HCursor) => { + for { + header <- Decoder[Header].apply(c) + txs <- c.downField("transactions").as[List[Transaction]] + uncles <- c.downField("uncles").as[Option[List[Hash]]] + } yield BlockWithTransactions(header, txs, uncles) + } + } + + @JsonCodec(decodeOnly = true) final case class Log( + address: Address, topics: List[Bytes32], data: Array[Byte], blockNumber: Option[Long], transactionHash: Option[Hash], + transactionIndex: Option[Int], blockHash: Option[Hash], logIndex: Option[Int], removed: Boolean + ) + + @JsonCodec(decodeOnly = true) final case class TransactionReceipt( + transactionHash: Hash, transactionIndex: Int, blockHash: Hash, blockNumber: Long, + cumulativeGasUsed: Long, gasUsed: Long, contractAddress: Option[Address], root: Option[Hash], + status: Option[String], from: Address, to: Option[Address], logs: List[Log], logsBloom: String + ) { + // 1: succeed; 0: failure + def txSucceed: Boolean = status.fold(false)(v => Hex.hex2Int(v) == 1) + } + + // Option filed because of pending transaction + @JsonCodec(decodeOnly = true) + final case class Transaction( + blockHash: Option[Hash], blockNumber: Option[Long], from: Address, gas: Long, gasPrice: BigInt, + hash: Hash, input: Array[Byte], nonce: Long, to: Option[Address], transactionIndex: Option[Int], + value: BigInt, v: String, r: String, s: String + ) + + final case class Work(hash: Hash, seedHash: Hash, target: Bytes32) + + object Work { + implicit val decoder: Decoder[Work] = (c: HCursor) => { + c.downArray + for { + lst <- c.as[List[String]] + hash = Hash(lst.head) + seedHash = Hash(lst(1)) + target = Bytes32.from(lst(2)) + } yield Work(hash, seedHash, target) + } + } +} diff --git a/ethabi/src/main/scala/ethabi/protocol/Subscriber.scala b/ethabi/src/main/scala/ethabi/protocol/Subscriber.scala new file mode 100644 index 0000000..7108fb7 --- /dev/null +++ b/ethabi/src/main/scala/ethabi/protocol/Subscriber.scala @@ -0,0 +1,49 @@ +package ethabi +package protocol + +import cats.effect.concurrent.Deferred +import ethabi.protocol.Request.LogQuery +import ethabi.protocol.Response._ +import Subscription.SubscriptionResult +import Subscription.SubscriptionId + +trait Subscriber[F[_]] { self: Client[F] => + /** + * subscribe new headers from ethereum client + * + * @return [[Subscription.SubscriptionResult]] + */ + def subscribeNewHeaders(): F[SubscriptionResult[F, Header]] + + /** + * subscribe logs from ethereum client with [[Request.LogQuery]] + * + * @param logQuery refer to [[Request.LogQuery]] + * @return [[Subscription.SubscriptionResult]] + */ + def subscribeLogs(logQuery: LogQuery): F[SubscriptionResult[F, Log]] + + /** + * subscribe new pending transactions from ethereum + * + * @return [[Subscription.SubscriptionResult]] + */ + def subscribeNewPendingTransactions(): F[SubscriptionResult[F, String]] + + /** + * subscribe ethereum block sync status + * + * @return [[Subscription.SubscriptionResult]] + */ + def subscribeSyncStatus(): F[SubscriptionResult[F, SyncStatus]] + + /** + * cancel previous subscription + * + * @param id subscription id + * @return true if cancel succeed, false otherwise; client won't receive notification no matter if succeed + */ + def unsubscribe(id: SubscriptionId): F[Deferred[F, Boolean]] +} + + diff --git a/ethabi/src/main/scala/ethabi/protocol/Subscription.scala b/ethabi/src/main/scala/ethabi/protocol/Subscription.scala new file mode 100644 index 0000000..6203d94 --- /dev/null +++ b/ethabi/src/main/scala/ethabi/protocol/Subscription.scala @@ -0,0 +1,45 @@ +package ethabi +package protocol + +import cats.effect.Sync +import cats.ApplicativeError +import cats.Applicative +import fs2.Stream +import io.circe.Decoder +import io.circe.Json +import io.circe.generic.JsonCodec + +object Subscription { + + /** + * [[SubscriptionResult]] returned after call subscribe* method + * + * @param id subscription id returned from ethereum client + * @param stream notification stream + * @tparam F effect type + * @tparam A notification type + */ + final case class SubscriptionResult[F[_], A](id: SubscriptionId, stream: Stream[F, A]) + + type SubscriptionId = String + + // dummy notification for all subscription + private[protocol] val dummy = Notification("", "", NotificationParam(Json.Null, "")) + + // keep this struct for auto derived json Encoder/Decoder from circe + @JsonCodec(decodeOnly = true) + private[protocol] case class NotificationParam(result: Json, subscription: String) + @JsonCodec(decodeOnly = true) + private[protocol] case class Notification(jsonrpc: String, method: String, params: NotificationParam) { + def subscriptionId: String = params.subscription + + def valid: Boolean = params.subscription != dummy.params.subscription + + def convertTo[T: Decoder, F[_]: Sync]: F[T] = { + Decoder[T].decodeJson(params.result).fold( + ApplicativeError[F, Throwable].raiseError, + Applicative[F].pure + ) + } + } +} diff --git a/ethabi/src/main/scala/ethabi/protocol/http/HttpClient.scala b/ethabi/src/main/scala/ethabi/protocol/http/HttpClient.scala new file mode 100644 index 0000000..3e69b89 --- /dev/null +++ b/ethabi/src/main/scala/ethabi/protocol/http/HttpClient.scala @@ -0,0 +1,58 @@ +package ethabi +package protocol +package http + +import cats._ +import cats.effect._ +import cats.effect.concurrent._ +import cats.implicits._ +import io.circe.Decoder +import org.http4s.client.jdkhttpclient.JdkHttpClient +import org.http4s.EntityDecoder +import org.http4s.EntityEncoder +import org.http4s.Header +import org.http4s.Headers +import org.http4s.Method +import org.http4s.Uri +import org.http4s.circe +import org.http4s.{ Request => HttpRequest } + +abstract class HttpClient[F[_]] extends Client[F] + +object HttpClient { + def apply[F[_]: ConcurrentEffect](endpoint: String)(implicit CS: ContextShift[F]): Resource[F, HttpClient[F]] = { + val httpClient = for { + requestId <- Ref.of[F, Long](0) + client <- JdkHttpClient.simple[F] + } yield new HttpClient[F] { + + override def doRequest[R: Decoder](request: Request): F[Deferred[F, R]] = { + implicit val responseDecoder: EntityDecoder[F, Response] = circe.jsonOf[F, Response] + implicit val requestEncoder: EntityEncoder[F, Request] = circe.jsonEncoderOf[F, Request] + + def nextId: F[Long] = requestId.getAndUpdate(_ + 1) + + val requestF: Long => F[HttpRequest[F]] = id => Uri.fromString(endpoint).fold( + ApplicativeError[F, Throwable].raiseError, + uri => Applicative[F].pure(HttpRequest[F]( + method = Method.POST, + headers = Headers.of(Header("Content-Type", "application/json")), + uri = uri, + ).withEntity(request.withId(id))) + ) + + for { + id <- nextId + request <- requestF(id) + response <- client.expect[Response](request) + result <- response.convertTo[R, F] + promise <- Deferred[F, R] + _ <- promise.complete(result) + } yield promise + + } + } + + Resource.liftF[F, HttpClient[F]](httpClient) + } +} diff --git a/ethabi/src/main/scala/ethabi/protocol/package.scala b/ethabi/src/main/scala/ethabi/protocol/package.scala new file mode 100644 index 0000000..d419108 --- /dev/null +++ b/ethabi/src/main/scala/ethabi/protocol/package.scala @@ -0,0 +1,37 @@ +package ethabi + +import cats.ApplicativeError +import cats.Applicative +import cats.MonadError +import cats.effect.Timer +import retry.RetryPolicy +import retry.RetryDetails + +package object protocol { + + // TODO: replace with kind-projector + type ApplicativeErrorL[F[_]] = ApplicativeError[F, Throwable] + + private[protocol] def assertNotNone[F[_]: ApplicativeErrorL, T](tag: String, result: Option[T]): F[T] = { + val exception = new RuntimeException(s"expected $tag, but have none") + result.fold[F[T]]( + ApplicativeError[F, Throwable].raiseError(exception))(t => Applicative[F].pure(t)) + } + + type MonadErrorL[F[_]] = MonadError[F, Throwable] + + def retryUntil[F[_]: MonadErrorL: Timer, T]( + tag: String, + strategy: RetryPolicy[F], + task: => F[T], + pred: T => Boolean + ): F[T] = { + val onFailure: (T, RetryDetails) => F[Unit] = { + case (_, RetryDetails.GivingUp(retries, delay)) => ApplicativeError[F, Throwable].raiseError( + new RuntimeException(s"$tag retry give up, total retries: $retries, total delay: ${delay.toSeconds} seconds") + ) + case (_, RetryDetails.WillDelayAndRetry(_, _, _)) => Applicative[F].unit // TODO: log here + } + retry.retryingM[T].apply[F](strategy, pred, onFailure)(task) + } +} diff --git a/ethabi/src/main/scala/ethabi/protocol/ws/WebsocketClient.scala b/ethabi/src/main/scala/ethabi/protocol/ws/WebsocketClient.scala new file mode 100644 index 0000000..619c6c3 --- /dev/null +++ b/ethabi/src/main/scala/ethabi/protocol/ws/WebsocketClient.scala @@ -0,0 +1,142 @@ +package ethabi +package protocol +package ws + +import cats._ +import cats.effect._ +import cats.effect.concurrent._ +import cats.effect.implicits._ +import cats.implicits._ +import fs2.Stream +import fs2.concurrent.Topic +import io.circe.Decoder +import io.circe.jawn +import org.http4s.Uri +import org.http4s.client.jdkhttpclient._ + +abstract class WebsocketClient[F[_]] extends Client[F] with Subscriber[F] { + private[ws] def terminate: F[Unit] +} + +object WebsocketClient { + + import Subscription.SubscriptionResult + import Subscription.SubscriptionId + + def apply[F[_]: ConcurrentEffect](endpoint: String)(implicit CS: ContextShift[F]): Resource[F, WebsocketClient[F]] = { + + val resource: F[Resource[F, WSConnectionHighLevel[F]]] = JdkWSClient.simple[F].flatMap { wsClient => + Uri.fromString(endpoint).map(uri => wsClient.connectHighLevel(WSRequest(uri))) + .fold(err => ApplicativeError[F, Throwable].raiseError(err), resource => Sync[F].delay(resource)) + } + + type CompletedCallback = Response => F[Unit] + + def dispatch( + connection: WSConnectionHighLevel[F], + requestMap: Ref[F, Map[Long, CompletedCallback]], + subscriptionMap: Ref[F, Map[SubscriptionId, Topic[F, Subscription.Notification]]] + ): Stream[F, Nothing] = { + Stream.eval(connection.receive).flatMap { + + case Some(WSFrame.Text(data, true)) => + + def onResponse(id: Long, resp: Response): F[Unit] = { + requestMap.modify[Option[CompletedCallback]] { map => + map.get(id) match { + case Some(callback) => (map - id, Some(callback)) + case _ => (map, None) + } + }.flatMap(_.fold[F[Unit]](Sync[F].unit)(_.apply(resp))) + } + + def onNotify(notification: Subscription.Notification): F[Unit] = { + for { + map <- subscriptionMap.get + _ <- map.get(notification.subscriptionId).fold(Sync[F].unit)(_.publish1(notification)) + } yield () + } + + jawn.parse(data).fold(Stream.raiseError[F], json => { + val decoder = Decoder[Response].either(Decoder[Subscription.Notification]) + decoder.decodeJson(json) + .fold(Stream.raiseError[F], _.fold( + resp => Stream.eval_(onResponse(resp.id.value, resp)), + notification => Stream.eval_(onNotify(notification)) + )) + }) + + case Some(frame) => Stream.raiseError[F](new RuntimeException(s"unexpected frame $frame")) + + case None => Stream.empty + + }.handleErrorWith { err => + Stream.eval_(Sync[F].delay(err.printStackTrace())) ++ // ignore current error and continue, NOTE: it is necessary to handle + dispatch(connection, requestMap, subscriptionMap) // error in `Stream` level, stream will stop if handle error in `F` level + } + } + + val newWsClient: WSConnectionHighLevel[F] => F[WebsocketClient[F]] = conn => for { + requestId <- Ref.of[F, Long](0) + requestMap <- Ref[F].of(Map.empty[Long, CompletedCallback]) + subscriptionMap <- Ref[F].of(Map.empty[SubscriptionId, Topic[F, Subscription.Notification]]) + fiber <- dispatch(conn, requestMap, subscriptionMap).repeat.compile.drain.start + } yield new WebsocketClient[F] { + + override def terminate: F[Unit] = fiber.cancel + + override def doRequest[R: Decoder](request: Request): F[Deferred[F, R]] = { + def nextId: F[Long] = requestId.getAndUpdate(_ + 1) + + for { + id <- nextId + promise <- Deferred[F, R] + _ <- conn.send(fromRequest(request.withId(id))) + _ <- requestMap.update(_.updated(id, _.convertTo[R, F].flatMap(promise.complete))) + } yield promise + } + + private def subscribeHelper[RESP: Decoder](request: Request, queueSize: Int = 64): F[SubscriptionResult[F, RESP]] = { + val result = for { + promise <- doRequest[String](request) + subscriptionId <- promise.get + topic <- Topic[F, Subscription.Notification](Subscription.dummy) + _ <- subscriptionMap.update(_.updated(subscriptionId, topic)) + } yield (subscriptionId, topic.subscribe(queueSize)) // TODO: configurable + result.map { case (id, stream) => + SubscriptionResult(id, stream.filter(_.valid).evalMap(_.convertTo[RESP, F])) + } + } + + override def subscribeLogs(query: Request.LogQuery): F[SubscriptionResult[F, Response.Log]] = { + subscribeHelper[Response.Log](Request.subscribeLogs(query)) + } + + override def subscribeNewHeaders(): F[SubscriptionResult[F, Response.Header]] = { + subscribeHelper[Response.Header](Request.subscribeNewHeader()) + } + + override def subscribeNewPendingTransactions(): F[SubscriptionResult[F, String]] = { + subscribeHelper[String](Request.subscribeNewPendingTransactions()) + } + + override def subscribeSyncStatus(): F[SubscriptionResult[F, Response.SyncStatus]] = { + subscribeHelper[Response.SyncStatus](Request.subscribeSyncStatus()) + } + + override def unsubscribe(subscriptionId: String): F[Deferred[F, Boolean]] = { + // send unsubscribe request first, and ignore the response + for { + promise <- doRequest[Boolean](Request.unsubscribe(subscriptionId)) + _ <- subscriptionMap.update(_ - subscriptionId) + } yield promise + } + } + + Resource.suspend(resource).flatMap { conn => + Resource.make(newWsClient(conn))(_.terminate) + } + } + + private def fromRequest(request: Request): WSDataFrame = WSFrame.Text(request.toJson) +} diff --git a/src/main/scala/ethabi/types/Address.scala b/ethabi/src/main/scala/ethabi/types/Address.scala similarity index 83% rename from src/main/scala/ethabi/types/Address.scala rename to ethabi/src/main/scala/ethabi/types/Address.scala index 3be63f9..a9be9a0 100644 --- a/src/main/scala/ethabi/types/Address.scala +++ b/ethabi/src/main/scala/ethabi/types/Address.scala @@ -21,10 +21,10 @@ object Address { override def name: String = "address" override def isStatic: Boolean = true override def encode[U >: Address](address: U): Array[Byte] = { - Uint160.typeInfo.encode(Uint160(BigInt(new BigInteger(address.asInstanceOf[Address].value)))) + TypeInfo[Uint160].encode(Uint160(BigInt(new BigInteger(address.asInstanceOf[Address].value)))) } override def decode(bytes: Array[Byte], position: Int): (Address, Int) = { - val (result, consumed) = Uint160.typeInfo.decode(bytes, position) + val (result, consumed) = TypeInfo[Uint160].decode(bytes, position) (Address(result.value.toByteArray), consumed) } } diff --git a/src/main/scala/ethabi/types/Bool.scala b/ethabi/src/main/scala/ethabi/types/Bool.scala similarity index 89% rename from src/main/scala/ethabi/types/Bool.scala rename to ethabi/src/main/scala/ethabi/types/Bool.scala index 42cdb44..5f7051f 100644 --- a/src/main/scala/ethabi/types/Bool.scala +++ b/ethabi/src/main/scala/ethabi/types/Bool.scala @@ -18,8 +18,8 @@ object Bool { override def isStatic: Boolean = true override def encode[U >: Bool](value: U): Array[Byte] = { val b = value.asInstanceOf[Bool].value - if (b) Uint8.typeInfo.encode(Uint8(BigInt(1))) - else Uint8.typeInfo.encode(Uint8(BigInt(0))) + if (b) TypeInfo[Uint8].encode(Uint8(BigInt(1))) + else TypeInfo[Uint8].encode(Uint8(BigInt(0))) } override def decode(bytes: Array[Byte], position: Int): (Bool, Int) = { val (result, consumed) = Uint8.typeInfo.decode(bytes, position) diff --git a/src/main/scala/ethabi/types/DynamicArray.scala b/ethabi/src/main/scala/ethabi/types/DynamicArray.scala similarity index 86% rename from src/main/scala/ethabi/types/DynamicArray.scala rename to ethabi/src/main/scala/ethabi/types/DynamicArray.scala index 7a965fd..424dc31 100644 --- a/src/main/scala/ethabi/types/DynamicArray.scala +++ b/ethabi/src/main/scala/ethabi/types/DynamicArray.scala @@ -1,7 +1,6 @@ package ethabi.types import generated.Uint256 -import scala.language.implicitConversions final class DynamicArray[T <: SolType](val values: Seq[T]) extends SolType { def apply(x: Int): T = values(x) @@ -16,14 +15,14 @@ object DynamicArray { override def isStatic: Boolean = false override def encode[U >: DynamicArray[T]](value: U): Array[Byte] = { val values = value.asInstanceOf[DynamicArray[T]].values - val encodedLength = Uint256.typeInfo.encode(Uint256(BigInt(values.length))) + val encodedLength = TypeInfo[Uint256].encode(Uint256(BigInt(values.length))) val encodedValues = values.map(typeInfoT.encode(_)) val typeInfos = Seq.fill[TypeInfo[T]](values.length)(typeInfoT) val bytes = TupleType.encode(typeInfos, encodedValues) encodedLength ++ bytes } override def decode(bytes: Array[Byte], position: Int): (DynamicArray[T], Int) = { - val (length, lengthConsumed) = Uint256.typeInfo.decode(bytes, position) + val (length, lengthConsumed) = TypeInfo[Uint256].decode(bytes, position) val typeInfos = Seq.fill[TypeInfo[T]](length.value.toInt)(typeInfoT) val (result, consumed) = TupleType.decode(bytes, position + lengthConsumed, typeInfos) val values = result.map(_.asInstanceOf[T]) diff --git a/src/main/scala/ethabi/types/DynamicBytes.scala b/ethabi/src/main/scala/ethabi/types/DynamicBytes.scala similarity index 92% rename from src/main/scala/ethabi/types/DynamicBytes.scala rename to ethabi/src/main/scala/ethabi/types/DynamicBytes.scala index 8cc5b2d..46e65ae 100644 --- a/src/main/scala/ethabi/types/DynamicBytes.scala +++ b/ethabi/src/main/scala/ethabi/types/DynamicBytes.scala @@ -22,7 +22,7 @@ object DynamicBytes { def encode(value: Array[Byte]): Array[Byte] = { val length = Uint256(BigInt(value.length)) - val lengthEncoded = Uint256.typeInfo.encode(length) + val lengthEncoded = TypeInfo[Uint256].encode(length) val totalLength = encodedLength(value.length) + 32 val result = Array.fill[Byte](totalLength)(0) Array.copy(lengthEncoded, 0, result, 0, 32) @@ -31,7 +31,7 @@ object DynamicBytes { } def decode(bytes: Array[Byte], position: Int): (Array[Byte], Int) = { - val (result, consumed) = Uint256.typeInfo.decode(bytes, position) + val (result, consumed) = TypeInfo[Uint256].decode(bytes, position) val offset = position + consumed val length = result.value.toInt val encodedLen = encodedLength(length) diff --git a/src/main/scala/ethabi/types/FunctionType.scala b/ethabi/src/main/scala/ethabi/types/FunctionType.scala similarity index 91% rename from src/main/scala/ethabi/types/FunctionType.scala rename to ethabi/src/main/scala/ethabi/types/FunctionType.scala index 885b17d..f2c352a 100644 --- a/src/main/scala/ethabi/types/FunctionType.scala +++ b/ethabi/src/main/scala/ethabi/types/FunctionType.scala @@ -14,11 +14,11 @@ object FunctionType { val bytes = Array.fill[Byte](24)(0) Array.copy(function.selector, 0, bytes, 0, 4) Array.copy(function.address.value, 0, bytes, 4, 20) - Bytes24.typeInfo.encode(Bytes24(bytes)) + TypeInfo[Bytes24].encode(Bytes24(bytes)) } def decode(bytes: Array[Byte], position: Int): (FunctionType, Int) = { - val (result, consumed) = Bytes24.typeInfo.decode(bytes, position) + val (result, consumed) = TypeInfo[Bytes24].decode(bytes, position) (FunctionType(result.value.slice(0, 4), Address(result.value.slice(4, 24))), consumed) } diff --git a/src/main/scala/ethabi/types/IntType.scala b/ethabi/src/main/scala/ethabi/types/IntType.scala similarity index 100% rename from src/main/scala/ethabi/types/IntType.scala rename to ethabi/src/main/scala/ethabi/types/IntType.scala diff --git a/ethabi/src/main/scala/ethabi/types/SolType.scala b/ethabi/src/main/scala/ethabi/types/SolType.scala new file mode 100644 index 0000000..207c3e1 --- /dev/null +++ b/ethabi/src/main/scala/ethabi/types/SolType.scala @@ -0,0 +1,4 @@ +package ethabi.types + +// Mark trait for solidity type +trait SolType diff --git a/src/main/scala/ethabi/types/StaticArray.scala b/ethabi/src/main/scala/ethabi/types/StaticArray.scala similarity index 96% rename from src/main/scala/ethabi/types/StaticArray.scala rename to ethabi/src/main/scala/ethabi/types/StaticArray.scala index 43cab6a..0ef1dd8 100644 --- a/src/main/scala/ethabi/types/StaticArray.scala +++ b/ethabi/src/main/scala/ethabi/types/StaticArray.scala @@ -1,7 +1,5 @@ package ethabi.types -import scala.language.implicitConversions - // TODO: it would be better if `length` as type argument, maybe `shapeless` can solve this final class StaticArray[T <: SolType](val values: Seq[T]) extends SolType { def apply(x: Int): T = values(x) diff --git a/src/main/scala/ethabi/types/StaticBytes.scala b/ethabi/src/main/scala/ethabi/types/StaticBytes.scala similarity index 100% rename from src/main/scala/ethabi/types/StaticBytes.scala rename to ethabi/src/main/scala/ethabi/types/StaticBytes.scala diff --git a/src/main/scala/ethabi/types/StringType.scala b/ethabi/src/main/scala/ethabi/types/StringType.scala similarity index 85% rename from src/main/scala/ethabi/types/StringType.scala rename to ethabi/src/main/scala/ethabi/types/StringType.scala index 8ae750e..4d4480e 100644 --- a/src/main/scala/ethabi/types/StringType.scala +++ b/ethabi/src/main/scala/ethabi/types/StringType.scala @@ -15,10 +15,10 @@ object StringType { override def isStatic: Boolean = false override def encode[U >: StringType](value: U): Array[Byte] = { val bytes = value.asInstanceOf[StringType].value.getBytes(StandardCharsets.UTF_8) - DynamicBytes.typeInfo.encode(DynamicBytes(bytes)) + TypeInfo[DynamicBytes].encode(DynamicBytes(bytes)) } override def decode(bytes: Array[Byte], position: Int): (StringType, Int) = { - val (result, consumed) = DynamicBytes.typeInfo.decode(bytes, position) + val (result, consumed) = TypeInfo[DynamicBytes].decode(bytes, position) (StringType(new String(result.value, StandardCharsets.UTF_8)), consumed) } } diff --git a/src/main/scala/ethabi/types/TupleType.scala b/ethabi/src/main/scala/ethabi/types/TupleType.scala similarity index 91% rename from src/main/scala/ethabi/types/TupleType.scala rename to ethabi/src/main/scala/ethabi/types/TupleType.scala index 0e59dab..cd641e2 100644 --- a/src/main/scala/ethabi/types/TupleType.scala +++ b/ethabi/src/main/scala/ethabi/types/TupleType.scala @@ -29,7 +29,7 @@ object TupleType { Array.copy(encoded, 0, bytes, staticOffset, encoded.length) staticOffset += encoded.length } else { - val dynamicOffsetEncoded = Uint256.typeInfo.encode(Uint256(BigInt(dynamicOffset))) + val dynamicOffsetEncoded = TypeInfo[Uint256].encode(Uint256(BigInt(dynamicOffset))) Array.copy(dynamicOffsetEncoded, 0, bytes, staticOffset, 32) Array.copy(encoded, 0, bytes, dynamicOffset, encoded.length) staticOffset += 32 @@ -49,7 +49,7 @@ object TupleType { staticOffset += consumed results += result } else { - val (offset, offsetConsumed) = Uint256.typeInfo.decode(bytes, staticOffset + position) + val (offset, offsetConsumed) = TypeInfo[Uint256].decode(bytes, staticOffset + position) staticOffset += offsetConsumed totalConsumed += offsetConsumed val (result, resultConsumed) = typeInfo.decode(bytes, offset.value.toInt + position) diff --git a/src/main/scala/ethabi/types/SolType.scala b/ethabi/src/main/scala/ethabi/types/TypeInfo.scala similarity index 66% rename from src/main/scala/ethabi/types/SolType.scala rename to ethabi/src/main/scala/ethabi/types/TypeInfo.scala index d01a8f9..63c189e 100644 --- a/src/main/scala/ethabi/types/SolType.scala +++ b/ethabi/src/main/scala/ethabi/types/TypeInfo.scala @@ -1,12 +1,12 @@ package ethabi.types -// Mark trait for solidity type -trait SolType - -// TODO: make T as type member of `TypeInfo` trait TypeInfo[+T <: SolType] { def name: String def isStatic: Boolean def encode[U >: T](value: U): Array[Byte] def decode(bytes: Array[Byte], position: Int): (T, Int) } + +object TypeInfo { + implicit def apply[T <: SolType](implicit info: TypeInfo[T]): TypeInfo[T] = info +} diff --git a/src/main/scala/ethabi/types/UintType.scala b/ethabi/src/main/scala/ethabi/types/UintType.scala similarity index 100% rename from src/main/scala/ethabi/types/UintType.scala rename to ethabi/src/main/scala/ethabi/types/UintType.scala diff --git a/src/main/scala/ethabi/types/generated/Bytes1.scala b/ethabi/src/main/scala/ethabi/types/generated/Bytes1.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Bytes1.scala rename to ethabi/src/main/scala/ethabi/types/generated/Bytes1.scala diff --git a/src/main/scala/ethabi/types/generated/Bytes10.scala b/ethabi/src/main/scala/ethabi/types/generated/Bytes10.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Bytes10.scala rename to ethabi/src/main/scala/ethabi/types/generated/Bytes10.scala diff --git a/src/main/scala/ethabi/types/generated/Bytes11.scala b/ethabi/src/main/scala/ethabi/types/generated/Bytes11.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Bytes11.scala rename to ethabi/src/main/scala/ethabi/types/generated/Bytes11.scala diff --git a/src/main/scala/ethabi/types/generated/Bytes12.scala b/ethabi/src/main/scala/ethabi/types/generated/Bytes12.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Bytes12.scala rename to ethabi/src/main/scala/ethabi/types/generated/Bytes12.scala diff --git a/src/main/scala/ethabi/types/generated/Bytes13.scala b/ethabi/src/main/scala/ethabi/types/generated/Bytes13.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Bytes13.scala rename to ethabi/src/main/scala/ethabi/types/generated/Bytes13.scala diff --git a/src/main/scala/ethabi/types/generated/Bytes14.scala b/ethabi/src/main/scala/ethabi/types/generated/Bytes14.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Bytes14.scala rename to ethabi/src/main/scala/ethabi/types/generated/Bytes14.scala diff --git a/src/main/scala/ethabi/types/generated/Bytes15.scala b/ethabi/src/main/scala/ethabi/types/generated/Bytes15.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Bytes15.scala rename to ethabi/src/main/scala/ethabi/types/generated/Bytes15.scala diff --git a/src/main/scala/ethabi/types/generated/Bytes16.scala b/ethabi/src/main/scala/ethabi/types/generated/Bytes16.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Bytes16.scala rename to ethabi/src/main/scala/ethabi/types/generated/Bytes16.scala diff --git a/src/main/scala/ethabi/types/generated/Bytes17.scala b/ethabi/src/main/scala/ethabi/types/generated/Bytes17.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Bytes17.scala rename to ethabi/src/main/scala/ethabi/types/generated/Bytes17.scala diff --git a/src/main/scala/ethabi/types/generated/Bytes18.scala b/ethabi/src/main/scala/ethabi/types/generated/Bytes18.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Bytes18.scala rename to ethabi/src/main/scala/ethabi/types/generated/Bytes18.scala diff --git a/src/main/scala/ethabi/types/generated/Bytes19.scala b/ethabi/src/main/scala/ethabi/types/generated/Bytes19.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Bytes19.scala rename to ethabi/src/main/scala/ethabi/types/generated/Bytes19.scala diff --git a/src/main/scala/ethabi/types/generated/Bytes2.scala b/ethabi/src/main/scala/ethabi/types/generated/Bytes2.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Bytes2.scala rename to ethabi/src/main/scala/ethabi/types/generated/Bytes2.scala diff --git a/src/main/scala/ethabi/types/generated/Bytes20.scala b/ethabi/src/main/scala/ethabi/types/generated/Bytes20.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Bytes20.scala rename to ethabi/src/main/scala/ethabi/types/generated/Bytes20.scala diff --git a/src/main/scala/ethabi/types/generated/Bytes21.scala b/ethabi/src/main/scala/ethabi/types/generated/Bytes21.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Bytes21.scala rename to ethabi/src/main/scala/ethabi/types/generated/Bytes21.scala diff --git a/src/main/scala/ethabi/types/generated/Bytes22.scala b/ethabi/src/main/scala/ethabi/types/generated/Bytes22.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Bytes22.scala rename to ethabi/src/main/scala/ethabi/types/generated/Bytes22.scala diff --git a/src/main/scala/ethabi/types/generated/Bytes23.scala b/ethabi/src/main/scala/ethabi/types/generated/Bytes23.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Bytes23.scala rename to ethabi/src/main/scala/ethabi/types/generated/Bytes23.scala diff --git a/src/main/scala/ethabi/types/generated/Bytes24.scala b/ethabi/src/main/scala/ethabi/types/generated/Bytes24.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Bytes24.scala rename to ethabi/src/main/scala/ethabi/types/generated/Bytes24.scala diff --git a/src/main/scala/ethabi/types/generated/Bytes25.scala b/ethabi/src/main/scala/ethabi/types/generated/Bytes25.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Bytes25.scala rename to ethabi/src/main/scala/ethabi/types/generated/Bytes25.scala diff --git a/src/main/scala/ethabi/types/generated/Bytes26.scala b/ethabi/src/main/scala/ethabi/types/generated/Bytes26.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Bytes26.scala rename to ethabi/src/main/scala/ethabi/types/generated/Bytes26.scala diff --git a/src/main/scala/ethabi/types/generated/Bytes27.scala b/ethabi/src/main/scala/ethabi/types/generated/Bytes27.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Bytes27.scala rename to ethabi/src/main/scala/ethabi/types/generated/Bytes27.scala diff --git a/src/main/scala/ethabi/types/generated/Bytes28.scala b/ethabi/src/main/scala/ethabi/types/generated/Bytes28.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Bytes28.scala rename to ethabi/src/main/scala/ethabi/types/generated/Bytes28.scala diff --git a/src/main/scala/ethabi/types/generated/Bytes29.scala b/ethabi/src/main/scala/ethabi/types/generated/Bytes29.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Bytes29.scala rename to ethabi/src/main/scala/ethabi/types/generated/Bytes29.scala diff --git a/src/main/scala/ethabi/types/generated/Bytes3.scala b/ethabi/src/main/scala/ethabi/types/generated/Bytes3.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Bytes3.scala rename to ethabi/src/main/scala/ethabi/types/generated/Bytes3.scala diff --git a/src/main/scala/ethabi/types/generated/Bytes30.scala b/ethabi/src/main/scala/ethabi/types/generated/Bytes30.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Bytes30.scala rename to ethabi/src/main/scala/ethabi/types/generated/Bytes30.scala diff --git a/src/main/scala/ethabi/types/generated/Bytes31.scala b/ethabi/src/main/scala/ethabi/types/generated/Bytes31.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Bytes31.scala rename to ethabi/src/main/scala/ethabi/types/generated/Bytes31.scala diff --git a/src/main/scala/ethabi/types/generated/Bytes32.scala b/ethabi/src/main/scala/ethabi/types/generated/Bytes32.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Bytes32.scala rename to ethabi/src/main/scala/ethabi/types/generated/Bytes32.scala diff --git a/src/main/scala/ethabi/types/generated/Bytes4.scala b/ethabi/src/main/scala/ethabi/types/generated/Bytes4.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Bytes4.scala rename to ethabi/src/main/scala/ethabi/types/generated/Bytes4.scala diff --git a/src/main/scala/ethabi/types/generated/Bytes5.scala b/ethabi/src/main/scala/ethabi/types/generated/Bytes5.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Bytes5.scala rename to ethabi/src/main/scala/ethabi/types/generated/Bytes5.scala diff --git a/src/main/scala/ethabi/types/generated/Bytes6.scala b/ethabi/src/main/scala/ethabi/types/generated/Bytes6.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Bytes6.scala rename to ethabi/src/main/scala/ethabi/types/generated/Bytes6.scala diff --git a/src/main/scala/ethabi/types/generated/Bytes7.scala b/ethabi/src/main/scala/ethabi/types/generated/Bytes7.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Bytes7.scala rename to ethabi/src/main/scala/ethabi/types/generated/Bytes7.scala diff --git a/src/main/scala/ethabi/types/generated/Bytes8.scala b/ethabi/src/main/scala/ethabi/types/generated/Bytes8.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Bytes8.scala rename to ethabi/src/main/scala/ethabi/types/generated/Bytes8.scala diff --git a/src/main/scala/ethabi/types/generated/Bytes9.scala b/ethabi/src/main/scala/ethabi/types/generated/Bytes9.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Bytes9.scala rename to ethabi/src/main/scala/ethabi/types/generated/Bytes9.scala diff --git a/src/main/scala/ethabi/types/generated/Int104.scala b/ethabi/src/main/scala/ethabi/types/generated/Int104.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Int104.scala rename to ethabi/src/main/scala/ethabi/types/generated/Int104.scala diff --git a/src/main/scala/ethabi/types/generated/Int112.scala b/ethabi/src/main/scala/ethabi/types/generated/Int112.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Int112.scala rename to ethabi/src/main/scala/ethabi/types/generated/Int112.scala diff --git a/src/main/scala/ethabi/types/generated/Int120.scala b/ethabi/src/main/scala/ethabi/types/generated/Int120.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Int120.scala rename to ethabi/src/main/scala/ethabi/types/generated/Int120.scala diff --git a/src/main/scala/ethabi/types/generated/Int128.scala b/ethabi/src/main/scala/ethabi/types/generated/Int128.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Int128.scala rename to ethabi/src/main/scala/ethabi/types/generated/Int128.scala diff --git a/src/main/scala/ethabi/types/generated/Int136.scala b/ethabi/src/main/scala/ethabi/types/generated/Int136.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Int136.scala rename to ethabi/src/main/scala/ethabi/types/generated/Int136.scala diff --git a/src/main/scala/ethabi/types/generated/Int144.scala b/ethabi/src/main/scala/ethabi/types/generated/Int144.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Int144.scala rename to ethabi/src/main/scala/ethabi/types/generated/Int144.scala diff --git a/src/main/scala/ethabi/types/generated/Int152.scala b/ethabi/src/main/scala/ethabi/types/generated/Int152.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Int152.scala rename to ethabi/src/main/scala/ethabi/types/generated/Int152.scala diff --git a/src/main/scala/ethabi/types/generated/Int16.scala b/ethabi/src/main/scala/ethabi/types/generated/Int16.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Int16.scala rename to ethabi/src/main/scala/ethabi/types/generated/Int16.scala diff --git a/src/main/scala/ethabi/types/generated/Int160.scala b/ethabi/src/main/scala/ethabi/types/generated/Int160.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Int160.scala rename to ethabi/src/main/scala/ethabi/types/generated/Int160.scala diff --git a/src/main/scala/ethabi/types/generated/Int168.scala b/ethabi/src/main/scala/ethabi/types/generated/Int168.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Int168.scala rename to ethabi/src/main/scala/ethabi/types/generated/Int168.scala diff --git a/src/main/scala/ethabi/types/generated/Int176.scala b/ethabi/src/main/scala/ethabi/types/generated/Int176.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Int176.scala rename to ethabi/src/main/scala/ethabi/types/generated/Int176.scala diff --git a/src/main/scala/ethabi/types/generated/Int184.scala b/ethabi/src/main/scala/ethabi/types/generated/Int184.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Int184.scala rename to ethabi/src/main/scala/ethabi/types/generated/Int184.scala diff --git a/src/main/scala/ethabi/types/generated/Int192.scala b/ethabi/src/main/scala/ethabi/types/generated/Int192.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Int192.scala rename to ethabi/src/main/scala/ethabi/types/generated/Int192.scala diff --git a/src/main/scala/ethabi/types/generated/Int200.scala b/ethabi/src/main/scala/ethabi/types/generated/Int200.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Int200.scala rename to ethabi/src/main/scala/ethabi/types/generated/Int200.scala diff --git a/src/main/scala/ethabi/types/generated/Int208.scala b/ethabi/src/main/scala/ethabi/types/generated/Int208.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Int208.scala rename to ethabi/src/main/scala/ethabi/types/generated/Int208.scala diff --git a/src/main/scala/ethabi/types/generated/Int216.scala b/ethabi/src/main/scala/ethabi/types/generated/Int216.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Int216.scala rename to ethabi/src/main/scala/ethabi/types/generated/Int216.scala diff --git a/src/main/scala/ethabi/types/generated/Int224.scala b/ethabi/src/main/scala/ethabi/types/generated/Int224.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Int224.scala rename to ethabi/src/main/scala/ethabi/types/generated/Int224.scala diff --git a/src/main/scala/ethabi/types/generated/Int232.scala b/ethabi/src/main/scala/ethabi/types/generated/Int232.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Int232.scala rename to ethabi/src/main/scala/ethabi/types/generated/Int232.scala diff --git a/src/main/scala/ethabi/types/generated/Int24.scala b/ethabi/src/main/scala/ethabi/types/generated/Int24.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Int24.scala rename to ethabi/src/main/scala/ethabi/types/generated/Int24.scala diff --git a/src/main/scala/ethabi/types/generated/Int240.scala b/ethabi/src/main/scala/ethabi/types/generated/Int240.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Int240.scala rename to ethabi/src/main/scala/ethabi/types/generated/Int240.scala diff --git a/src/main/scala/ethabi/types/generated/Int248.scala b/ethabi/src/main/scala/ethabi/types/generated/Int248.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Int248.scala rename to ethabi/src/main/scala/ethabi/types/generated/Int248.scala diff --git a/src/main/scala/ethabi/types/generated/Int256.scala b/ethabi/src/main/scala/ethabi/types/generated/Int256.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Int256.scala rename to ethabi/src/main/scala/ethabi/types/generated/Int256.scala diff --git a/src/main/scala/ethabi/types/generated/Int32.scala b/ethabi/src/main/scala/ethabi/types/generated/Int32.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Int32.scala rename to ethabi/src/main/scala/ethabi/types/generated/Int32.scala diff --git a/src/main/scala/ethabi/types/generated/Int40.scala b/ethabi/src/main/scala/ethabi/types/generated/Int40.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Int40.scala rename to ethabi/src/main/scala/ethabi/types/generated/Int40.scala diff --git a/src/main/scala/ethabi/types/generated/Int48.scala b/ethabi/src/main/scala/ethabi/types/generated/Int48.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Int48.scala rename to ethabi/src/main/scala/ethabi/types/generated/Int48.scala diff --git a/src/main/scala/ethabi/types/generated/Int56.scala b/ethabi/src/main/scala/ethabi/types/generated/Int56.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Int56.scala rename to ethabi/src/main/scala/ethabi/types/generated/Int56.scala diff --git a/src/main/scala/ethabi/types/generated/Int64.scala b/ethabi/src/main/scala/ethabi/types/generated/Int64.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Int64.scala rename to ethabi/src/main/scala/ethabi/types/generated/Int64.scala diff --git a/src/main/scala/ethabi/types/generated/Int72.scala b/ethabi/src/main/scala/ethabi/types/generated/Int72.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Int72.scala rename to ethabi/src/main/scala/ethabi/types/generated/Int72.scala diff --git a/src/main/scala/ethabi/types/generated/Int8.scala b/ethabi/src/main/scala/ethabi/types/generated/Int8.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Int8.scala rename to ethabi/src/main/scala/ethabi/types/generated/Int8.scala diff --git a/src/main/scala/ethabi/types/generated/Int80.scala b/ethabi/src/main/scala/ethabi/types/generated/Int80.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Int80.scala rename to ethabi/src/main/scala/ethabi/types/generated/Int80.scala diff --git a/src/main/scala/ethabi/types/generated/Int88.scala b/ethabi/src/main/scala/ethabi/types/generated/Int88.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Int88.scala rename to ethabi/src/main/scala/ethabi/types/generated/Int88.scala diff --git a/src/main/scala/ethabi/types/generated/Int96.scala b/ethabi/src/main/scala/ethabi/types/generated/Int96.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Int96.scala rename to ethabi/src/main/scala/ethabi/types/generated/Int96.scala diff --git a/src/main/scala/ethabi/types/generated/Uint104.scala b/ethabi/src/main/scala/ethabi/types/generated/Uint104.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Uint104.scala rename to ethabi/src/main/scala/ethabi/types/generated/Uint104.scala diff --git a/src/main/scala/ethabi/types/generated/Uint112.scala b/ethabi/src/main/scala/ethabi/types/generated/Uint112.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Uint112.scala rename to ethabi/src/main/scala/ethabi/types/generated/Uint112.scala diff --git a/src/main/scala/ethabi/types/generated/Uint120.scala b/ethabi/src/main/scala/ethabi/types/generated/Uint120.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Uint120.scala rename to ethabi/src/main/scala/ethabi/types/generated/Uint120.scala diff --git a/src/main/scala/ethabi/types/generated/Uint128.scala b/ethabi/src/main/scala/ethabi/types/generated/Uint128.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Uint128.scala rename to ethabi/src/main/scala/ethabi/types/generated/Uint128.scala diff --git a/src/main/scala/ethabi/types/generated/Uint136.scala b/ethabi/src/main/scala/ethabi/types/generated/Uint136.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Uint136.scala rename to ethabi/src/main/scala/ethabi/types/generated/Uint136.scala diff --git a/src/main/scala/ethabi/types/generated/Uint144.scala b/ethabi/src/main/scala/ethabi/types/generated/Uint144.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Uint144.scala rename to ethabi/src/main/scala/ethabi/types/generated/Uint144.scala diff --git a/src/main/scala/ethabi/types/generated/Uint152.scala b/ethabi/src/main/scala/ethabi/types/generated/Uint152.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Uint152.scala rename to ethabi/src/main/scala/ethabi/types/generated/Uint152.scala diff --git a/src/main/scala/ethabi/types/generated/Uint16.scala b/ethabi/src/main/scala/ethabi/types/generated/Uint16.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Uint16.scala rename to ethabi/src/main/scala/ethabi/types/generated/Uint16.scala diff --git a/src/main/scala/ethabi/types/generated/Uint160.scala b/ethabi/src/main/scala/ethabi/types/generated/Uint160.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Uint160.scala rename to ethabi/src/main/scala/ethabi/types/generated/Uint160.scala diff --git a/src/main/scala/ethabi/types/generated/Uint168.scala b/ethabi/src/main/scala/ethabi/types/generated/Uint168.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Uint168.scala rename to ethabi/src/main/scala/ethabi/types/generated/Uint168.scala diff --git a/src/main/scala/ethabi/types/generated/Uint176.scala b/ethabi/src/main/scala/ethabi/types/generated/Uint176.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Uint176.scala rename to ethabi/src/main/scala/ethabi/types/generated/Uint176.scala diff --git a/src/main/scala/ethabi/types/generated/Uint184.scala b/ethabi/src/main/scala/ethabi/types/generated/Uint184.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Uint184.scala rename to ethabi/src/main/scala/ethabi/types/generated/Uint184.scala diff --git a/src/main/scala/ethabi/types/generated/Uint192.scala b/ethabi/src/main/scala/ethabi/types/generated/Uint192.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Uint192.scala rename to ethabi/src/main/scala/ethabi/types/generated/Uint192.scala diff --git a/src/main/scala/ethabi/types/generated/Uint200.scala b/ethabi/src/main/scala/ethabi/types/generated/Uint200.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Uint200.scala rename to ethabi/src/main/scala/ethabi/types/generated/Uint200.scala diff --git a/src/main/scala/ethabi/types/generated/Uint208.scala b/ethabi/src/main/scala/ethabi/types/generated/Uint208.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Uint208.scala rename to ethabi/src/main/scala/ethabi/types/generated/Uint208.scala diff --git a/src/main/scala/ethabi/types/generated/Uint216.scala b/ethabi/src/main/scala/ethabi/types/generated/Uint216.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Uint216.scala rename to ethabi/src/main/scala/ethabi/types/generated/Uint216.scala diff --git a/src/main/scala/ethabi/types/generated/Uint224.scala b/ethabi/src/main/scala/ethabi/types/generated/Uint224.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Uint224.scala rename to ethabi/src/main/scala/ethabi/types/generated/Uint224.scala diff --git a/src/main/scala/ethabi/types/generated/Uint232.scala b/ethabi/src/main/scala/ethabi/types/generated/Uint232.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Uint232.scala rename to ethabi/src/main/scala/ethabi/types/generated/Uint232.scala diff --git a/src/main/scala/ethabi/types/generated/Uint24.scala b/ethabi/src/main/scala/ethabi/types/generated/Uint24.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Uint24.scala rename to ethabi/src/main/scala/ethabi/types/generated/Uint24.scala diff --git a/src/main/scala/ethabi/types/generated/Uint240.scala b/ethabi/src/main/scala/ethabi/types/generated/Uint240.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Uint240.scala rename to ethabi/src/main/scala/ethabi/types/generated/Uint240.scala diff --git a/src/main/scala/ethabi/types/generated/Uint248.scala b/ethabi/src/main/scala/ethabi/types/generated/Uint248.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Uint248.scala rename to ethabi/src/main/scala/ethabi/types/generated/Uint248.scala diff --git a/src/main/scala/ethabi/types/generated/Uint256.scala b/ethabi/src/main/scala/ethabi/types/generated/Uint256.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Uint256.scala rename to ethabi/src/main/scala/ethabi/types/generated/Uint256.scala diff --git a/src/main/scala/ethabi/types/generated/Uint32.scala b/ethabi/src/main/scala/ethabi/types/generated/Uint32.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Uint32.scala rename to ethabi/src/main/scala/ethabi/types/generated/Uint32.scala diff --git a/src/main/scala/ethabi/types/generated/Uint40.scala b/ethabi/src/main/scala/ethabi/types/generated/Uint40.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Uint40.scala rename to ethabi/src/main/scala/ethabi/types/generated/Uint40.scala diff --git a/src/main/scala/ethabi/types/generated/Uint48.scala b/ethabi/src/main/scala/ethabi/types/generated/Uint48.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Uint48.scala rename to ethabi/src/main/scala/ethabi/types/generated/Uint48.scala diff --git a/src/main/scala/ethabi/types/generated/Uint56.scala b/ethabi/src/main/scala/ethabi/types/generated/Uint56.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Uint56.scala rename to ethabi/src/main/scala/ethabi/types/generated/Uint56.scala diff --git a/src/main/scala/ethabi/types/generated/Uint64.scala b/ethabi/src/main/scala/ethabi/types/generated/Uint64.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Uint64.scala rename to ethabi/src/main/scala/ethabi/types/generated/Uint64.scala diff --git a/src/main/scala/ethabi/types/generated/Uint72.scala b/ethabi/src/main/scala/ethabi/types/generated/Uint72.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Uint72.scala rename to ethabi/src/main/scala/ethabi/types/generated/Uint72.scala diff --git a/src/main/scala/ethabi/types/generated/Uint8.scala b/ethabi/src/main/scala/ethabi/types/generated/Uint8.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Uint8.scala rename to ethabi/src/main/scala/ethabi/types/generated/Uint8.scala diff --git a/src/main/scala/ethabi/types/generated/Uint80.scala b/ethabi/src/main/scala/ethabi/types/generated/Uint80.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Uint80.scala rename to ethabi/src/main/scala/ethabi/types/generated/Uint80.scala diff --git a/src/main/scala/ethabi/types/generated/Uint88.scala b/ethabi/src/main/scala/ethabi/types/generated/Uint88.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Uint88.scala rename to ethabi/src/main/scala/ethabi/types/generated/Uint88.scala diff --git a/src/main/scala/ethabi/types/generated/Uint96.scala b/ethabi/src/main/scala/ethabi/types/generated/Uint96.scala similarity index 100% rename from src/main/scala/ethabi/types/generated/Uint96.scala rename to ethabi/src/main/scala/ethabi/types/generated/Uint96.scala diff --git a/src/main/scala/ethabi/types/package.scala b/ethabi/src/main/scala/ethabi/types/package.scala similarity index 100% rename from src/main/scala/ethabi/types/package.scala rename to ethabi/src/main/scala/ethabi/types/package.scala diff --git a/src/main/scala/ethabi/util/Hash.scala b/ethabi/src/main/scala/ethabi/util/Hash.scala similarity index 100% rename from src/main/scala/ethabi/util/Hash.scala rename to ethabi/src/main/scala/ethabi/util/Hash.scala diff --git a/src/main/scala/ethabi/util/Hex.scala b/ethabi/src/main/scala/ethabi/util/Hex.scala similarity index 87% rename from src/main/scala/ethabi/util/Hex.scala rename to ethabi/src/main/scala/ethabi/util/Hex.scala index 317a4da..ad291a8 100644 --- a/src/main/scala/ethabi/util/Hex.scala +++ b/ethabi/src/main/scala/ethabi/util/Hex.scala @@ -3,13 +3,13 @@ package ethabi.util import java.math.BigInteger object Hex { - private def removePrefix(hex: String): String = { + private def removePrefixIfExist(hex: String): String = { if (hex.startsWith("0x") || hex.startsWith("0X")) hex.slice(2, hex.length) else hex } def hex2Bytes(hex: String): Array[Byte] = { - val slice = removePrefix(hex) + val slice = removePrefixIfExist(hex) slice.toSeq.sliding(2, 2).toArray.map(v => Integer.parseInt(v.toString, 16).toByte) } @@ -36,7 +36,7 @@ object Hex { } def hex2Int(hex: String): Int = { - val slice = removePrefix(hex) + val slice = removePrefixIfExist(hex) java.lang.Integer.parseInt(slice, 16) } @@ -46,7 +46,7 @@ object Hex { } def hex2Long(hex: String): Long = { - val slice = removePrefix(hex) + val slice = removePrefixIfExist(hex) java.lang.Long.parseLong(slice, 16) } } diff --git a/src/main/scala/ethabi/util/package.scala b/ethabi/src/main/scala/ethabi/util/package.scala similarity index 100% rename from src/main/scala/ethabi/util/package.scala rename to ethabi/src/main/scala/ethabi/util/package.scala diff --git a/ethabi/src/test/scala/ethabi/protocol/ResponseSpec.scala b/ethabi/src/test/scala/ethabi/protocol/ResponseSpec.scala new file mode 100644 index 0000000..73d9a8a --- /dev/null +++ b/ethabi/src/test/scala/ethabi/protocol/ResponseSpec.scala @@ -0,0 +1,69 @@ +package ethabi +package protocol + +import io.circe._ +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpec +import Response._ + +class ResponseSpec extends AnyWordSpec with Matchers { + "test decode header" in { + val headerJson = jawn.parse( + s""" + |{ + | "parentHash": "0xe99e022112df268087ea7eafaf4790497fd21dbeeb6bd7a1721df161a6657a54", + | "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + | "miner": "0xbb7b8287f3f0a933474a79eae42cbca977791171", + | "stateRoot": "0xddc8b0234c2e0cad087c8b389aa7ef01f7d79b2570bccb77ce48648aa61c904d", + | "transactionsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + | "difficulty": "0x4ea3f27bc", + | "extraData": "0x476574682f4c5649562f76312e302e302f6c696e75782f676f312e342e32", + | "gasLimit": "0x1388", + | "gasUsed": "0x0", + | "hash": "0xdc0818cf78f21a8e70579cb46a43643f78291264dda342ae31049421c82d21ae", + | "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + | "miner": "0xbb7b8287f3f0a933474a79eae42cbca977791171", + | "mixHash": "0x4fffe9ae21f1c9e15207b1f472d5bbdd68c9595d461666602f2be20daf5e7843", + | "nonce": "0x689056015818adbe", + | "number": "0x1b4", + | "receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + | "size": "0x220", + | "timestamp": "0x55ba467c", + | "totalDifficulty": "0x78ed983323d" + |} + |""".stripMargin + ).getOrElse(null) + Decoder[Header].decodeJson(headerJson).fold(err => err.printStackTrace(), println) + } + + "test decode block" in { + val blockJson = jawn.parse( + s""" + |{ + | "parentHash": "0xe99e022112df268087ea7eafaf4790497fd21dbeeb6bd7a1721df161a6657a54", + | "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + | "miner": "0xbb7b8287f3f0a933474a79eae42cbca977791171", + | "stateRoot": "0xddc8b0234c2e0cad087c8b389aa7ef01f7d79b2570bccb77ce48648aa61c904d", + | "transactionsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + | "difficulty": "0x4ea3f27bc", + | "extraData": "0x476574682f4c5649562f76312e302e302f6c696e75782f676f312e342e32", + | "gasLimit": "0x1388", + | "gasUsed": "0x0", + | "hash": "0xdc0818cf78f21a8e70579cb46a43643f78291264dda342ae31049421c82d21ae", + | "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + | "miner": "0xbb7b8287f3f0a933474a79eae42cbca977791171", + | "mixHash": "0x4fffe9ae21f1c9e15207b1f472d5bbdd68c9595d461666602f2be20daf5e7843", + | "nonce": "0x689056015818adbe", + | "number": "0x1b4", + | "receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + | "size": "0x220", + | "timestamp": "0x55ba467c", + | "totalDifficulty": "0x78ed983323d", + | "transactions": ["0xe99e022112df268087ea7eafaf4790497fd21dbeeb6bd7a1721df161a6657a54"], + | "uncles": ["0xe99e022112df268087ea7eafaf4790497fd21dbeeb6bd7a1721df161a6657a54"] + |} + |""".stripMargin + ).getOrElse(null) + Decoder[Block].decodeJson(blockJson).fold(exp => throw exp, println) + } +} diff --git a/src/test/scala/ethabi/types/AddressSpec.scala b/ethabi/src/test/scala/ethabi/types/AddressSpec.scala similarity index 56% rename from src/test/scala/ethabi/types/AddressSpec.scala rename to ethabi/src/test/scala/ethabi/types/AddressSpec.scala index b6a81f1..a129259 100644 --- a/src/test/scala/ethabi/types/AddressSpec.scala +++ b/ethabi/src/test/scala/ethabi/types/AddressSpec.scala @@ -1,19 +1,20 @@ package ethabi.types -import org.scalatest.{WordSpec, Matchers} +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpec import ethabi.util.Hex -class AddressSpec extends WordSpec with Matchers { +class AddressSpec extends AnyWordSpec with Matchers { "test address encode" in { val bytes = Array.fill[Byte](20)(0x24) // 0000000000000000000000002424242424242424242424242424242424242424 - Hex.bytes2Hex(Address.typeInfo.encode(Address(bytes))) shouldBe "0000000000000000000000002424242424242424242424242424242424242424" + Hex.bytes2Hex(TypeInfo[Address].encode(Address(bytes))) shouldBe "0000000000000000000000002424242424242424242424242424242424242424" } "test address decode" in { val bytes = Array.fill[Byte](20)(0x24) val encoded = Hex.hex2Bytes("0000000000000000000000002424242424242424242424242424242424242424") - val (result, consumed) = Address.typeInfo.decode(encoded, 0) + val (result, consumed) = TypeInfo[Address].decode(encoded, 0) result.value shouldBe bytes consumed shouldBe encoded.length } diff --git a/src/test/scala/ethabi/types/DynamicArraySpec.scala b/ethabi/src/test/scala/ethabi/types/DynamicArraySpec.scala similarity index 69% rename from src/test/scala/ethabi/types/DynamicArraySpec.scala rename to ethabi/src/test/scala/ethabi/types/DynamicArraySpec.scala index 91436c1..85b82ac 100644 --- a/src/test/scala/ethabi/types/DynamicArraySpec.scala +++ b/ethabi/src/test/scala/ethabi/types/DynamicArraySpec.scala @@ -1,13 +1,13 @@ package ethabi.types -import org.scalatest.{WordSpec, Matchers} +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpec import ethabi.util.Hex -class DynamicArraySpec extends WordSpec with Matchers { +class DynamicArraySpec extends AnyWordSpec with Matchers { import DynamicBytes._ val bytes = DynamicBytes(Array.fill[Byte](50)(0x24)) "test bytes[] encode" in { - val typeInfo = implicitly[TypeInfo[DynamicArray[DynamicBytes]]] // 0000000000000000000000000000000000000000000000000000000000000003 + // 0000000000000000000000000000000000000000000000000000000000000060 + // 00000000000000000000000000000000000000000000000000000000000000c0 + @@ -21,13 +21,12 @@ class DynamicArraySpec extends WordSpec with Matchers { // 0000000000000000000000000000000000000000000000000000000000000032 + // 2424242424242424242424242424242424242424242424242424242424242424 + // 2424242424242424242424242424242424240000000000000000000000000000 - Hex.bytes2Hex(typeInfo.encode(DynamicArray(Seq.fill[DynamicBytes](3)(bytes)))) shouldBe "0000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000003224242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424240000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003224242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424240000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003224242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424240000000000000000000000000000" + Hex.bytes2Hex(TypeInfo[DynamicArray[DynamicBytes]].encode(DynamicArray(Seq.fill[DynamicBytes](3)(bytes)))) shouldBe "0000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000003224242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424240000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003224242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424240000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003224242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424240000000000000000000000000000" } "test bytes[] decode" in { val encoded = Hex.hex2Bytes("0000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000003224242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424240000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003224242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424240000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003224242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424240000000000000000000000000000") - val typeInfo = implicitly[TypeInfo[DynamicArray[DynamicBytes]]] - val (result, consumed) = typeInfo.decode(encoded, 0) + val (result, consumed) = TypeInfo[DynamicArray[DynamicBytes]].decode(encoded, 0) result.values.length shouldBe 3 result.values.map(_.value).foreach(_ shouldBe bytes.value) consumed shouldBe encoded.length diff --git a/src/test/scala/ethabi/types/DynamicBytesSpec.scala b/ethabi/src/test/scala/ethabi/types/DynamicBytesSpec.scala similarity index 62% rename from src/test/scala/ethabi/types/DynamicBytesSpec.scala rename to ethabi/src/test/scala/ethabi/types/DynamicBytesSpec.scala index 5f96292..6c5d999 100644 --- a/src/test/scala/ethabi/types/DynamicBytesSpec.scala +++ b/ethabi/src/test/scala/ethabi/types/DynamicBytesSpec.scala @@ -1,20 +1,21 @@ package ethabi.types -import org.scalatest.{Matchers, WordSpec} +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpec import ethabi.util.Hex -class DynamicBytesSpec extends WordSpec with Matchers { +class DynamicBytesSpec extends AnyWordSpec with Matchers { "test dynamic bytes encode(length = 20)" in { val bytes = Array.fill[Byte](20)(0x24) // 0000000000000000000000000000000000000000000000000000000000000014 + // 2424242424242424242424242424242424242424000000000000000000000000 - Hex.bytes2Hex(DynamicBytes.typeInfo.encode(DynamicBytes(bytes))) shouldBe "00000000000000000000000000000000000000000000000000000000000000142424242424242424242424242424242424242424000000000000000000000000" + Hex.bytes2Hex(TypeInfo[DynamicBytes].encode(DynamicBytes(bytes))) shouldBe "00000000000000000000000000000000000000000000000000000000000000142424242424242424242424242424242424242424000000000000000000000000" } "test dynamic bytes decode(length = 20)" in { val bytes = Array.fill[Byte](20)(0x24) val encoded = Hex.hex2Bytes("00000000000000000000000000000000000000000000000000000000000000142424242424242424242424242424242424242424000000000000000000000000") - val (result, consumed) = DynamicBytes.typeInfo.decode(encoded, 0) + val (result, consumed) = TypeInfo[DynamicBytes].decode(encoded, 0) result.value shouldBe bytes consumed shouldBe 64 } @@ -24,13 +25,13 @@ class DynamicBytesSpec extends WordSpec with Matchers { // 0000000000000000000000000000000000000000000000000000000000000032 + // 2424242424242424242424242424242424242424242424242424242424242424 + // 2424242424242424242424242424242424240000000000000000000000000000 - Hex.bytes2Hex(DynamicBytes.typeInfo.encode(DynamicBytes(bytes))) shouldBe "000000000000000000000000000000000000000000000000000000000000003224242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424240000000000000000000000000000" + Hex.bytes2Hex(TypeInfo[DynamicBytes].encode(DynamicBytes(bytes))) shouldBe "000000000000000000000000000000000000000000000000000000000000003224242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424240000000000000000000000000000" } "test dynamic bytes decode(length = 50)" in { val bytes = Array.fill[Byte](50)(0x24) val encoded = Hex.hex2Bytes("000000000000000000000000000000000000000000000000000000000000003224242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424240000000000000000000000000000") - val (result, consumed) = DynamicBytes.typeInfo.decode(encoded, 0) + val (result, consumed) = TypeInfo[DynamicBytes].decode(encoded, 0) result.value shouldBe bytes consumed shouldBe encoded.length } diff --git a/ethabi/src/test/scala/ethabi/types/IntTypeSpec.scala b/ethabi/src/test/scala/ethabi/types/IntTypeSpec.scala new file mode 100644 index 0000000..987606c --- /dev/null +++ b/ethabi/src/test/scala/ethabi/types/IntTypeSpec.scala @@ -0,0 +1,32 @@ +package ethabi.types + +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpec +import ethabi.types.generated._ +import ethabi.util.Hex + +class IntTypeSpec extends AnyWordSpec with Matchers { + "test int8 encode" in { + // fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff4 + Hex.bytes2Hex(TypeInfo[Int8].encode(Int8(BigInt(-12)))) shouldBe "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff4" + } + + "test int8 decode" in { + val encoded = Hex.hex2Bytes("fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff4") + val (result, consumed) = TypeInfo[Int8].decode(encoded, 0) + result.value.toInt shouldBe -12 + consumed shouldBe 32 + } + + "test int16 encode" in { + // 0000000000000000000000000000000000000000000000000000000000002710 + Hex.bytes2Hex(TypeInfo[Int16].encode(Int16(BigInt(10000)))) shouldBe "0000000000000000000000000000000000000000000000000000000000002710" + } + + "test int16 decode" in { + val encoded = Hex.hex2Bytes("0000000000000000000000000000000000000000000000000000000000002710") + val (result, consumed) = TypeInfo[Int16].decode(encoded, 0) + result.value.toInt shouldBe 10000 + consumed shouldBe 32 + } +} diff --git a/src/test/scala/ethabi/types/StaticArraySpec.scala b/ethabi/src/test/scala/ethabi/types/StaticArraySpec.scala similarity index 64% rename from src/test/scala/ethabi/types/StaticArraySpec.scala rename to ethabi/src/test/scala/ethabi/types/StaticArraySpec.scala index c251654..15ac0c1 100644 --- a/src/test/scala/ethabi/types/StaticArraySpec.scala +++ b/ethabi/src/test/scala/ethabi/types/StaticArraySpec.scala @@ -1,13 +1,13 @@ package ethabi.types -import org.scalatest.{WordSpec, Matchers} +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpec import ethabi.util.Hex -class StaticArraySpec extends WordSpec with Matchers { +class StaticArraySpec extends AnyWordSpec with Matchers { val bytes = DynamicBytes(Array.fill[Byte](50)(0x24)) "test bytes[3] encode" in { implicit val length: Int = 3 - val typeInfo = implicitly[TypeInfo[StaticArray[DynamicBytes]]] // 0000000000000000000000000000000000000000000000000000000000000060 + // 00000000000000000000000000000000000000000000000000000000000000c0 + // 0000000000000000000000000000000000000000000000000000000000000120 + @@ -20,14 +20,13 @@ class StaticArraySpec extends WordSpec with Matchers { // 0000000000000000000000000000000000000000000000000000000000000032 + // 2424242424242424242424242424242424242424242424242424242424242424 + // 2424242424242424242424242424242424240000000000000000000000000000 - Hex.bytes2Hex(typeInfo.encode(StaticArray(Seq.fill[DynamicBytes](3)(bytes)))) shouldBe "000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000003224242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424240000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003224242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424240000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003224242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424240000000000000000000000000000" + Hex.bytes2Hex(TypeInfo[StaticArray[DynamicBytes]].encode(StaticArray(Seq.fill[DynamicBytes](3)(bytes)))) shouldBe "000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000003224242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424240000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003224242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424240000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003224242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424240000000000000000000000000000" } "test bytes[3] decode" in { val encoded = Hex.hex2Bytes("000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000003224242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424240000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003224242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424240000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003224242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424240000000000000000000000000000") implicit val length: Int = 3 - val typeInfo = implicitly[TypeInfo[StaticArray[DynamicBytes]]] - val (result, consumed) = typeInfo.decode(encoded, 0) + val (result, consumed) = TypeInfo[StaticArray[DynamicBytes]].decode(encoded, 0) result.values.length shouldBe length result.values.map(_.value).foreach(_ shouldBe bytes.value) consumed shouldBe encoded.length @@ -36,19 +35,17 @@ class StaticArraySpec extends WordSpec with Matchers { val address = Address(Array.fill[Byte](20)(0x24)) "test address[4] encode" in { implicit val length: Int = 4 - val typeInfo = implicitly[TypeInfo[StaticArray[Address]]] // 0000000000000000000000002424242424242424242424242424242424242424 + // 0000000000000000000000002424242424242424242424242424242424242424 + // 0000000000000000000000002424242424242424242424242424242424242424 + // 0000000000000000000000002424242424242424242424242424242424242424 - Hex.bytes2Hex(typeInfo.encode(StaticArray(Seq.fill[Address](4)(address)))) shouldBe "0000000000000000000000002424242424242424242424242424242424242424000000000000000000000000242424242424242424242424242424242424242400000000000000000000000024242424242424242424242424242424242424240000000000000000000000002424242424242424242424242424242424242424" + Hex.bytes2Hex(TypeInfo[StaticArray[Address]].encode(StaticArray(Seq.fill[Address](4)(address)))) shouldBe "0000000000000000000000002424242424242424242424242424242424242424000000000000000000000000242424242424242424242424242424242424242400000000000000000000000024242424242424242424242424242424242424240000000000000000000000002424242424242424242424242424242424242424" } "test address[4] decode" in { implicit val length: Int = 4 - val typeInfo = implicitly[TypeInfo[StaticArray[Address]]] val encoded = Hex.hex2Bytes("0000000000000000000000002424242424242424242424242424242424242424000000000000000000000000242424242424242424242424242424242424242400000000000000000000000024242424242424242424242424242424242424240000000000000000000000002424242424242424242424242424242424242424") - val (result, consumed) = typeInfo.decode(encoded, 0) + val (result, consumed) = TypeInfo[StaticArray[Address]].decode(encoded, 0) result.values.length shouldBe length result.values.map(_.value).foreach(_ shouldBe address.value) consumed shouldBe encoded.length diff --git a/src/test/scala/ethabi/types/StaticBytesSpec.scala b/ethabi/src/test/scala/ethabi/types/StaticBytesSpec.scala similarity index 58% rename from src/test/scala/ethabi/types/StaticBytesSpec.scala rename to ethabi/src/test/scala/ethabi/types/StaticBytesSpec.scala index 257cc0b..0a8ca8d 100644 --- a/src/test/scala/ethabi/types/StaticBytesSpec.scala +++ b/ethabi/src/test/scala/ethabi/types/StaticBytesSpec.scala @@ -1,20 +1,21 @@ package ethabi.types -import org.scalatest.{Matchers, WordSpec} +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpec import ethabi.types.generated._ import ethabi.util.Hex -class StaticBytesSpec extends WordSpec with Matchers { +class StaticBytesSpec extends AnyWordSpec with Matchers { "test bytes5 encode" in { val bytes = Array[Byte](0x01, 0x02, 0x03, 0x04, 0x05) // 0102030405000000000000000000000000000000000000000000000000000000 - Hex.bytes2Hex(Bytes5.typeInfo.encode(Bytes5(bytes))) shouldBe "0102030405000000000000000000000000000000000000000000000000000000" + Hex.bytes2Hex(TypeInfo[Bytes5].encode(Bytes5(bytes))) shouldBe "0102030405000000000000000000000000000000000000000000000000000000" } "test bytes5 decode" in { val bytes = Array[Byte](0x01, 0x02, 0x03, 0x04, 0x05) val encoded = Hex.hex2Bytes("0102030405000000000000000000000000000000000000000000000000000000") - val (result, consumed) = Bytes5.typeInfo.decode(encoded, 0) + val (result, consumed) = TypeInfo[Bytes5].decode(encoded, 0) result.value shouldBe bytes consumed shouldBe 32 } diff --git a/src/test/scala/ethabi/types/StringTypeSpec.scala b/ethabi/src/test/scala/ethabi/types/StringTypeSpec.scala similarity index 57% rename from src/test/scala/ethabi/types/StringTypeSpec.scala rename to ethabi/src/test/scala/ethabi/types/StringTypeSpec.scala index 5879a82..b7eea66 100644 --- a/src/test/scala/ethabi/types/StringTypeSpec.scala +++ b/ethabi/src/test/scala/ethabi/types/StringTypeSpec.scala @@ -1,19 +1,20 @@ package ethabi.types -import org.scalatest.{WordSpec, Matchers} +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpec import ethabi.util.Hex -class StringTypeSpec extends WordSpec with Matchers { +class StringTypeSpec extends AnyWordSpec with Matchers { "test utf8 string encode" in { val value = StringType("以太坊") // 0000000000000000000000000000000000000000000000000000000000000009 + // e4bba5e5a4aae59d8a0000000000000000000000000000000000000000000000 - Hex.bytes2Hex(StringType.typeInfo.encode(value)) shouldBe "0000000000000000000000000000000000000000000000000000000000000009e4bba5e5a4aae59d8a0000000000000000000000000000000000000000000000" + Hex.bytes2Hex(TypeInfo[StringType].encode(value)) shouldBe "0000000000000000000000000000000000000000000000000000000000000009e4bba5e5a4aae59d8a0000000000000000000000000000000000000000000000" } "test utf8 string decode" in { val encoded = Hex.hex2Bytes("0000000000000000000000000000000000000000000000000000000000000009e4bba5e5a4aae59d8a0000000000000000000000000000000000000000000000") - val (result, consumed) = StringType.typeInfo.decode(encoded, 0) + val (result, consumed) = TypeInfo[StringType].decode(encoded, 0) result.value shouldBe "以太坊" consumed shouldBe encoded.length } diff --git a/src/test/scala/ethabi/types/TupleTypeSpec.scala b/ethabi/src/test/scala/ethabi/types/TupleTypeSpec.scala similarity index 79% rename from src/test/scala/ethabi/types/TupleTypeSpec.scala rename to ethabi/src/test/scala/ethabi/types/TupleTypeSpec.scala index c2dd2c9..7d0ec0c 100644 --- a/src/test/scala/ethabi/types/TupleTypeSpec.scala +++ b/ethabi/src/test/scala/ethabi/types/TupleTypeSpec.scala @@ -1,33 +1,28 @@ package ethabi.types -import org.scalatest.{Matchers, WordSpec} +import org.scalatest.wordspec.AnyWordSpec +import org.scalatest.matchers.should.Matchers import ethabi.types.generated._ import ethabi.util.Hex -class TupleTypeSpec extends WordSpec with Matchers { - import Uint256._ - import Address._ - import DynamicBytes._ - +class TupleTypeSpec extends AnyWordSpec with Matchers { val number = Uint256(BigInt(10000)) val address = Address(Array.fill[Byte](20)(0x24)) val bytes = DynamicBytes(Array.fill[Byte](20)(0x13)) "test tuple3(uint256, address, bytes) encode" in { val tuple = TupleType3.from(Seq(number, address, bytes)) - val tupleTypeInfo = implicitly[TypeInfo[TupleType3[Uint256, Address, DynamicBytes]]] // 0000000000000000000000000000000000000000000000000000000000002710 + // 0000000000000000000000002424242424242424242424242424242424242424 + // 0000000000000000000000000000000000000000000000000000000000000060 + // 0000000000000000000000000000000000000000000000000000000000000014 + // 1313131313131313131313131313131313131313000000000000000000000000 - Hex.bytes2Hex(tupleTypeInfo.encode(tuple)) shouldBe "00000000000000000000000000000000000000000000000000000000000027100000000000000000000000002424242424242424242424242424242424242424000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000141313131313131313131313131313131313131313000000000000000000000000" + Hex.bytes2Hex(TypeInfo[TupleType3[Uint256, Address, DynamicBytes]].encode(tuple)) shouldBe "00000000000000000000000000000000000000000000000000000000000027100000000000000000000000002424242424242424242424242424242424242424000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000141313131313131313131313131313131313131313000000000000000000000000" } "test tuple3(uint256, address, bytes) decode" in { val encoded = Hex.hex2Bytes("00000000000000000000000000000000000000000000000000000000000027100000000000000000000000002424242424242424242424242424242424242424000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000141313131313131313131313131313131313131313000000000000000000000000") - val tupleTypeInfo = implicitly[TypeInfo[TupleType3[Uint256, Address, DynamicBytes]]] - val (result, consumed) = tupleTypeInfo.decode(encoded, 0) + val (result, consumed) = TypeInfo[TupleType3[Uint256, Address, DynamicBytes]].decode(encoded, 0) result._1.value.toInt shouldBe number.value.toInt result._2.value shouldBe address.value result._3.value shouldBe bytes.value @@ -37,7 +32,7 @@ class TupleTypeSpec extends WordSpec with Matchers { "test tuple3(tuple2(bytes, address), uint256, bytes) encode" in { val tuple2 = TupleType2[DynamicBytes, Address](bytes, address) val tuple3 = TupleType3[TupleType2[DynamicBytes, Address], Uint256, DynamicBytes](tuple2, number, bytes) - val tupleTypeInfo = implicitly[TypeInfo[TupleType3[TupleType2[DynamicBytes, Address], Uint256, DynamicBytes]]] + val tupleTypeInfo = TypeInfo[TupleType3[TupleType2[DynamicBytes, Address], Uint256, DynamicBytes]] // 0000000000000000000000000000000000000000000000000000000000000060 + // 0000000000000000000000000000000000000000000000000000000000002710 + // 00000000000000000000000000000000000000000000000000000000000000e0 + @@ -52,7 +47,7 @@ class TupleTypeSpec extends WordSpec with Matchers { "test tuple3(tuple2(bytes, address), uint256, bytes) decode" in { val encoded = Hex.hex2Bytes("0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000271000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000024242424242424242424242424242424242424240000000000000000000000000000000000000000000000000000000000000014131313131313131313131313131313131313131300000000000000000000000000000000000000000000000000000000000000000000000000000000000000141313131313131313131313131313131313131313000000000000000000000000") - val tupleTypeInfo = implicitly[TypeInfo[TupleType3[TupleType2[DynamicBytes, Address], Uint256, DynamicBytes]]] + val tupleTypeInfo = TypeInfo[TupleType3[TupleType2[DynamicBytes, Address], Uint256, DynamicBytes]] val (result, consumed) = tupleTypeInfo.decode(encoded, 0) result._1._1.value shouldBe bytes.value result._1._2.value shouldBe address.value diff --git a/ethabi/src/test/scala/ethabi/types/UintTypeSpec.scala b/ethabi/src/test/scala/ethabi/types/UintTypeSpec.scala new file mode 100644 index 0000000..23266d3 --- /dev/null +++ b/ethabi/src/test/scala/ethabi/types/UintTypeSpec.scala @@ -0,0 +1,32 @@ +package ethabi.types + +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpec +import ethabi.types.generated._ +import ethabi.util.Hex + +class UintTypeSpec extends AnyWordSpec with Matchers { + // 000000000000000000000000000000000000000000000000000000000000000c + "test uint8 encode" in { + Hex.bytes2Hex(TypeInfo[Uint8].encode(Uint8(BigInt(12)))) shouldBe "000000000000000000000000000000000000000000000000000000000000000c" + } + + "test uint8 decode" in { + val encoded = Hex.hex2Bytes("000000000000000000000000000000000000000000000000000000000000000c") + val (result, consumed) = TypeInfo[Uint8].decode(encoded, 0) + result.value.toInt shouldBe 12 + consumed shouldBe 32 + } + + "test uint16 encode" in { + // 0000000000000000000000000000000000000000000000000000000000002710 + Hex.bytes2Hex(TypeInfo[Uint16].encode(Uint16(BigInt(10000)))) shouldBe "0000000000000000000000000000000000000000000000000000000000002710" + } + + "test uint16 decode" in { + val encoded = Hex.hex2Bytes("0000000000000000000000000000000000000000000000000000000000002710") + val (result, consumed) = TypeInfo[Uint16].decode(encoded, 0) + result.value.toInt shouldBe 10000 + consumed shouldBe 32 + } +} diff --git a/examples/src/main/resources/KVStore.abi b/examples/src/main/resources/KVStore.abi new file mode 100644 index 0000000..17a3ad7 --- /dev/null +++ b/examples/src/main/resources/KVStore.abi @@ -0,0 +1 @@ +[{"constant":true,"inputs":[{"name":"","type":"uint16"}],"name":"data","outputs":[{"name":"","type":"bytes"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_key","type":"uint16"},{"name":"_value","type":"bytes"}],"name":"set","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[{"name":"_key","type":"uint16"}],"name":"get","outputs":[{"name":"","type":"bytes"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[],"payable":true,"stateMutability":"payable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"sender","type":"address"},{"indexed":true,"name":"key","type":"uint16"},{"indexed":false,"name":"value","type":"bytes"}],"name":"Record","type":"event"}] \ No newline at end of file diff --git a/examples/src/main/resources/KVStore.bin b/examples/src/main/resources/KVStore.bin new file mode 100644 index 0000000..a1d9536 --- /dev/null +++ b/examples/src/main/resources/KVStore.bin @@ -0,0 +1 @@ +6080604052610542806100136000396000f300608060405260043610610057576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630df584f11461005c578063385ddf0014610106578063b3fd15a014610170575b600080fd5b34801561006857600080fd5b5061008b600480360381019080803561ffff16906020019092919050505061021a565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100cb5780820151818401526020810190506100b0565b50505050905090810190601f1680156100f85780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61016e600480360381019080803561ffff169060200190929190803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091929192905050506102ca565b005b34801561017c57600080fd5b5061019f600480360381019080803561ffff1690602001909291905050506103b5565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156101df5780820151818401526020810190506101c4565b50505050905090810190601f16801561020c5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b60006020528060005260406000206000915090508054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156102c25780601f10610297576101008083540402835291602001916102c2565b820191906000526020600020905b8154815290600101906020018083116102a557829003601f168201915b505050505081565b806000808461ffff1661ffff16815260200190815260200160002090805190602001906102f8929190610471565b508161ffff163373ffffffffffffffffffffffffffffffffffffffff167f5f9f5e049bc49bc3d2067c5c5077871baab0b4e104a1554c8ac68f7c9fd81884836040518080602001828103825283818151815260200191508051906020019080838360005b8381101561037757808201518184015260208101905061035c565b50505050905090810190601f1680156103a45780820380516001836020036101000a031916815260200191505b509250505060405180910390a35050565b60606000808361ffff1661ffff1681526020019081526020016000208054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156104655780601f1061043a57610100808354040283529160200191610465565b820191906000526020600020905b81548152906001019060200180831161044857829003601f168201915b50505050509050919050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106104b257805160ff19168380011785556104e0565b828001600101855582156104e0579182015b828111156104df5782518255916020019190600101906104c4565b5b5090506104ed91906104f1565b5090565b61051391905b8082111561050f5760008160009055506001016104f7565b5090565b905600a165627a7a7230582061428827ea316a5ddc2cc0c135b621c5525c61031d2556d7586f05dd0764c9810029 \ No newline at end of file diff --git a/examples/src/main/resources/Trivial.abi b/examples/src/main/resources/Trivial.abi deleted file mode 100644 index 98920a4..0000000 --- a/examples/src/main/resources/Trivial.abi +++ /dev/null @@ -1 +0,0 @@ -[{"constant":false,"inputs":[{"components":[{"name":"a","type":"uint256"},{"name":"b","type":"bytes"},{"name":"c","type":"uint256"},{"name":"d","type":"bytes"}],"name":"t","type":"tuple"}],"name":"trigger","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"a","type":"uint256"},{"indexed":false,"name":"b","type":"bytes"},{"indexed":true,"name":"c","type":"uint256"},{"indexed":false,"name":"d","type":"bytes"}],"name":"TestEvent","type":"event"}] \ No newline at end of file diff --git a/examples/src/main/resources/Trivial.bin b/examples/src/main/resources/Trivial.bin deleted file mode 100644 index 2a93ae5..0000000 --- a/examples/src/main/resources/Trivial.bin +++ /dev/null @@ -1 +0,0 @@ -608060405234801561001057600080fd5b50610374806100206000396000f300608060405260043610610041576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806385799c3614610046575b600080fd5b34801561005257600080fd5b5061006d600480360361006891908101906101cb565b61006f565b005b806040015181600001517f174e799db4473820d2ac095205358bba921759d7f2fb09509751a40a543842aa836020015184606001516040516100b2929190610242565b60405180910390a350565b600082601f83011215156100d057600080fd5b81356100e36100de826102a6565b610279565b915080825260208301602083018583830111156100ff57600080fd5b61010a8382846102e7565b50505092915050565b60006080828403121561012557600080fd5b61012f6080610279565b9050600061013f848285016101b7565b600083015250602082013567ffffffffffffffff81111561015f57600080fd5b61016b848285016100bd565b602083015250604061017f848285016101b7565b604083015250606082013567ffffffffffffffff81111561019f57600080fd5b6101ab848285016100bd565b60608301525092915050565b60006101c382356102dd565b905092915050565b6000602082840312156101dd57600080fd5b600082013567ffffffffffffffff8111156101f757600080fd5b61020384828501610113565b91505092915050565b6000610217826102d2565b80845261022b8160208601602086016102f6565b61023481610329565b602085010191505092915050565b6000604082019050818103600083015261025c818561020c565b90508181036020830152610270818461020c565b90509392505050565b6000604051905081810181811067ffffffffffffffff8211171561029c57600080fd5b8060405250919050565b600067ffffffffffffffff8211156102bd57600080fd5b601f19601f8301169050602081019050919050565b600081519050919050565b6000819050919050565b82818337600083830152505050565b60005b838110156103145780820151818401526020810190506102f9565b83811115610323576000848401525b50505050565b6000601f19601f83011690509190505600a265627a7a723058201ce538708976bc5eb642066442b9156e9bf442c7f38e3977bac329d2191e7b236c6578706572696d656e74616cf50037 \ No newline at end of file diff --git a/examples/src/main/scala/examples/exchange/Exchange.scala b/examples/src/main/scala/examples/exchange/Exchange.scala index 1969910..81cc294 100644 --- a/examples/src/main/scala/examples/exchange/Exchange.scala +++ b/examples/src/main/scala/examples/exchange/Exchange.scala @@ -1,330 +1,331 @@ // AUTO GENERATED, DO NOT EDIT package examples.exchange -import akka.NotUsed -import akka.stream.scaladsl.Source -import ethabi.util.{ Hex, Hash } +import ethabi.util._ import ethabi.types._ import ethabi.types.generated._ -import ethabi.protocol.{ Contract, EventValue } +import ethabi.protocol._ import ethabi.protocol.Request._ import ethabi.protocol.Response.Log -import scala.concurrent.Future -final class Exchange(endpoint: String) { self => - private val impl = Contract(endpoint) - import impl.dispatcher - def service = impl.service +import ethabi.protocol.Subscription.SubscriptionResult +import cats.implicits._ +import cats.Applicative +import cats.effect._ +import cats.effect.concurrent._ +final class Exchange[F[_]: ConcurrentEffect: Timer] private (private val impl: Contract[F]) { self => private val binary = "608060405260008060006101000a81548160ff0219169083151502179055503480156200002b57600080fd5b50604051620077c2380380620077c2833981018060405262000051919081019062000515565b8080600190805190602001906200006a92919062000408565b505060405160200180807f454950373132446f6d61696e2800000000000000000000000000000000000000815250600d01807f737472696e67206e616d652c0000000000000000000000000000000000000000815250600c01807f737472696e672076657273696f6e2c0000000000000000000000000000000000815250600f01807f6164647265737320766572696679696e67436f6e747261637400000000000000815250601901807f290000000000000000000000000000000000000000000000000000000000000081525060010190506040516020818303038152906040526040518082805190602001908083835b6020831015156200018357805182526020820191506020810190506020830392506200015c565b6001836020036101000a03801982511681845116808217855250505050505090500191505060405180910390206040805190810160405280600b81526020017f30782050726f746f636f6c0000000000000000000000000000000000000000008152506040518082805190602001908083835b6020831015156200021d5780518252602082019150602081019050602083039250620001f6565b6001836020036101000a03801982511681845116808217855250505050505090500191505060405180910390206040805190810160405280600181526020017f32000000000000000000000000000000000000000000000000000000000000008152506040518082805190602001908083835b602083101515620002b7578051825260208201915060208101905060208303925062000290565b6001836020036101000a03801982511681845116808217855250505050505090500191505060405180910390203073ffffffffffffffffffffffffffffffffffffffff166001026040516020018085600019166000191681526020018460001916600019168152602001836000191660001916815260200182600019166000191681526020019450505050506040516020818303038152906040526040518082805190602001908083835b60208310151562000389578051825260208201915060208101905060208303925062000362565b6001836020036101000a03801982511681845116808217855250505050505090500191505060405180910390206002816000191690555033600360006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050620005eb565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106200044b57805160ff19168380011785556200047c565b828001600101855582156200047c579182015b828111156200047b5782518255916020019190600101906200045e565b5b5090506200048b91906200048f565b5090565b620004b491905b80821115620004b057600081600090555060010162000496565b5090565b90565b600082601f8301121515620004cb57600080fd5b8151620004e2620004dc8262000588565b6200055a565b91508082526020830160208301858383011115620004ff57600080fd5b6200050c838284620005b5565b50505092915050565b6000602082840312156200052857600080fd5b600082015167ffffffffffffffff8111156200054357600080fd5b6200055184828501620004b7565b91505092915050565b6000604051905081810181811067ffffffffffffffff821117156200057e57600080fd5b8060405250919050565b600067ffffffffffffffff821115620005a057600080fd5b601f19601f8301169050602081019050919050565b60005b83811015620005d5578082015181840152602081019050620005b8565b83811115620005e5576000848401525b50505050565b6171c780620005fb6000396000f3006080604052600436106101b7576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063288cdc91146101bc578063297bb70b146101f95780632ac12622146102365780633683ef8e146102735780633c28d8611461029c5780633e228bae146102d95780633fd3c997146103165780634ac14782146103535780634d0ae5461461037c5780634f9559b1146103b957806350dde190146103e2578063607041081461041f578063642f2eaf1461045c57806364a3bc151461049957806377fcce68146104d65780637b8e3514146104ff5780637e1d98081461053c5780637e9d74dc1461057957806382c174d0146105b65780638da5cb5b146105f3578063936347021461061e578063a3e203801461065b578063b4be83d514610698578063bfc8bfce146106d5578063c585bb93146106fe578063c75e0a8114610727578063d46b02c314610764578063d9bfa73e1461078d578063db123b1a146107ca578063dd1c7d18146107f5578063e306f77914610832578063e5fa431b1461085d578063eea086ba1461089a578063f2fde38b146108c5578063ffa1ad74146108ee575b600080fd5b3480156101c857600080fd5b506101e360048036036101de91908101906159c2565b610919565b6040516101f09190616ee5565b60405180910390f35b34801561020557600080fd5b50610220600480360361021b91908101906158ac565b610931565b60405161022d9190616e93565b60405180910390f35b34801561024257600080fd5b5061025d600480360361025891908101906159c2565b610a51565b60405161026a9190616902565b60405180910390f35b34801561027f57600080fd5b5061029a60048036036102959190810190615a27565b610a71565b005b3480156102a857600080fd5b506102c360048036036102be9190810190615b8d565b610b9c565b6040516102d09190616eae565b60405180910390f35b3480156102e557600080fd5b5061030060048036036102fb9190810190615c50565b610d47565b60405161030d9190616e93565b60405180910390f35b34801561032257600080fd5b5061033d60048036036103389190810190615afa565b610d9f565b60405161034a9190616a36565b60405180910390f35b34801561035f57600080fd5b5061037a6004803603610375919081019061586b565b610dd2565b005b34801561038857600080fd5b506103a3600480360361039e91908101906158ac565b610e9e565b6040516103b09190616e93565b60405180910390f35b3480156103c557600080fd5b506103e060048036036103db9190810190615ccf565b610fbe565b005b3480156103ee57600080fd5b50610409600480360361040491908101906158ac565b611247565b6040516104169190616e93565b60405180910390f35b34801561042b57600080fd5b5061044660048036036104419190810190615afa565b6112e2565b60405161045391906167f4565b60405180910390f35b34801561046857600080fd5b50610483600480360361047e91908101906159c2565b61135d565b6040516104909190616902565b60405180910390f35b3480156104a557600080fd5b506104c060048036036104bb9190810190615c50565b61137d565b6040516104cd9190616e93565b60405180910390f35b3480156104e257600080fd5b506104fd60048036036104f8919081019061582f565b611421565b005b34801561050b57600080fd5b50610526600480360361052191908101906157f3565b6115b0565b6040516105339190616902565b60405180910390f35b34801561054857600080fd5b50610563600480360361055e9190810190615943565b6115df565b6040516105709190616e93565b60405180910390f35b34801561058557600080fd5b506105a0600480360361059b919081019061586b565b611751565b6040516105ad91906168e0565b60405180910390f35b3480156105c257600080fd5b506105dd60048036036105d891908101906159eb565b6117fb565b6040516105ea9190616902565b60405180910390f35b3480156105ff57600080fd5b5061060861182a565b60405161061591906167f4565b60405180910390f35b34801561062a57600080fd5b5061064560048036036106409190810190615a93565b611850565b6040516106529190616902565b60405180910390f35b34801561066757600080fd5b50610682600480360361067d9190810190615943565b611fcf565b60405161068f9190616e93565b60405180910390f35b3480156106a457600080fd5b506106bf60048036036106ba9190810190615c50565b612101565b6040516106cc9190616e93565b60405180910390f35b3480156106e157600080fd5b506106fc60048036036106f79190810190615cf8565b6121a5565b005b34801561070a57600080fd5b50610725600480360361072091908101906157ca565b612511565b005b34801561073357600080fd5b5061074e60048036036107499190810190615b4c565b6127fd565b60405161075b9190616eca565b60405180910390f35b34801561077057600080fd5b5061078b60048036036107869190810190615b4c565b612a4f565b005b34801561079957600080fd5b506107b460048036036107af91908101906157f3565b612ae0565b6040516107c19190616ee5565b60405180910390f35b3480156107d657600080fd5b506107df612b05565b6040516107ec9190616a14565b60405180910390f35b34801561080157600080fd5b5061081c60048036036108179190810190615943565b612ba3565b6040516108299190616e93565b60405180910390f35b34801561083e57600080fd5b50610847612c90565b604051610854919061691d565b60405180910390f35b34801561086957600080fd5b50610884600480360361087f9190810190615943565b612c96565b6040516108919190616e93565b60405180910390f35b3480156108a657600080fd5b506108af612e4d565b6040516108bc91906167f4565b60405180910390f35b3480156108d157600080fd5b506108ec60048036036108e791908101906157ca565b612e73565b005b3480156108fa57600080fd5b50610903612f80565b6040516109109190616a51565b60405180910390f35b60046020528060005260406000206000915090505481565b61093961517c565b60008061094461517c565b6000809054906101000a900460ff16151515610995576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161098c90616d93565b60405180910390fd5b60016000806101000a81548160ff02191690831515021790555086519250600091505b8282141515610a2a57610a1187838151811015156109d257fe5b9060200190602002015187848151811015156109ea57fe5b906020019060200201518785815181101515610a0257fe5b90602001906020020151612fb9565b9050610a1d848261305a565b81806001019250506109b8565b83935060008060006101000a81548160ff0219169083151502179055505050509392505050565b60056020528060005260406000206000915054906101000a900460ff1681565b3373ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16141515610b2557610ae3848484848080601f016020809104026020016040519081016040528093929190818152602001838380828437820191505050505050611850565b1515610b24576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b1b90616bf3565b60405180910390fd5b5b600160076000866000191660001916815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff02191690831515021790555050505050565b610ba46151a5565b610bac6151d4565b610bb46151d4565b60008060009054906101000a900460ff16151515610c07576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610bfe90616d93565b60405180910390fd5b60016000806101000a81548160ff021916908315150217905550876101600151876101400181905250876101400151876101600181905250610c48886127fd565b9250610c53876127fd565b9150610c5d6130ca565b9050610c6b8884838961313a565b610c778783838861313a565b610c81888861335a565b610c958888856040015185604001516133c5565b9350610cbc88848660000151602001518760000151602001518860000151600001516135b2565b610ce187838660200151602001518760200151602001518860200151600001516135b2565b610cfa88828560200151866040015188600001516136f9565b610d1387828460200151856040015188602001516136f9565b610d1f888883876137c7565b83935060008060006101000a81548160ff021916908315150217905550505050949350505050565b610d4f61517c565b6060610d5c85858561399b565b9050608081825160208401305af48015610d9357815183526020820151602084015260408201516040840152606082015160608401525b50819150509392505050565b600b6020528060005260406000206000915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000806000809054906101000a900460ff16151515610e26576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610e1d90616d93565b60405180910390fd5b60016000806101000a81548160ff02191690831515021790555082519150600090505b8181141515610e7f57610e728382815181101515610e6357fe5b90602001906020020151613b8a565b8080600101915050610e49565b60008060006101000a81548160ff021916908315150217905550505050565b610ea661517c565b600080610eb161517c565b6000809054906101000a900460ff16151515610f02576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610ef990616d93565b60405180910390fd5b60016000806101000a81548160ff02191690831515021790555086519250600091505b8282141515610f9757610f7e8783815181101515610f3f57fe5b906020019060200201518784815181101515610f5757fe5b906020019060200201518785815181101515610f6f57fe5b90602001906020020151613bb9565b9050610f8a848261305a565b8180600101925050610f25565b83935060008060006101000a81548160ff0219169083151502179055505050509392505050565b6000806000806000809054906101000a900460ff16151515611015576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161100c90616d93565b60405180910390fd5b60016000806101000a81548160ff0219169083151502179055506110376130ca565b93503373ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff16146110725733611075565b60005b9250600185019150600660008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490508082111515611140576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161113790616d13565b60405180910390fd5b81600660008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167f82af639571738f4ebd4268fb0363d8957ebe1bbb9e78dba5ebd69eed39b154f08460405161121e9190616ee5565b60405180910390a360008060006101000a81548160ff0219169083151502179055505050505050565b61124f61517c565b60008061125a61517c565b86519250600091505b82821415156112d5576112bc878381518110151561127d57fe5b90602001906020020151878481518110151561129557fe5b9060200190602002015187858151811015156112ad57fe5b90602001906020020151610d47565b90506112c8848261305a565b8180600101925050611263565b8393505050509392505050565b6000600b6000837bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050919050565b60096020528060005260406000206000915054906101000a900460ff1681565b61138561517c565b6000809054906101000a900460ff161515156113d6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016113cd90616d93565b60405180910390fd5b60016000806101000a81548160ff0219169083151502179055506113fb848484613bb9565b905080905060008060006101000a81548160ff0219169083151502179055509392505050565b60008060009054906101000a900460ff16151515611474576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161146b90616d93565b60405180910390fd5b60016000806101000a81548160ff0219169083151502179055506114966130ca565b905081600860008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055508273ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167fa8656e308026eeabce8f0bc18048433252318ab80ac79da0b3d3d8697dfba891846040516115899190616902565b60405180910390a360008060006101000a81548160ff021916908315150217905550505050565b60086020528160005260406000206020528060005260406000206000915091509054906101000a900460ff1681565b6115e761517c565b606060008060006115f661517c565b6000809054906101000a900460ff16151515611647576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161163e90616d93565b60405180910390fd5b60016000806101000a81548160ff02191690831515021790555088600081518110151561167057fe5b906020019060200201516101600151945088519350600092505b8383141515611728578489848151811015156116a257fe5b9060200190602002015161016001819052506116c2888760200151613c20565b91506116fd89848151811015156116d557fe5b906020019060200201518389868151811015156116ee57fe5b90602001906020020151612fb9565b9050611709868261305a565b87866020015110151561171b57611728565b828060010193505061168a565b85955060008060006101000a81548160ff02191690831515021790555050505050509392505050565b6060600060606000845192508260405190808252806020026020018201604052801561179757816020015b6117846151fc565b81526020019060019003908161177c5790505b509150600090505b82811415156117f0576117c885828151811015156117b957fe5b906020019060200201516127fd565b82828151811015156117d657fe5b90602001906020020181905250808060010191505061179f565b819350505050919050565b60076020528160005260406000206020528060005260406000206000915091509054906101000a900460ff1681565b600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600080600080600080600080600089511115156118a2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161189990616cf3565b60405180910390fd5b6118ab89613c72565b7f0100000000000000000000000000000000000000000000000000000000000000900496506007808111156118dc57fe5b60ff168760ff16101515611925576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161191c90616b33565b60405180910390fd5b8660ff16600781111561193457fe5b95506000600781111561194357fe5b86600781111561194f57fe5b1415611990576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161198790616cd3565b60405180910390fd5b6001600781111561199d57fe5b8660078111156119a957fe5b1415611a0157600089511415156119f5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016119ec90616e73565b60405180910390fd5b60009750879750611fc1565b60026007811115611a0e57fe5b866007811115611a1a57fe5b1415611b9d5760418951141515611a66576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611a5d90616a73565b60405180910390fd5b886000815181101515611a7557fe5b9060200101517f010000000000000000000000000000000000000000000000000000000000000090047f0100000000000000000000000000000000000000000000000000000000000000027f010000000000000000000000000000000000000000000000000000000000000090049450611af960018a613d2990919063ffffffff16565b9350611b0f60218a613d2990919063ffffffff16565b925060018b86868660405160008152602001604052604051611b3494939291906169a6565b60206040516020810390808403906000865af1158015611b58573d6000803e3d6000fd5b5050506020604051035191508173ffffffffffffffffffffffffffffffffffffffff168a73ffffffffffffffffffffffffffffffffffffffff16149750879750611fc1565b60036007811115611baa57fe5b866007811115611bb657fe5b1415611dea5760418951141515611c02576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611bf990616a73565b60405180910390fd5b886000815181101515611c1157fe5b9060200101517f010000000000000000000000000000000000000000000000000000000000000090047f0100000000000000000000000000000000000000000000000000000000000000027f010000000000000000000000000000000000000000000000000000000000000090049450611c9560018a613d2990919063ffffffff16565b9350611cab60218a613d2990919063ffffffff16565b925060018b60405160200180807f19457468657265756d205369676e6564204d6573736167653a0a333200000000815250601c0182600019166000191681526020019150506040516020818303038152906040526040518082805190602001908083835b602083101515611d345780518252602082019150602081019050602083039250611d0f565b6001836020036101000a038019825116818451168082178552505050505050905001915050604051809103902086868660405160008152602001604052604051611d8194939291906169a6565b60206040516020810390808403906000865af1158015611da5573d6000803e3d6000fd5b5050506020604051035191508173ffffffffffffffffffffffffffffffffffffffff168a73ffffffffffffffffffffffffffffffffffffffff16149750879750611fc1565b60046007811115611df757fe5b866007811115611e0357fe5b1415611e1e57611e148b8b8b613d89565b9750879750611fc1565b60056007811115611e2b57fe5b866007811115611e3757fe5b1415611ef757611e4689613ee3565b9050600860008b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515611ee15760009750611fc1565b611eed818c8c8c613f4d565b9750879750611fc1565b60066007811115611f0457fe5b866007811115611f1057fe5b1415611f8657600760008c6000191660001916815260200190815260200160002060008b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff169750879750611fc1565b6040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611fb890616b33565b60405180910390fd5b505050505050509392505050565b611fd761517c565b6060600080600080611fe761517c565b896000815181101515611ff657fe5b906020019060200201516101400151955089519450600093505b84841415156120f157858a8581518110151561202857fe5b906020019060200201516101400181905250612048898860000151613c20565b925061208b8a8581518110151561205b57fe5b9060200190602002015160a001518b8681518110151561207757fe5b9060200190602002015160800151856140aa565b91506120c68a8581518110151561209e57fe5b90602001906020020151838a878151811015156120b757fe5b90602001906020020151610d47565b90506120d2878261305a565b8887600001511015156120e4576120f1565b8380600101945050612010565b8696505050505050509392505050565b61210961517c565b6000809054906101000a900460ff1615151561215a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161215190616d93565b60405180910390fd5b60016000806101000a81548160ff02191690831515021790555061217f848484612fb9565b905080905060008060006101000a81548160ff0219169083151502179055509392505050565b60008073ffffffffffffffffffffffffffffffffffffffff16600a60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141515612239576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161223090616d93565b60405180910390fd5b61227e612279888888888080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505061410f565b6142fe565b905060096000826000191660001916815260200190815260200160002060009054906101000a900460ff161515156122eb576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016122e290616bd3565b60405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff161415156123e05761235d818785858080601f016020809104026020016040519081016040528093929190818152602001838380828437820191505050505050611850565b151561239e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161239590616e53565b60405180910390fd5b85600a60006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505b600160096000836000191660001916815260200190815260200160002060006101000a81548160ff0219169083151502179055503073ffffffffffffffffffffffffffffffffffffffff16858560405180838380828437820191505092505050600060405180830381855af49150501515612490576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161248790616c33565b60405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff16141515612508576000600a60006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505b50505050505050565b6000806000600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415156125a8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161259f90616db3565b60405180910390fd5b8392508273ffffffffffffffffffffffffffffffffffffffff1663ae25532e6040518163ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401602060405180830381600087803b15801561260f57600080fd5b505af1158015612623573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506126479190810190615b23565b9150600b6000837bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614151561272e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161272590616af3565b60405180910390fd5b82600b6000847bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055507fd2c6b762299c609bdb96520b58a49bfb80186934d4f71a86a367571a15c0319482856040516127ef9291906169eb565b60405180910390a150505050565b6128056151d4565b61280e82614348565b81602001906000191690816000191681525050600460008260200151600019166000191681526020019081526020016000205481604001818152505060008260800151141561287d576001600681111561286457fe5b816000019060ff16908160ff1681525050809050612a4a565b60008260a0015114156128b0576002600681111561289757fe5b816000019060ff16908160ff1681525050809050612a4a565b8160a0015181604001511015156128e757600560068111156128ce57fe5b816000019060ff16908160ff1681525050809050612a4a565b8161010001514210151561291b576004600681111561290257fe5b816000019060ff16908160ff1681525050809050612a4a565b6005600082602001516000191660001916815260200190815260200160002060009054906101000a900460ff16156129725760068081111561295957fe5b816000019060ff16908160ff1681525050809050612a4a565b81610120015160066000846000015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000846060015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020541115612a2857600680811115612a0f57fe5b816000019060ff16908160ff1681525050809050612a4a565b60036006811115612a3557fe5b816000019060ff16908160ff16815250508090505b919050565b6000809054906101000a900460ff16151515612aa0576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612a9790616d93565b60405180910390fd5b60016000806101000a81548160ff021916908315150217905550612ac381613b8a565b60008060006101000a81548160ff02191690831515021790555050565b6006602052816000526040600020602052806000526040600020600091509150505481565b60018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015612b9b5780601f10612b7057610100808354040283529160200191612b9b565b820191906000526020600020905b815481529060010190602001808311612b7e57829003601f168201915b505050505081565b612bab61517c565b60606000806000612bba61517c565b886000815181101515612bc957fe5b906020019060200201516101600151945088519350600092505b8383141515612c8157848984815181101515612bfb57fe5b906020019060200201516101600181905250612c1b888760200151613c20565b9150612c568984815181101515612c2e57fe5b90602001906020020151838986815181101515612c4757fe5b90602001906020020151610d47565b9050612c62868261305a565b878660200151101515612c7457612c81565b8280600101935050612be3565b85955050505050509392505050565b60025481565b612c9e61517c565b6060600080600080612cae61517c565b6000809054906101000a900460ff16151515612cff576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612cf690616d93565b60405180910390fd5b60016000806101000a81548160ff021916908315150217905550896000815181101515612d2857fe5b906020019060200201516101400151955089519450600093505b8484141515612e2357858a85815181101515612d5a57fe5b906020019060200201516101400181905250612d7a898860000151613c20565b9250612dbd8a85815181101515612d8d57fe5b9060200190602002015160a001518b86815181101515612da957fe5b9060200190602002015160800151856140aa565b9150612df88a85815181101515612dd057fe5b90602001906020020151838a87815181101515612de957fe5b90602001906020020151612fb9565b9050612e04878261305a565b888760000151101515612e1657612e23565b8380600101945050612d42565b86965060008060006101000a81548160ff0219169083151502179055505050505050509392505050565b600a60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141515612f05576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612efc90616db3565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141515612f7d5780600360006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505b50565b6040805190810160405280600b81526020017f322e302e312d616c70686100000000000000000000000000000000000000000081525081565b612fc161517c565b612fc96151d4565b6000806000612fd7886127fd565b9350612fe16130ca565b9250612fef8885858961313a565b6130018860a001518560400151613c20565b915061300d8783614365565b90506130208885898489600001516135b2565b61302a888261437e565b9450613041888486602001518760400151896136f9565b61304c8884876143f1565b849450505050509392505050565b61306c826000015182600001516144f4565b826000018181525050613087826020015182602001516144f4565b8260200181815250506130a2826040015182604001516144f4565b8260400181815250506130bd826060015182606001516144f4565b8260600181815250505050565b6000806000600a60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169150600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161461312e5781613130565b335b9050809250505090565b6003600681111561314757fe5b60ff16836000015160ff16141515613194576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161318b90616df3565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff16846060015173ffffffffffffffffffffffffffffffffffffffff16141515613243573373ffffffffffffffffffffffffffffffffffffffff16846060015173ffffffffffffffffffffffffffffffffffffffff16141515613242576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161323990616e13565b60405180910390fd5b5b600073ffffffffffffffffffffffffffffffffffffffff16846020015173ffffffffffffffffffffffffffffffffffffffff161415156132f2578173ffffffffffffffffffffffffffffffffffffffff16846020015173ffffffffffffffffffffffffffffffffffffffff161415156132f1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016132e890616a93565b60405180910390fd5b5b600083604001511415613354576133128360200151856000015183611850565b1515613353576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161334a90616b73565b60405180910390fd5b5b50505050565b61336c8260a001518260a0015161454b565b61337e8360800151836080015161454b565b101515156133c1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016133b890616e33565b60405180910390fd5b5050565b6133cd6151a5565b6000806000806133e18960a0015188613c20565b93506133f689608001518a60a00151866145bf565b92506134068860a0015187613c20565b915061341b88608001518960a00151846145bf565b9050808410151561348557808560200151600001818152505081856020015160200181815250508460200151600001518560000151602001818152505061347389608001518a60a001518760000151602001516145bf565b856000015160000181815250506134e0565b82856000015160000181815250508385600001516020018181525050846000015160200151856020015160000181815250506134d28860a001518960800151876020015160000151614671565b856020015160200181815250505b6134fa856000015160000151866020015160200151613c20565b85604001818152505061351e8560000151600001518a608001518b60c001516145bf565b856000015160400181815250506135468560000151602001518a60a001518b60e001516145bf565b8560000151606001818152505061356e85602001516000015189608001518a60c001516145bf565b856020015160400181815250506135968560200151602001518960a001518a60e001516145bf565b8560200151606001818152505084945050505050949350505050565b600083141515156135f8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016135ef90616c73565b60405180910390fd5b82821115151561363d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161363490616c53565b60405180910390fd5b8460a001516136508560400151846144f4565b11151515613693576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161368a90616ab3565b60405180910390fd5b6136a185608001518361454b565b6136af828760a0015161454b565b111515156136f2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016136e990616d73565b60405180910390fd5b5050505050565b6137078282602001516144f4565b600460008560001916600019168152602001908152602001600020819055508260001916856040015173ffffffffffffffffffffffffffffffffffffffff16866000015173ffffffffffffffffffffffffffffffffffffffff167f0bcc4c97732e47d9946f229edb95f5b6323f601300e4690de719993f3c371129873386600001518760200151886040015189606001518e61014001518f61016001516040516137b898979695949392919061680f565b60405180910390a45050505050565b606060018054600181600116156101000203166002900480601f01602080910402602001604051908101604052809291908181526020018280546001816001161561010002031660029004801561385f5780601f106138345761010080835404028352916020019161385f565b820191906000526020600020905b81548152906001019060200180831161384257829003601f168201915b5050505050905061388785610140015186600001518660000151856020015160200151614736565b6138a884610140015185600001518760000151856000015160200151614736565b6138c18561014001518660000151858560400151614736565b6138dd8186600001518760400151856000015160400151614736565b6138f98185600001518660400151856020015160400151614736565b836040015173ffffffffffffffffffffffffffffffffffffffff16856040015173ffffffffffffffffffffffffffffffffffffffff1614156139635761395e818487604001516139598660000151606001518760200151606001516144f4565b614736565b613994565b61397b81848760400151856000015160600151614736565b61399381848660400151856020015160600151614736565b5b5050505050565b606060405190507fb4be83d5000000000000000000000000000000000000000000000000000000006020820152602481018060608101818180896000808784038652602086019550825184526020830151602085015260408301516040850152606083015160608501526080830151608085015260a083015160a085015260c083015160c085015260e083015160e085015261010083015161010085015261012083015161012085015261014083015161014085015261016083015161016085015261018084019350610180830192508484036020600a028601526101408d01519250825191506020830192506020601f830104905081845260208401935060005b81811015613ac05783518552602085019450602084019350600181019050613a9d565b508484036020600b028601526101608d01519250825191506020830192506020601f830104905081845260208401935060005b81811015613b165783518552602085019450602084019350600181019050613af3565b508b865260208601955087840386528a9250825191506020830192506020601f830104905081845260208401935060005b81811015613b6a5783518552602085019450602084019350600181019050613b47565b5060208a0184038a52836040525050505050505050508090509392505050565b613b926151d4565b613b9b826127fd565b9050613ba78282614996565b613bb5828260200151614b24565b5050565b613bc161517c565b613bcc848484612fb9565b9050828160200151141515613c16576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401613c0d90616d53565b60405180910390fd5b8090509392505050565b6000828211151515613c67576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401613c5e90616ad3565b60405180910390fd5b818303905092915050565b6000808251111515613cb9576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401613cb090616cb3565b60405180910390fd5b816001835103815181101515613ccb57fe5b9060200101517f010000000000000000000000000000000000000000000000000000000000000090047f0100000000000000000000000000000000000000000000000000000000000000029050600182510380835250809050919050565b600060208201835110151515613d74576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401613d6b90616b13565b60405180910390fd5b60208201915081830151905080905092915050565b600060608373ffffffffffffffffffffffffffffffffffffffff16631626ba7e90507c0100000000000000000000000000000000000000000000000000000000028584604051602401613ddd929190616976565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050905060208101602081835183885afa8060008114613e5f5760018114613ed057613ed5565b7f08c379a0000000000000000000000000000000000000000000000000000000006000527c20000000000000000000000000000000000000000000000000000000006020527c0c57414c4c45545f4552524f5200000000000000000000000000000000604052600060605260646000fd5b825194505b505050819150509392505050565b60006014825110151515613f2c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401613f2390616dd3565b60405180910390fd5b613f3a826014845103614bdc565b9050601482510380835250809050919050565b600060608373ffffffffffffffffffffffffffffffffffffffff16639363470290507c010000000000000000000000000000000000000000000000000000000002858585604051602401613fa393929190616938565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050509050602081016020818351838a5afa806000811461402557600181146140965761409b565b7f08c379a0000000000000000000000000000000000000000000000000000000006000527c20000000000000000000000000000000000000000000000000000000006020527c0f56414c494441544f525f4552524f5200000000000000000000000000604052600060605260646000fd5b825194505b50505081915050949350505050565b600080831115156140f0576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016140e790616b53565b60405180910390fd5b6141036140fd858461454b565b84614c52565b90508090509392505050565b600080600060405160200180807f5a65726f45785472616e73616374696f6e280000000000000000000000000000815250601201807f75696e743235362073616c742c00000000000000000000000000000000000000815250600d01807f61646472657373207369676e6572416464726573732c00000000000000000000815250601601807f6279746573206461746100000000000000000000000000000000000000000000815250600a01807f290000000000000000000000000000000000000000000000000000000000000081525060010190506040516020818303038152906040526040518082805190602001908083835b6020831015156142295780518252602082019150602081019050602083039250614204565b6001836020036101000a03801982511681845116808217855250505050505090500191505060405180910390209150836040518082805190602001908083835b60208310151561428e5780518252602082019150602081019050602083039250614269565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040518091039020905060405182815286602082015273ffffffffffffffffffffffffffffffffffffffff861660408201528160608201526080812093505082925050509392505050565b60008060025490506040517f190100000000000000000000000000000000000000000000000000000000000081528160028201528360228201526042812092505081915050919050565b600061435b61435683614c6d565b6142fe565b9050809050919050565b60008183106143745781614376565b825b905092915050565b61438661517c565b818160200181815250506143a3828460a0015185608001516145bf565b8160000181815250506143c3816000015184608001518560c001516145bf565b8160400181815250506143df828460a001518560e001516145bf565b81606001818152505080905092915050565b606060018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156144895780601f1061445e57610100808354040283529160200191614489565b820191906000526020600020905b81548152906001019060200180831161446c57829003601f168201915b505050505090506144a98461014001518560000151858560000151614736565b6144c28461016001518486600001518560200151614736565b6144da81856000015186604001518560400151614736565b6144ee818486604001518560600151614736565b50505050565b6000808284019050838110151515614541576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161453890616bb3565b60405180910390fd5b8091505092915050565b600080600084141561456057600091506145b8565b828402905082848281151561457157fe5b041415156145b4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016145ab90616bb3565b60405180910390fd5b8091505b5092915050565b60008083111515614605576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016145fc90616b53565b60405180910390fd5b614610848484615031565b151515614652576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161464990616c13565b60405180910390fd5b61466561465f858461454b565b84614c52565b90508090509392505050565b600080831115156146b7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016146ae90616b53565b60405180910390fd5b6146c28484846150cb565b151515614704576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016146fb90616c13565b60405180910390fd5b61472a614724614714868561454b565b61471f866001613c20565b6144f4565b84614c52565b90508090509392505050565b60008060008311801561477557508373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff1614155b1561498e57600386511115156147c0576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016147b790616d33565b60405180910390fd5b7fffffffff000000000000000000000000000000000000000000000000000000006020870151169150600b6000837bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141515156148cf576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016148c690616c93565b60405180910390fd5b604051660fffffffffffe0603f885101168060840182017fa85e59e40000000000000000000000000000000000000000000000000000000083526080600484015273ffffffffffffffffffffffffffffffffffffffff8816602484015273ffffffffffffffffffffffffffffffffffffffff87166044840152856064840152608483015b81811015614970578951815260208101905060208a019950614953565b61020084858403866000895af1801515614988573d85fd5b50505050505b505050505050565b6000600360068111156149a557fe5b60ff16826000015160ff161415156149f2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016149e990616df3565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff16836060015173ffffffffffffffffffffffffffffffffffffffff16141515614aa1573373ffffffffffffffffffffffffffffffffffffffff16836060015173ffffffffffffffffffffffffffffffffffffffff16141515614aa0576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401614a9790616e13565b60405180910390fd5b5b614aa96130ca565b90508073ffffffffffffffffffffffffffffffffffffffff16836000015173ffffffffffffffffffffffffffffffffffffffff16141515614b1f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401614b1690616b93565b60405180910390fd5b505050565b600160056000836000191660001916815260200190815260200160002060006101000a81548160ff0219169083151502179055508060001916826040015173ffffffffffffffffffffffffffffffffffffffff16836000015173ffffffffffffffffffffffffffffffffffffffff167fdc47b3613d9fe400085f6dbdc99453462279057e6207385042827ed6b1a62cf733866101400151876101600151604051614bd09392919061689b565b60405180910390a45050565b600060148201835110151515614c27576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401614c1e90616dd3565b60405180910390fd5b60148201915073ffffffffffffffffffffffffffffffffffffffff8284015116905080905092915050565b6000808284811515614c6057fe5b0490508091505092915050565b60008060008060405160200180807f4f72646572280000000000000000000000000000000000000000000000000000815250600601807f61646472657373206d616b6572416464726573732c0000000000000000000000815250601501807f616464726573732074616b6572416464726573732c0000000000000000000000815250601501807f6164647265737320666565526563697069656e74416464726573732c00000000815250601c01807f616464726573732073656e646572416464726573732c00000000000000000000815250601601807f75696e74323536206d616b65724173736574416d6f756e742c00000000000000815250601901807f75696e743235362074616b65724173736574416d6f756e742c00000000000000815250601901807f75696e74323536206d616b65724665652c000000000000000000000000000000815250601101807f75696e743235362074616b65724665652c000000000000000000000000000000815250601101807f75696e743235362065787069726174696f6e54696d655365636f6e64732c0000815250601e01807f75696e743235362073616c742c00000000000000000000000000000000000000815250600d01807f6279746573206d616b65724173736574446174612c0000000000000000000000815250601501807f62797465732074616b6572417373657444617461000000000000000000000000815250601401807f290000000000000000000000000000000000000000000000000000000000000081525060010190506040516020818303038152906040526040518082805190602001908083835b602083101515614ef05780518252602082019150602081019050602083039250614ecb565b6001836020036101000a038019825116818451168082178552505050505050905001915050604051809103902092508461014001516040518082805190602001908083835b602083101515614f5a5780518252602082019150602081019050602083039250614f35565b6001836020036101000a038019825116818451168082178552505050505050905001915050604051809103902091508461016001516040518082805190602001908083835b602083101515614fc45780518252602082019150602081019050602083039250614f9f565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040518091039020905060208503610140860161016087018251825182518886528785528684526101a086209950828652818552808452505050505050839350505050919050565b600080600084111515615079576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161507090616b53565b60405180910390fd5b60008314806150885750600085145b1561509657600091506150c3565b838015156150a057fe5b85840990506150af858461454b565b6150bb6103e88361454b565b101591508191505b509392505050565b600080600084111515615113576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161510a90616b53565b60405180910390fd5b60008314806151225750600085145b156151305760009150615174565b8380151561513a57fe5b85840990508361514a8583613c20565b81151561515357fe5b069050615160858461454b565b61516c6103e88361454b565b101591508191505b509392505050565b608060405190810160405280600081526020016000815260200160008152602001600081525090565b610120604051908101604052806151ba615224565b81526020016151c7615224565b8152602001600081525090565b606060405190810160405280600060ff16815260200160008019168152602001600081525090565b606060405190810160405280600060ff16815260200160008019168152602001600081525090565b608060405190810160405280600081526020016000815260200160008152602001600081525090565b600061525982356170bc565b905092915050565b600082601f830112151561527457600080fd5b813561528761528282616f2d565b616f00565b9150818183526020840193506020810190508360005b838110156152cd57813586016152b3888261546e565b84526020840193506020830192505060018101905061529d565b5050505092915050565b600082601f83011215156152ea57600080fd5b81356152fd6152f882616f55565b616f00565b9150818183526020840193506020810190508360005b838110156153435781358601615329888261551a565b845260208401935060208301925050600181019050615313565b5050505092915050565b600082601f830112151561536057600080fd5b813561537361536e82616f7d565b616f00565b9150818183526020840193506020810190508385602084028201111561539857600080fd5b60005b838110156153c857816153ae88826157b6565b84526020840193506020830192505060018101905061539b565b5050505092915050565b60006153de82356170dc565b905092915050565b60006153f282356170e8565b905092915050565b600061540682356170f2565b905092915050565b600061541a82516170f2565b905092915050565b60008083601f840112151561543657600080fd5b8235905067ffffffffffffffff81111561544f57600080fd5b60208301915083600182028301111561546757600080fd5b9250929050565b600082601f830112151561548157600080fd5b813561549461548f82616fa5565b616f00565b915080825260208301602083018583830111156154b057600080fd5b6154bb83828461713a565b50505092915050565b600082601f83011215156154d757600080fd5b81356154ea6154e582616fd1565b616f00565b9150808252602083016020830185838301111561550657600080fd5b61551183828461713a565b50505092915050565b6000610180828403121561552d57600080fd5b615538610180616f00565b905060006155488482850161524d565b600083015250602061555c8482850161524d565b60208301525060406155708482850161524d565b60408301525060606155848482850161524d565b6060830152506080615598848285016157b6565b60808301525060a06155ac848285016157b6565b60a08301525060c06155c0848285016157b6565b60c08301525060e06155d4848285016157b6565b60e0830152506101006155e9848285016157b6565b610100830152506101206155ff848285016157b6565b6101208301525061014082013567ffffffffffffffff81111561562157600080fd5b61562d8482850161546e565b6101408301525061016082013567ffffffffffffffff81111561564f57600080fd5b61565b8482850161546e565b6101608301525092915050565b6000610180828403121561567b57600080fd5b615686610180616f00565b905060006156968482850161524d565b60008301525060206156aa8482850161524d565b60208301525060406156be8482850161524d565b60408301525060606156d28482850161524d565b60608301525060806156e6848285016157b6565b60808301525060a06156fa848285016157b6565b60a08301525060c061570e848285016157b6565b60c08301525060e0615722848285016157b6565b60e083015250610100615737848285016157b6565b6101008301525061012061574d848285016157b6565b6101208301525061014082013567ffffffffffffffff81111561576f57600080fd5b61577b8482850161546e565b6101408301525061016082013567ffffffffffffffff81111561579d57600080fd5b6157a98482850161546e565b6101608301525092915050565b60006157c2823561711e565b905092915050565b6000602082840312156157dc57600080fd5b60006157ea8482850161524d565b91505092915050565b6000806040838503121561580657600080fd5b60006158148582860161524d565b92505060206158258582860161524d565b9150509250929050565b6000806040838503121561584257600080fd5b60006158508582860161524d565b9250506020615861858286016153d2565b9150509250929050565b60006020828403121561587d57600080fd5b600082013567ffffffffffffffff81111561589757600080fd5b6158a3848285016152d7565b91505092915050565b6000806000606084860312156158c157600080fd5b600084013567ffffffffffffffff8111156158db57600080fd5b6158e7868287016152d7565b935050602084013567ffffffffffffffff81111561590457600080fd5b6159108682870161534d565b925050604084013567ffffffffffffffff81111561592d57600080fd5b61593986828701615261565b9150509250925092565b60008060006060848603121561595857600080fd5b600084013567ffffffffffffffff81111561597257600080fd5b61597e868287016152d7565b935050602061598f868287016157b6565b925050604084013567ffffffffffffffff8111156159ac57600080fd5b6159b886828701615261565b9150509250925092565b6000602082840312156159d457600080fd5b60006159e2848285016153e6565b91505092915050565b600080604083850312156159fe57600080fd5b6000615a0c858286016153e6565b9250506020615a1d8582860161524d565b9150509250929050565b60008060008060608587031215615a3d57600080fd5b6000615a4b878288016153e6565b9450506020615a5c8782880161524d565b935050604085013567ffffffffffffffff811115615a7957600080fd5b615a8587828801615422565b925092505092959194509250565b600080600060608486031215615aa857600080fd5b6000615ab6868287016153e6565b9350506020615ac78682870161524d565b925050604084013567ffffffffffffffff811115615ae457600080fd5b615af0868287016154c4565b9150509250925092565b600060208284031215615b0c57600080fd5b6000615b1a848285016153fa565b91505092915050565b600060208284031215615b3557600080fd5b6000615b438482850161540e565b91505092915050565b600060208284031215615b5e57600080fd5b600082013567ffffffffffffffff811115615b7857600080fd5b615b8484828501615668565b91505092915050565b60008060008060808587031215615ba357600080fd5b600085013567ffffffffffffffff811115615bbd57600080fd5b615bc987828801615668565b945050602085013567ffffffffffffffff811115615be657600080fd5b615bf287828801615668565b935050604085013567ffffffffffffffff811115615c0f57600080fd5b615c1b878288016154c4565b925050606085013567ffffffffffffffff811115615c3857600080fd5b615c44878288016154c4565b91505092959194509250565b600080600060608486031215615c6557600080fd5b600084013567ffffffffffffffff811115615c7f57600080fd5b615c8b86828701615668565b9350506020615c9c868287016157b6565b925050604084013567ffffffffffffffff811115615cb957600080fd5b615cc5868287016154c4565b9150509250925092565b600060208284031215615ce157600080fd5b6000615cef848285016157b6565b91505092915050565b60008060008060008060808789031215615d1157600080fd5b6000615d1f89828a016157b6565b9650506020615d3089828a0161524d565b955050604087013567ffffffffffffffff811115615d4d57600080fd5b615d5989828a01615422565b9450945050606087013567ffffffffffffffff811115615d7857600080fd5b615d8489828a01615422565b92509250509295509295509295565b615d9c81617043565b82525050565b6000615dad8261700a565b808452602084019350615dbf83616ffd565b60005b82811015615df157615dd5868351616794565b615dde82617036565b9150606086019550600181019050615dc2565b50849250505092915050565b615e0681617063565b82525050565b615e158161706f565b82525050565b615e2481617079565b82525050565b6000615e3582617020565b808452615e49816020860160208601617149565b615e528161717c565b602085010191505092915050565b6000615e6b82617015565b808452615e7f816020860160208601617149565b615e888161717c565b602085010191505092915050565b615e9f81617128565b82525050565b6000615eb08261702b565b808452615ec4816020860160208601617149565b615ecd8161717c565b602085010191505092915050565b6000601282527f4c454e4754485f36355f524551554952454400000000000000000000000000006020830152604082019050919050565b6000600d82527f494e56414c49445f54414b4552000000000000000000000000000000000000006020830152604082019050919050565b6000600e82527f4f524445525f4f56455246494c4c0000000000000000000000000000000000006020830152604082019050919050565b6000601182527f55494e543235365f554e444552464c4f570000000000000000000000000000006020830152604082019050919050565b6000601a82527f41535345545f50524f58595f414c52454144595f4558495354530000000000006020830152604082019050919050565b6000602682527f475245415445525f4f525f455155414c5f544f5f33325f4c454e4754485f524560208301527f51554952454400000000000000000000000000000000000000000000000000006040830152606082019050919050565b6000601582527f5349474e41545552455f554e535550504f5254454400000000000000000000006020830152604082019050919050565b6000601082527f4449564953494f4e5f42595f5a45524f000000000000000000000000000000006020830152604082019050919050565b6000601782527f494e56414c49445f4f524445525f5349474e41545552450000000000000000006020830152604082019050919050565b6000600d82527f494e56414c49445f4d414b4552000000000000000000000000000000000000006020830152604082019050919050565b6000601082527f55494e543235365f4f564552464c4f57000000000000000000000000000000006020830152604082019050919050565b6000600f82527f494e56414c49445f54585f4841534800000000000000000000000000000000006020830152604082019050919050565b6000601182527f494e56414c49445f5349474e41545552450000000000000000000000000000006020830152604082019050919050565b6000600e82527f524f554e44494e475f4552524f520000000000000000000000000000000000006020830152604082019050919050565b6000601082527f4641494c45445f455845435554494f4e000000000000000000000000000000006020830152604082019050919050565b6000600d82527f54414b45525f4f564552504159000000000000000000000000000000000000006020830152604082019050919050565b6000601482527f494e56414c49445f54414b45525f414d4f554e540000000000000000000000006020830152604082019050919050565b6000601a82527f41535345545f50524f58595f444f45535f4e4f545f45584953540000000000006020830152604082019050919050565b6000602182527f475245415445525f5448414e5f5a45524f5f4c454e4754485f5245515549524560208301527f44000000000000000000000000000000000000000000000000000000000000006040830152606082019050919050565b6000601182527f5349474e41545552455f494c4c4547414c0000000000000000000000000000006020830152604082019050919050565b6000601e82527f4c454e4754485f475245415445525f5448414e5f305f524551554952454400006020830152604082019050919050565b6000601782527f494e56414c49445f4e45575f4f524445525f45504f43480000000000000000006020830152604082019050919050565b6000601e82527f4c454e4754485f475245415445525f5448414e5f335f524551554952454400006020830152604082019050919050565b6000601482527f434f4d504c4554455f46494c4c5f4641494c45440000000000000000000000006020830152604082019050919050565b6000601282527f494e56414c49445f46494c4c5f505249434500000000000000000000000000006020830152604082019050919050565b6000601282527f5245454e5452414e43595f494c4c4547414c00000000000000000000000000006020830152604082019050919050565b6000601382527f4f4e4c595f434f4e54524143545f4f574e4552000000000000000000000000006020830152604082019050919050565b6000602682527f475245415445525f4f525f455155414c5f544f5f32305f4c454e4754485f524560208301527f51554952454400000000000000000000000000000000000000000000000000006040830152606082019050919050565b6000601082527f4f524445525f554e46494c4c41424c45000000000000000000000000000000006020830152604082019050919050565b6000600e82527f494e56414c49445f53454e4445520000000000000000000000000000000000006020830152604082019050919050565b6000601882527f4e454741544956455f5350524541445f524551554952454400000000000000006020830152604082019050919050565b6000601482527f494e56414c49445f54585f5349474e41545552450000000000000000000000006020830152604082019050919050565b6000601182527f4c454e4754485f305f52455155495245440000000000000000000000000000006020830152604082019050919050565b60808201600082015161667a60008501826167d6565b50602082015161668d60208501826167d6565b5060408201516166a060408501826167d6565b5060608201516166b360608501826167d6565b50505050565b6080820160008201516166cf60008501826167d6565b5060208201516166e260208501826167d6565b5060408201516166f560408501826167d6565b50606082015161670860608501826167d6565b50505050565b6101208201600082015161672560008501826166b9565b50602082015161673860808501826166b9565b50604082015161674c6101008501826167d6565b50505050565b60608201600082015161676860008501826167e5565b50602082015161677b6020850182615e0c565b50604082015161678e60408501826167d6565b50505050565b6060820160008201516167aa60008501826167e5565b5060208201516167bd6020850182615e0c565b5060408201516167d060408501826167d6565b50505050565b6167df816170a5565b82525050565b6167ee816170af565b82525050565b60006020820190506168096000830184615d93565b92915050565b600061010082019050616825600083018b615d93565b616832602083018a615d93565b61683f60408301896167d6565b61684c60608301886167d6565b61685960808301876167d6565b61686660a08301866167d6565b81810360c08301526168788185615e60565b905081810360e083015261688c8184615e60565b90509998505050505050505050565b60006060820190506168b06000830186615d93565b81810360208301526168c28185615e60565b905081810360408301526168d68184615e60565b9050949350505050565b600060208201905081810360008301526168fa8184615da2565b905092915050565b60006020820190506169176000830184615dfd565b92915050565b60006020820190506169326000830184615e0c565b92915050565b600060608201905061694d6000830186615e0c565b61695a6020830185615d93565b818103604083015261696c8184615e2a565b9050949350505050565b600060408201905061698b6000830185615e0c565b818103602083015261699d8184615e2a565b90509392505050565b60006080820190506169bb6000830187615e0c565b6169c860208301866167e5565b6169d56040830185615e0c565b6169e26060830184615e0c565b95945050505050565b6000604082019050616a006000830185615e1b565b616a0d6020830184615d93565b9392505050565b60006020820190508181036000830152616a2e8184615e60565b905092915050565b6000602082019050616a4b6000830184615e96565b92915050565b60006020820190508181036000830152616a6b8184615ea5565b905092915050565b60006020820190508181036000830152616a8c81615edb565b9050919050565b60006020820190508181036000830152616aac81615f12565b9050919050565b60006020820190508181036000830152616acc81615f49565b9050919050565b60006020820190508181036000830152616aec81615f80565b9050919050565b60006020820190508181036000830152616b0c81615fb7565b9050919050565b60006020820190508181036000830152616b2c81615fee565b9050919050565b60006020820190508181036000830152616b4c8161604b565b9050919050565b60006020820190508181036000830152616b6c81616082565b9050919050565b60006020820190508181036000830152616b8c816160b9565b9050919050565b60006020820190508181036000830152616bac816160f0565b9050919050565b60006020820190508181036000830152616bcc81616127565b9050919050565b60006020820190508181036000830152616bec8161615e565b9050919050565b60006020820190508181036000830152616c0c81616195565b9050919050565b60006020820190508181036000830152616c2c816161cc565b9050919050565b60006020820190508181036000830152616c4c81616203565b9050919050565b60006020820190508181036000830152616c6c8161623a565b9050919050565b60006020820190508181036000830152616c8c81616271565b9050919050565b60006020820190508181036000830152616cac816162a8565b9050919050565b60006020820190508181036000830152616ccc816162df565b9050919050565b60006020820190508181036000830152616cec8161633c565b9050919050565b60006020820190508181036000830152616d0c81616373565b9050919050565b60006020820190508181036000830152616d2c816163aa565b9050919050565b60006020820190508181036000830152616d4c816163e1565b9050919050565b60006020820190508181036000830152616d6c81616418565b9050919050565b60006020820190508181036000830152616d8c8161644f565b9050919050565b60006020820190508181036000830152616dac81616486565b9050919050565b60006020820190508181036000830152616dcc816164bd565b9050919050565b60006020820190508181036000830152616dec816164f4565b9050919050565b60006020820190508181036000830152616e0c81616551565b9050919050565b60006020820190508181036000830152616e2c81616588565b9050919050565b60006020820190508181036000830152616e4c816165bf565b9050919050565b60006020820190508181036000830152616e6c816165f6565b9050919050565b60006020820190508181036000830152616e8c8161662d565b9050919050565b6000608082019050616ea86000830184616664565b92915050565b600061012082019050616ec4600083018461670e565b92915050565b6000606082019050616edf6000830184616752565b92915050565b6000602082019050616efa60008301846167d6565b92915050565b6000604051905081810181811067ffffffffffffffff82111715616f2357600080fd5b8060405250919050565b600067ffffffffffffffff821115616f4457600080fd5b602082029050602081019050919050565b600067ffffffffffffffff821115616f6c57600080fd5b602082029050602081019050919050565b600067ffffffffffffffff821115616f9457600080fd5b602082029050602081019050919050565b600067ffffffffffffffff821115616fbc57600080fd5b601f19601f8301169050602081019050919050565b600067ffffffffffffffff821115616fe857600080fd5b601f19601f8301169050602081019050919050565b6000602082019050919050565b600081519050919050565b600081519050919050565b600081519050919050565b600081519050919050565b6000602082019050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60008115159050919050565b6000819050919050565b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b6000819050919050565b600060ff82169050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60008115159050919050565b6000819050919050565b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b6000819050919050565b600061713382617043565b9050919050565b82818337600083830152505050565b60005b8381101561716757808201518184015260208101905061714c565b83811115617176576000848401525b50505050565b6000601f19601f83011690509190505600a265627a7a7230582077660df1266e2183c378e8f2513f33170c0994f2c2d9335e426dd87fd4a7c3916c6578706572696d656e74616cf50037" - def filled(fresh1: Bytes32, sender: Address, opt: TransactionOpt): Future[TupleType1[Uint256]] = { - val paramsEncoded = implicitly[TypeInfo[TupleType1[Bytes32]]].encode(TupleType1.apply[Bytes32](fresh1)) + def client: F[Client[F]] = impl.client + def subscriber: F[Subscriber[F]] = impl.subscriber + def isDeployed: F[Boolean] = impl.isDeployed + def address: F[Option[Address]] = impl.address + def loadFrom(address: Address): F[Unit] = impl.load(address) + def filled(fresh1: Bytes32, sender: Address, opt: TransactionOpt): F[TupleType1[Uint256]] = { + val paramsEncoded = TypeInfo[TupleType1[Bytes32]].encode(TupleType1.apply[Bytes32](fresh1)) val functionId = Hex.hex2Bytes("288cdc91") val encoded = functionId ++ paramsEncoded - impl.call(encoded, sender, opt).map { bytes => - val result = implicitly[TypeInfo[TupleType1[Uint256]]].decode(bytes, 0) + for (promise <- impl.call(CallArgs(encoded, sender, opt)); data <- promise.get) yield { + val result = TypeInfo[TupleType1[Uint256]].decode(data, 0) result._1 } } - def batchFillOrders(orders: DynamicArray[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes]], takerAssetFillAmounts: DynamicArray[Uint256], signatures: DynamicArray[DynamicBytes], sender: Address, opt: TransactionOpt): Future[Hash] = { - val paramsEncoded = implicitly[TypeInfo[TupleType3[DynamicArray[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes]], DynamicArray[Uint256], DynamicArray[DynamicBytes]]]].encode(TupleType3.apply[DynamicArray[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes]], DynamicArray[Uint256], DynamicArray[DynamicBytes]](orders, takerAssetFillAmounts, signatures)) + def batchFillOrders(orders: DynamicArray[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes]], takerAssetFillAmounts: DynamicArray[Uint256], signatures: DynamicArray[DynamicBytes], sender: Address, opt: TransactionOpt): F[Deferred[F, Hash]] = { + val paramsEncoded = TypeInfo[TupleType3[DynamicArray[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes]], DynamicArray[Uint256], DynamicArray[DynamicBytes]]].encode(TupleType3.apply[DynamicArray[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes]], DynamicArray[Uint256], DynamicArray[DynamicBytes]](orders, takerAssetFillAmounts, signatures)) val functionId = Hex.hex2Bytes("297bb70b") val encoded = functionId ++ paramsEncoded - impl.sendTransaction(encoded, sender, opt) + impl.sendTransaction(CallArgs(encoded, sender, opt)) } - def cancelled(fresh3: Bytes32, sender: Address, opt: TransactionOpt): Future[TupleType1[Bool]] = { - val paramsEncoded = implicitly[TypeInfo[TupleType1[Bytes32]]].encode(TupleType1.apply[Bytes32](fresh3)) + def cancelled(fresh3: Bytes32, sender: Address, opt: TransactionOpt): F[TupleType1[Bool]] = { + val paramsEncoded = TypeInfo[TupleType1[Bytes32]].encode(TupleType1.apply[Bytes32](fresh3)) val functionId = Hex.hex2Bytes("2ac12622") val encoded = functionId ++ paramsEncoded - impl.call(encoded, sender, opt).map { bytes => - val result = implicitly[TypeInfo[TupleType1[Bool]]].decode(bytes, 0) + for (promise <- impl.call(CallArgs(encoded, sender, opt)); data <- promise.get) yield { + val result = TypeInfo[TupleType1[Bool]].decode(data, 0) result._1 } } - def preSign(hash: Bytes32, signerAddress: Address, signature: DynamicBytes, sender: Address, opt: TransactionOpt): Future[Hash] = { - val paramsEncoded = implicitly[TypeInfo[TupleType3[Bytes32, Address, DynamicBytes]]].encode(TupleType3.apply[Bytes32, Address, DynamicBytes](hash, signerAddress, signature)) + def preSign(hash: Bytes32, signerAddress: Address, signature: DynamicBytes, sender: Address, opt: TransactionOpt): F[Deferred[F, Hash]] = { + val paramsEncoded = TypeInfo[TupleType3[Bytes32, Address, DynamicBytes]].encode(TupleType3.apply[Bytes32, Address, DynamicBytes](hash, signerAddress, signature)) val functionId = Hex.hex2Bytes("3683ef8e") val encoded = functionId ++ paramsEncoded - impl.sendTransaction(encoded, sender, opt) + impl.sendTransaction(CallArgs(encoded, sender, opt)) } - def matchOrders(leftOrder: TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes], rightOrder: TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes], leftSignature: DynamicBytes, rightSignature: DynamicBytes, sender: Address, opt: TransactionOpt): Future[Hash] = { - val paramsEncoded = implicitly[TypeInfo[TupleType4[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes], TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes], DynamicBytes, DynamicBytes]]].encode(TupleType4.apply[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes], TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes], DynamicBytes, DynamicBytes](leftOrder, rightOrder, leftSignature, rightSignature)) + def matchOrders(leftOrder: TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes], rightOrder: TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes], leftSignature: DynamicBytes, rightSignature: DynamicBytes, sender: Address, opt: TransactionOpt): F[Deferred[F, Hash]] = { + val paramsEncoded = TypeInfo[TupleType4[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes], TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes], DynamicBytes, DynamicBytes]].encode(TupleType4.apply[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes], TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes], DynamicBytes, DynamicBytes](leftOrder, rightOrder, leftSignature, rightSignature)) val functionId = Hex.hex2Bytes("3c28d861") val encoded = functionId ++ paramsEncoded - impl.sendTransaction(encoded, sender, opt) + impl.sendTransaction(CallArgs(encoded, sender, opt)) } - def fillOrderNoThrow(order: TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes], takerAssetFillAmount: Uint256, signature: DynamicBytes, sender: Address, opt: TransactionOpt): Future[Hash] = { - val paramsEncoded = implicitly[TypeInfo[TupleType3[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes], Uint256, DynamicBytes]]].encode(TupleType3.apply[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes], Uint256, DynamicBytes](order, takerAssetFillAmount, signature)) + def fillOrderNoThrow(order: TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes], takerAssetFillAmount: Uint256, signature: DynamicBytes, sender: Address, opt: TransactionOpt): F[Deferred[F, Hash]] = { + val paramsEncoded = TypeInfo[TupleType3[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes], Uint256, DynamicBytes]].encode(TupleType3.apply[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes], Uint256, DynamicBytes](order, takerAssetFillAmount, signature)) val functionId = Hex.hex2Bytes("3e228bae") val encoded = functionId ++ paramsEncoded - impl.sendTransaction(encoded, sender, opt) + impl.sendTransaction(CallArgs(encoded, sender, opt)) } - def assetProxies(fresh5: Bytes4, sender: Address, opt: TransactionOpt): Future[TupleType1[Address]] = { - val paramsEncoded = implicitly[TypeInfo[TupleType1[Bytes4]]].encode(TupleType1.apply[Bytes4](fresh5)) + def assetProxies(fresh5: Bytes4, sender: Address, opt: TransactionOpt): F[TupleType1[Address]] = { + val paramsEncoded = TypeInfo[TupleType1[Bytes4]].encode(TupleType1.apply[Bytes4](fresh5)) val functionId = Hex.hex2Bytes("3fd3c997") val encoded = functionId ++ paramsEncoded - impl.call(encoded, sender, opt).map { bytes => - val result = implicitly[TypeInfo[TupleType1[Address]]].decode(bytes, 0) + for (promise <- impl.call(CallArgs(encoded, sender, opt)); data <- promise.get) yield { + val result = TypeInfo[TupleType1[Address]].decode(data, 0) result._1 } } - def batchCancelOrders(orders: DynamicArray[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes]], sender: Address, opt: TransactionOpt): Future[Hash] = { - val paramsEncoded = implicitly[TypeInfo[TupleType1[DynamicArray[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes]]]]].encode(TupleType1.apply[DynamicArray[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes]]](orders)) + def batchCancelOrders(orders: DynamicArray[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes]], sender: Address, opt: TransactionOpt): F[Deferred[F, Hash]] = { + val paramsEncoded = TypeInfo[TupleType1[DynamicArray[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes]]]].encode(TupleType1.apply[DynamicArray[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes]]](orders)) val functionId = Hex.hex2Bytes("4ac14782") val encoded = functionId ++ paramsEncoded - impl.sendTransaction(encoded, sender, opt) + impl.sendTransaction(CallArgs(encoded, sender, opt)) } - def batchFillOrKillOrders(orders: DynamicArray[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes]], takerAssetFillAmounts: DynamicArray[Uint256], signatures: DynamicArray[DynamicBytes], sender: Address, opt: TransactionOpt): Future[Hash] = { - val paramsEncoded = implicitly[TypeInfo[TupleType3[DynamicArray[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes]], DynamicArray[Uint256], DynamicArray[DynamicBytes]]]].encode(TupleType3.apply[DynamicArray[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes]], DynamicArray[Uint256], DynamicArray[DynamicBytes]](orders, takerAssetFillAmounts, signatures)) + def batchFillOrKillOrders(orders: DynamicArray[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes]], takerAssetFillAmounts: DynamicArray[Uint256], signatures: DynamicArray[DynamicBytes], sender: Address, opt: TransactionOpt): F[Deferred[F, Hash]] = { + val paramsEncoded = TypeInfo[TupleType3[DynamicArray[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes]], DynamicArray[Uint256], DynamicArray[DynamicBytes]]].encode(TupleType3.apply[DynamicArray[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes]], DynamicArray[Uint256], DynamicArray[DynamicBytes]](orders, takerAssetFillAmounts, signatures)) val functionId = Hex.hex2Bytes("4d0ae546") val encoded = functionId ++ paramsEncoded - impl.sendTransaction(encoded, sender, opt) + impl.sendTransaction(CallArgs(encoded, sender, opt)) } - def cancelOrdersUpTo(targetOrderEpoch: Uint256, sender: Address, opt: TransactionOpt): Future[Hash] = { - val paramsEncoded = implicitly[TypeInfo[TupleType1[Uint256]]].encode(TupleType1.apply[Uint256](targetOrderEpoch)) + def cancelOrdersUpTo(targetOrderEpoch: Uint256, sender: Address, opt: TransactionOpt): F[Deferred[F, Hash]] = { + val paramsEncoded = TypeInfo[TupleType1[Uint256]].encode(TupleType1.apply[Uint256](targetOrderEpoch)) val functionId = Hex.hex2Bytes("4f9559b1") val encoded = functionId ++ paramsEncoded - impl.sendTransaction(encoded, sender, opt) + impl.sendTransaction(CallArgs(encoded, sender, opt)) } - def batchFillOrdersNoThrow(orders: DynamicArray[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes]], takerAssetFillAmounts: DynamicArray[Uint256], signatures: DynamicArray[DynamicBytes], sender: Address, opt: TransactionOpt): Future[Hash] = { - val paramsEncoded = implicitly[TypeInfo[TupleType3[DynamicArray[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes]], DynamicArray[Uint256], DynamicArray[DynamicBytes]]]].encode(TupleType3.apply[DynamicArray[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes]], DynamicArray[Uint256], DynamicArray[DynamicBytes]](orders, takerAssetFillAmounts, signatures)) + def batchFillOrdersNoThrow(orders: DynamicArray[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes]], takerAssetFillAmounts: DynamicArray[Uint256], signatures: DynamicArray[DynamicBytes], sender: Address, opt: TransactionOpt): F[Deferred[F, Hash]] = { + val paramsEncoded = TypeInfo[TupleType3[DynamicArray[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes]], DynamicArray[Uint256], DynamicArray[DynamicBytes]]].encode(TupleType3.apply[DynamicArray[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes]], DynamicArray[Uint256], DynamicArray[DynamicBytes]](orders, takerAssetFillAmounts, signatures)) val functionId = Hex.hex2Bytes("50dde190") val encoded = functionId ++ paramsEncoded - impl.sendTransaction(encoded, sender, opt) + impl.sendTransaction(CallArgs(encoded, sender, opt)) } - def getAssetProxy(assetProxyId: Bytes4, sender: Address, opt: TransactionOpt): Future[TupleType1[Address]] = { - val paramsEncoded = implicitly[TypeInfo[TupleType1[Bytes4]]].encode(TupleType1.apply[Bytes4](assetProxyId)) + def getAssetProxy(assetProxyId: Bytes4, sender: Address, opt: TransactionOpt): F[TupleType1[Address]] = { + val paramsEncoded = TypeInfo[TupleType1[Bytes4]].encode(TupleType1.apply[Bytes4](assetProxyId)) val functionId = Hex.hex2Bytes("60704108") val encoded = functionId ++ paramsEncoded - impl.call(encoded, sender, opt).map { bytes => - val result = implicitly[TypeInfo[TupleType1[Address]]].decode(bytes, 0) + for (promise <- impl.call(CallArgs(encoded, sender, opt)); data <- promise.get) yield { + val result = TypeInfo[TupleType1[Address]].decode(data, 0) result._1 } } - def transactions(fresh8: Bytes32, sender: Address, opt: TransactionOpt): Future[TupleType1[Bool]] = { - val paramsEncoded = implicitly[TypeInfo[TupleType1[Bytes32]]].encode(TupleType1.apply[Bytes32](fresh8)) + def transactions(fresh8: Bytes32, sender: Address, opt: TransactionOpt): F[TupleType1[Bool]] = { + val paramsEncoded = TypeInfo[TupleType1[Bytes32]].encode(TupleType1.apply[Bytes32](fresh8)) val functionId = Hex.hex2Bytes("642f2eaf") val encoded = functionId ++ paramsEncoded - impl.call(encoded, sender, opt).map { bytes => - val result = implicitly[TypeInfo[TupleType1[Bool]]].decode(bytes, 0) + for (promise <- impl.call(CallArgs(encoded, sender, opt)); data <- promise.get) yield { + val result = TypeInfo[TupleType1[Bool]].decode(data, 0) result._1 } } - def fillOrKillOrder(order: TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes], takerAssetFillAmount: Uint256, signature: DynamicBytes, sender: Address, opt: TransactionOpt): Future[Hash] = { - val paramsEncoded = implicitly[TypeInfo[TupleType3[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes], Uint256, DynamicBytes]]].encode(TupleType3.apply[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes], Uint256, DynamicBytes](order, takerAssetFillAmount, signature)) + def fillOrKillOrder(order: TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes], takerAssetFillAmount: Uint256, signature: DynamicBytes, sender: Address, opt: TransactionOpt): F[Deferred[F, Hash]] = { + val paramsEncoded = TypeInfo[TupleType3[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes], Uint256, DynamicBytes]].encode(TupleType3.apply[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes], Uint256, DynamicBytes](order, takerAssetFillAmount, signature)) val functionId = Hex.hex2Bytes("64a3bc15") val encoded = functionId ++ paramsEncoded - impl.sendTransaction(encoded, sender, opt) + impl.sendTransaction(CallArgs(encoded, sender, opt)) } - def setSignatureValidatorApproval(validatorAddress: Address, approval: Bool, sender: Address, opt: TransactionOpt): Future[Hash] = { - val paramsEncoded = implicitly[TypeInfo[TupleType2[Address, Bool]]].encode(TupleType2.apply[Address, Bool](validatorAddress, approval)) + def setSignatureValidatorApproval(validatorAddress: Address, approval: Bool, sender: Address, opt: TransactionOpt): F[Deferred[F, Hash]] = { + val paramsEncoded = TypeInfo[TupleType2[Address, Bool]].encode(TupleType2.apply[Address, Bool](validatorAddress, approval)) val functionId = Hex.hex2Bytes("77fcce68") val encoded = functionId ++ paramsEncoded - impl.sendTransaction(encoded, sender, opt) + impl.sendTransaction(CallArgs(encoded, sender, opt)) } - def allowedValidators(fresh10: Address, fresh11: Address, sender: Address, opt: TransactionOpt): Future[TupleType1[Bool]] = { - val paramsEncoded = implicitly[TypeInfo[TupleType2[Address, Address]]].encode(TupleType2.apply[Address, Address](fresh10, fresh11)) + def allowedValidators(fresh10: Address, fresh11: Address, sender: Address, opt: TransactionOpt): F[TupleType1[Bool]] = { + val paramsEncoded = TypeInfo[TupleType2[Address, Address]].encode(TupleType2.apply[Address, Address](fresh10, fresh11)) val functionId = Hex.hex2Bytes("7b8e3514") val encoded = functionId ++ paramsEncoded - impl.call(encoded, sender, opt).map { bytes => - val result = implicitly[TypeInfo[TupleType1[Bool]]].decode(bytes, 0) + for (promise <- impl.call(CallArgs(encoded, sender, opt)); data <- promise.get) yield { + val result = TypeInfo[TupleType1[Bool]].decode(data, 0) result._1 } } - def marketSellOrders(orders: DynamicArray[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes]], takerAssetFillAmount: Uint256, signatures: DynamicArray[DynamicBytes], sender: Address, opt: TransactionOpt): Future[Hash] = { - val paramsEncoded = implicitly[TypeInfo[TupleType3[DynamicArray[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes]], Uint256, DynamicArray[DynamicBytes]]]].encode(TupleType3.apply[DynamicArray[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes]], Uint256, DynamicArray[DynamicBytes]](orders, takerAssetFillAmount, signatures)) + def marketSellOrders(orders: DynamicArray[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes]], takerAssetFillAmount: Uint256, signatures: DynamicArray[DynamicBytes], sender: Address, opt: TransactionOpt): F[Deferred[F, Hash]] = { + val paramsEncoded = TypeInfo[TupleType3[DynamicArray[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes]], Uint256, DynamicArray[DynamicBytes]]].encode(TupleType3.apply[DynamicArray[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes]], Uint256, DynamicArray[DynamicBytes]](orders, takerAssetFillAmount, signatures)) val functionId = Hex.hex2Bytes("7e1d9808") val encoded = functionId ++ paramsEncoded - impl.sendTransaction(encoded, sender, opt) + impl.sendTransaction(CallArgs(encoded, sender, opt)) } - def getOrdersInfo(orders: DynamicArray[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes]], sender: Address, opt: TransactionOpt): Future[TupleType1[DynamicArray[TupleType3[Uint8, Bytes32, Uint256]]]] = { - val paramsEncoded = implicitly[TypeInfo[TupleType1[DynamicArray[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes]]]]].encode(TupleType1.apply[DynamicArray[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes]]](orders)) + def getOrdersInfo(orders: DynamicArray[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes]], sender: Address, opt: TransactionOpt): F[TupleType1[DynamicArray[TupleType3[Uint8, Bytes32, Uint256]]]] = { + val paramsEncoded = TypeInfo[TupleType1[DynamicArray[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes]]]].encode(TupleType1.apply[DynamicArray[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes]]](orders)) val functionId = Hex.hex2Bytes("7e9d74dc") val encoded = functionId ++ paramsEncoded - impl.call(encoded, sender, opt).map { bytes => - val result = implicitly[TypeInfo[TupleType1[DynamicArray[TupleType3[Uint8, Bytes32, Uint256]]]]].decode(bytes, 0) + for (promise <- impl.call(CallArgs(encoded, sender, opt)); data <- promise.get) yield { + val result = TypeInfo[TupleType1[DynamicArray[TupleType3[Uint8, Bytes32, Uint256]]]].decode(data, 0) result._1 } } - def preSigned(fresh14: Bytes32, fresh15: Address, sender: Address, opt: TransactionOpt): Future[TupleType1[Bool]] = { - val paramsEncoded = implicitly[TypeInfo[TupleType2[Bytes32, Address]]].encode(TupleType2.apply[Bytes32, Address](fresh14, fresh15)) + def preSigned(fresh14: Bytes32, fresh15: Address, sender: Address, opt: TransactionOpt): F[TupleType1[Bool]] = { + val paramsEncoded = TypeInfo[TupleType2[Bytes32, Address]].encode(TupleType2.apply[Bytes32, Address](fresh14, fresh15)) val functionId = Hex.hex2Bytes("82c174d0") val encoded = functionId ++ paramsEncoded - impl.call(encoded, sender, opt).map { bytes => - val result = implicitly[TypeInfo[TupleType1[Bool]]].decode(bytes, 0) + for (promise <- impl.call(CallArgs(encoded, sender, opt)); data <- promise.get) yield { + val result = TypeInfo[TupleType1[Bool]].decode(data, 0) result._1 } } - def owner(sender: Address, opt: TransactionOpt): Future[TupleType1[Address]] = { + def owner(sender: Address, opt: TransactionOpt): F[TupleType1[Address]] = { val encoded = Hex.hex2Bytes("8da5cb5b") - impl.call(encoded, sender, opt).map { bytes => - val result = implicitly[TypeInfo[TupleType1[Address]]].decode(bytes, 0) + for (promise <- impl.call(CallArgs(encoded, sender, opt)); data <- promise.get) yield { + val result = TypeInfo[TupleType1[Address]].decode(data, 0) result._1 } } - def isValidSignature(hash: Bytes32, signerAddress: Address, signature: DynamicBytes, sender: Address, opt: TransactionOpt): Future[TupleType1[Bool]] = { - val paramsEncoded = implicitly[TypeInfo[TupleType3[Bytes32, Address, DynamicBytes]]].encode(TupleType3.apply[Bytes32, Address, DynamicBytes](hash, signerAddress, signature)) + def isValidSignature(hash: Bytes32, signerAddress: Address, signature: DynamicBytes, sender: Address, opt: TransactionOpt): F[TupleType1[Bool]] = { + val paramsEncoded = TypeInfo[TupleType3[Bytes32, Address, DynamicBytes]].encode(TupleType3.apply[Bytes32, Address, DynamicBytes](hash, signerAddress, signature)) val functionId = Hex.hex2Bytes("93634702") val encoded = functionId ++ paramsEncoded - impl.call(encoded, sender, opt).map { bytes => - val result = implicitly[TypeInfo[TupleType1[Bool]]].decode(bytes, 0) + for (promise <- impl.call(CallArgs(encoded, sender, opt)); data <- promise.get) yield { + val result = TypeInfo[TupleType1[Bool]].decode(data, 0) result._1 } } - def marketBuyOrdersNoThrow(orders: DynamicArray[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes]], makerAssetFillAmount: Uint256, signatures: DynamicArray[DynamicBytes], sender: Address, opt: TransactionOpt): Future[Hash] = { - val paramsEncoded = implicitly[TypeInfo[TupleType3[DynamicArray[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes]], Uint256, DynamicArray[DynamicBytes]]]].encode(TupleType3.apply[DynamicArray[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes]], Uint256, DynamicArray[DynamicBytes]](orders, makerAssetFillAmount, signatures)) + def marketBuyOrdersNoThrow(orders: DynamicArray[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes]], makerAssetFillAmount: Uint256, signatures: DynamicArray[DynamicBytes], sender: Address, opt: TransactionOpt): F[Deferred[F, Hash]] = { + val paramsEncoded = TypeInfo[TupleType3[DynamicArray[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes]], Uint256, DynamicArray[DynamicBytes]]].encode(TupleType3.apply[DynamicArray[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes]], Uint256, DynamicArray[DynamicBytes]](orders, makerAssetFillAmount, signatures)) val functionId = Hex.hex2Bytes("a3e20380") val encoded = functionId ++ paramsEncoded - impl.sendTransaction(encoded, sender, opt) + impl.sendTransaction(CallArgs(encoded, sender, opt)) } - def fillOrder(order: TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes], takerAssetFillAmount: Uint256, signature: DynamicBytes, sender: Address, opt: TransactionOpt): Future[Hash] = { - val paramsEncoded = implicitly[TypeInfo[TupleType3[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes], Uint256, DynamicBytes]]].encode(TupleType3.apply[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes], Uint256, DynamicBytes](order, takerAssetFillAmount, signature)) + def fillOrder(order: TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes], takerAssetFillAmount: Uint256, signature: DynamicBytes, sender: Address, opt: TransactionOpt): F[Deferred[F, Hash]] = { + val paramsEncoded = TypeInfo[TupleType3[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes], Uint256, DynamicBytes]].encode(TupleType3.apply[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes], Uint256, DynamicBytes](order, takerAssetFillAmount, signature)) val functionId = Hex.hex2Bytes("b4be83d5") val encoded = functionId ++ paramsEncoded - impl.sendTransaction(encoded, sender, opt) + impl.sendTransaction(CallArgs(encoded, sender, opt)) } - def executeTransaction(salt: Uint256, signerAddress: Address, data: DynamicBytes, signature: DynamicBytes, sender: Address, opt: TransactionOpt): Future[Hash] = { - val paramsEncoded = implicitly[TypeInfo[TupleType4[Uint256, Address, DynamicBytes, DynamicBytes]]].encode(TupleType4.apply[Uint256, Address, DynamicBytes, DynamicBytes](salt, signerAddress, data, signature)) + def executeTransaction(salt: Uint256, signerAddress: Address, data: DynamicBytes, signature: DynamicBytes, sender: Address, opt: TransactionOpt): F[Deferred[F, Hash]] = { + val paramsEncoded = TypeInfo[TupleType4[Uint256, Address, DynamicBytes, DynamicBytes]].encode(TupleType4.apply[Uint256, Address, DynamicBytes, DynamicBytes](salt, signerAddress, data, signature)) val functionId = Hex.hex2Bytes("bfc8bfce") val encoded = functionId ++ paramsEncoded - impl.sendTransaction(encoded, sender, opt) + impl.sendTransaction(CallArgs(encoded, sender, opt)) } - def registerAssetProxy(assetProxy: Address, sender: Address, opt: TransactionOpt): Future[Hash] = { - val paramsEncoded = implicitly[TypeInfo[TupleType1[Address]]].encode(TupleType1.apply[Address](assetProxy)) + def registerAssetProxy(assetProxy: Address, sender: Address, opt: TransactionOpt): F[Deferred[F, Hash]] = { + val paramsEncoded = TypeInfo[TupleType1[Address]].encode(TupleType1.apply[Address](assetProxy)) val functionId = Hex.hex2Bytes("c585bb93") val encoded = functionId ++ paramsEncoded - impl.sendTransaction(encoded, sender, opt) + impl.sendTransaction(CallArgs(encoded, sender, opt)) } - def getOrderInfo(order: TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes], sender: Address, opt: TransactionOpt): Future[TupleType1[TupleType3[Uint8, Bytes32, Uint256]]] = { - val paramsEncoded = implicitly[TypeInfo[TupleType1[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes]]]].encode(TupleType1.apply[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes]](order)) + def getOrderInfo(order: TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes], sender: Address, opt: TransactionOpt): F[TupleType1[TupleType3[Uint8, Bytes32, Uint256]]] = { + val paramsEncoded = TypeInfo[TupleType1[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes]]].encode(TupleType1.apply[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes]](order)) val functionId = Hex.hex2Bytes("c75e0a81") val encoded = functionId ++ paramsEncoded - impl.call(encoded, sender, opt).map { bytes => - val result = implicitly[TypeInfo[TupleType1[TupleType3[Uint8, Bytes32, Uint256]]]].decode(bytes, 0) + for (promise <- impl.call(CallArgs(encoded, sender, opt)); data <- promise.get) yield { + val result = TypeInfo[TupleType1[TupleType3[Uint8, Bytes32, Uint256]]].decode(data, 0) result._1 } } - def cancelOrder(order: TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes], sender: Address, opt: TransactionOpt): Future[Hash] = { - val paramsEncoded = implicitly[TypeInfo[TupleType1[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes]]]].encode(TupleType1.apply[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes]](order)) + def cancelOrder(order: TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes], sender: Address, opt: TransactionOpt): F[Deferred[F, Hash]] = { + val paramsEncoded = TypeInfo[TupleType1[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes]]].encode(TupleType1.apply[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes]](order)) val functionId = Hex.hex2Bytes("d46b02c3") val encoded = functionId ++ paramsEncoded - impl.sendTransaction(encoded, sender, opt) + impl.sendTransaction(CallArgs(encoded, sender, opt)) } - def orderEpoch(fresh18: Address, fresh19: Address, sender: Address, opt: TransactionOpt): Future[TupleType1[Uint256]] = { - val paramsEncoded = implicitly[TypeInfo[TupleType2[Address, Address]]].encode(TupleType2.apply[Address, Address](fresh18, fresh19)) + def orderEpoch(fresh18: Address, fresh19: Address, sender: Address, opt: TransactionOpt): F[TupleType1[Uint256]] = { + val paramsEncoded = TypeInfo[TupleType2[Address, Address]].encode(TupleType2.apply[Address, Address](fresh18, fresh19)) val functionId = Hex.hex2Bytes("d9bfa73e") val encoded = functionId ++ paramsEncoded - impl.call(encoded, sender, opt).map { bytes => - val result = implicitly[TypeInfo[TupleType1[Uint256]]].decode(bytes, 0) + for (promise <- impl.call(CallArgs(encoded, sender, opt)); data <- promise.get) yield { + val result = TypeInfo[TupleType1[Uint256]].decode(data, 0) result._1 } } - def ZRX_ASSET_DATA(sender: Address, opt: TransactionOpt): Future[TupleType1[DynamicBytes]] = { + def ZRX_ASSET_DATA(sender: Address, opt: TransactionOpt): F[TupleType1[DynamicBytes]] = { val encoded = Hex.hex2Bytes("db123b1a") - impl.call(encoded, sender, opt).map { bytes => - val result = implicitly[TypeInfo[TupleType1[DynamicBytes]]].decode(bytes, 0) + for (promise <- impl.call(CallArgs(encoded, sender, opt)); data <- promise.get) yield { + val result = TypeInfo[TupleType1[DynamicBytes]].decode(data, 0) result._1 } } - def marketSellOrdersNoThrow(orders: DynamicArray[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes]], takerAssetFillAmount: Uint256, signatures: DynamicArray[DynamicBytes], sender: Address, opt: TransactionOpt): Future[Hash] = { - val paramsEncoded = implicitly[TypeInfo[TupleType3[DynamicArray[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes]], Uint256, DynamicArray[DynamicBytes]]]].encode(TupleType3.apply[DynamicArray[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes]], Uint256, DynamicArray[DynamicBytes]](orders, takerAssetFillAmount, signatures)) + def marketSellOrdersNoThrow(orders: DynamicArray[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes]], takerAssetFillAmount: Uint256, signatures: DynamicArray[DynamicBytes], sender: Address, opt: TransactionOpt): F[Deferred[F, Hash]] = { + val paramsEncoded = TypeInfo[TupleType3[DynamicArray[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes]], Uint256, DynamicArray[DynamicBytes]]].encode(TupleType3.apply[DynamicArray[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes]], Uint256, DynamicArray[DynamicBytes]](orders, takerAssetFillAmount, signatures)) val functionId = Hex.hex2Bytes("dd1c7d18") val encoded = functionId ++ paramsEncoded - impl.sendTransaction(encoded, sender, opt) + impl.sendTransaction(CallArgs(encoded, sender, opt)) } - def EIP712_DOMAIN_HASH(sender: Address, opt: TransactionOpt): Future[TupleType1[Bytes32]] = { + def EIP712_DOMAIN_HASH(sender: Address, opt: TransactionOpt): F[TupleType1[Bytes32]] = { val encoded = Hex.hex2Bytes("e306f779") - impl.call(encoded, sender, opt).map { bytes => - val result = implicitly[TypeInfo[TupleType1[Bytes32]]].decode(bytes, 0) + for (promise <- impl.call(CallArgs(encoded, sender, opt)); data <- promise.get) yield { + val result = TypeInfo[TupleType1[Bytes32]].decode(data, 0) result._1 } } - def marketBuyOrders(orders: DynamicArray[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes]], makerAssetFillAmount: Uint256, signatures: DynamicArray[DynamicBytes], sender: Address, opt: TransactionOpt): Future[Hash] = { - val paramsEncoded = implicitly[TypeInfo[TupleType3[DynamicArray[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes]], Uint256, DynamicArray[DynamicBytes]]]].encode(TupleType3.apply[DynamicArray[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes]], Uint256, DynamicArray[DynamicBytes]](orders, makerAssetFillAmount, signatures)) + def marketBuyOrders(orders: DynamicArray[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes]], makerAssetFillAmount: Uint256, signatures: DynamicArray[DynamicBytes], sender: Address, opt: TransactionOpt): F[Deferred[F, Hash]] = { + val paramsEncoded = TypeInfo[TupleType3[DynamicArray[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes]], Uint256, DynamicArray[DynamicBytes]]].encode(TupleType3.apply[DynamicArray[TupleType12[Address, Address, Address, Address, Uint256, Uint256, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes]], Uint256, DynamicArray[DynamicBytes]](orders, makerAssetFillAmount, signatures)) val functionId = Hex.hex2Bytes("e5fa431b") val encoded = functionId ++ paramsEncoded - impl.sendTransaction(encoded, sender, opt) + impl.sendTransaction(CallArgs(encoded, sender, opt)) } - def currentContextAddress(sender: Address, opt: TransactionOpt): Future[TupleType1[Address]] = { + def currentContextAddress(sender: Address, opt: TransactionOpt): F[TupleType1[Address]] = { val encoded = Hex.hex2Bytes("eea086ba") - impl.call(encoded, sender, opt).map { bytes => - val result = implicitly[TypeInfo[TupleType1[Address]]].decode(bytes, 0) + for (promise <- impl.call(CallArgs(encoded, sender, opt)); data <- promise.get) yield { + val result = TypeInfo[TupleType1[Address]].decode(data, 0) result._1 } } - def transferOwnership(newOwner: Address, sender: Address, opt: TransactionOpt): Future[Hash] = { - val paramsEncoded = implicitly[TypeInfo[TupleType1[Address]]].encode(TupleType1.apply[Address](newOwner)) + def transferOwnership(newOwner: Address, sender: Address, opt: TransactionOpt): F[Deferred[F, Hash]] = { + val paramsEncoded = TypeInfo[TupleType1[Address]].encode(TupleType1.apply[Address](newOwner)) val functionId = Hex.hex2Bytes("f2fde38b") val encoded = functionId ++ paramsEncoded - impl.sendTransaction(encoded, sender, opt) + impl.sendTransaction(CallArgs(encoded, sender, opt)) } - def VERSION(sender: Address, opt: TransactionOpt): Future[TupleType1[StringType]] = { + def VERSION(sender: Address, opt: TransactionOpt): F[TupleType1[StringType]] = { val encoded = Hex.hex2Bytes("ffa1ad74") - impl.call(encoded, sender, opt).map { bytes => - val result = implicitly[TypeInfo[TupleType1[StringType]]].decode(bytes, 0) + for (promise <- impl.call(CallArgs(encoded, sender, opt)); data <- promise.get) yield { + val result = TypeInfo[TupleType1[StringType]].decode(data, 0) result._1 } } - def deploy(_zrxAssetData: DynamicBytes, sender: Address, opt: TransactionOpt): Unit = { - val paramsEncoded = implicitly[TypeInfo[TupleType1[DynamicBytes]]].encode(TupleType1.apply[DynamicBytes](_zrxAssetData)) + def deploy(_zrxAssetData: DynamicBytes, sender: Address, opt: TransactionOpt): F[Deferred[F, Hash]] = { + val paramsEncoded = TypeInfo[TupleType1[DynamicBytes]].encode(TupleType1.apply[DynamicBytes](_zrxAssetData)) val code = Hex.hex2Bytes(binary) val encoded = code ++ paramsEncoded - impl.deploy(encoded, sender, opt) - } - def decodeSignatureValidatorApproval(log: Log): EventValue = { - var typeInfos = Seq.empty[TypeInfo[SolType]] - typeInfos = typeInfos :+ (implicitly[TypeInfo[Address]]) - typeInfos = typeInfos :+ (implicitly[TypeInfo[Address]]) - typeInfos = typeInfos :+ (implicitly[TypeInfo[TupleType1[Bool]]]) - EventValue.decodeEvent(typeInfos, log) - } - def subscribeSignatureValidatorApproval: Source[EventValue, NotUsed] = { - val query = LogQuery.from(contractAddress, Hash("0xa8656e308026eeabce8f0bc18048433252318ab80ac79da0b3d3d8697dfba891")) - impl.subscribeLogs(query).map(decodeSignatureValidatorApproval) - } - def decodeFill(log: Log): EventValue = { - var typeInfos = Seq.empty[TypeInfo[SolType]] - typeInfos = typeInfos :+ (implicitly[TypeInfo[Address]]) - typeInfos = typeInfos :+ (implicitly[TypeInfo[Address]]) - typeInfos = typeInfos :+ (implicitly[TypeInfo[Bytes32]]) - typeInfos = typeInfos :+ (implicitly[TypeInfo[TupleType8[Address, Address, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes]]]) - EventValue.decodeEvent(typeInfos, log) - } - def subscribeFill: Source[EventValue, NotUsed] = { - val query = LogQuery.from(contractAddress, Hash("0x0bcc4c97732e47d9946f229edb95f5b6323f601300e4690de719993f3c371129")) - impl.subscribeLogs(query).map(decodeFill) - } - def decodeCancel(log: Log): EventValue = { - var typeInfos = Seq.empty[TypeInfo[SolType]] - typeInfos = typeInfos :+ (implicitly[TypeInfo[Address]]) - typeInfos = typeInfos :+ (implicitly[TypeInfo[Address]]) - typeInfos = typeInfos :+ (implicitly[TypeInfo[Bytes32]]) - typeInfos = typeInfos :+ (implicitly[TypeInfo[TupleType3[Address, DynamicBytes, DynamicBytes]]]) - EventValue.decodeEvent(typeInfos, log) - } - def subscribeCancel: Source[EventValue, NotUsed] = { - val query = LogQuery.from(contractAddress, Hash("0xdc47b3613d9fe400085f6dbdc99453462279057e6207385042827ed6b1a62cf7")) - impl.subscribeLogs(query).map(decodeCancel) - } - def decodeCancelUpTo(log: Log): EventValue = { - var typeInfos = Seq.empty[TypeInfo[SolType]] - typeInfos = typeInfos :+ (implicitly[TypeInfo[Address]]) - typeInfos = typeInfos :+ (implicitly[TypeInfo[Address]]) - typeInfos = typeInfos :+ (implicitly[TypeInfo[TupleType1[Uint256]]]) - EventValue.decodeEvent(typeInfos, log) - } - def subscribeCancelUpTo: Source[EventValue, NotUsed] = { - val query = LogQuery.from(contractAddress, Hash("0x82af639571738f4ebd4268fb0363d8957ebe1bbb9e78dba5ebd69eed39b154f0")) - impl.subscribeLogs(query).map(decodeCancelUpTo) - } - def decodeAssetProxyRegistered(log: Log): EventValue = { - var typeInfos = Seq.empty[TypeInfo[SolType]] - typeInfos = typeInfos :+ (implicitly[TypeInfo[TupleType2[Bytes4, Address]]]) - EventValue.decodeEvent(typeInfos, log) - } - def subscribeAssetProxyRegistered: Source[EventValue, NotUsed] = { - val query = LogQuery.from(contractAddress, Hash("0xd2c6b762299c609bdb96520b58a49bfb80186934d4f71a86a367571a15c03194")) - impl.subscribeLogs(query).map(decodeAssetProxyRegistered) - } - def isDeployed: Boolean = impl.isDeployed - def contractAddress: Address = impl.address.get - def loadFrom(contractAddress: Address) = impl.load(contractAddress) + impl.deploy(CallArgs(encoded, sender, opt)) + } + private def decodeSignatureValidatorApproval(log: Log): Event = { + val typeInfo25 = TypeInfo[Address] + val typeInfo26 = TypeInfo[Address] + val typeInfo27 = TypeInfo[TupleType1[Bool]] + val typeInfos: List[TypeInfo[SolType]] = List(typeInfo25, typeInfo26, typeInfo27) + Event.decode(typeInfos, log) + } + def subscribeSignatureValidatorApproval: F[SubscriptionResult[F, Event]] = { + for (result <- impl.subscribeLogs(Bytes32.from("0xa8656e308026eeabce8f0bc18048433252318ab80ac79da0b3d3d8697dfba891"))) yield SubscriptionResult[F, Event](result.id, result.stream.map(decodeSignatureValidatorApproval)) + } + private def decodeFill(log: Log): Event = { + val typeInfo28 = TypeInfo[Address] + val typeInfo29 = TypeInfo[Address] + val typeInfo30 = TypeInfo[Bytes32] + val typeInfo31 = TypeInfo[TupleType8[Address, Address, Uint256, Uint256, Uint256, Uint256, DynamicBytes, DynamicBytes]] + val typeInfos: List[TypeInfo[SolType]] = List(typeInfo28, typeInfo29, typeInfo30, typeInfo31) + Event.decode(typeInfos, log) + } + def subscribeFill: F[SubscriptionResult[F, Event]] = { + for (result <- impl.subscribeLogs(Bytes32.from("0x0bcc4c97732e47d9946f229edb95f5b6323f601300e4690de719993f3c371129"))) yield SubscriptionResult[F, Event](result.id, result.stream.map(decodeFill)) + } + private def decodeCancel(log: Log): Event = { + val typeInfo32 = TypeInfo[Address] + val typeInfo33 = TypeInfo[Address] + val typeInfo34 = TypeInfo[Bytes32] + val typeInfo35 = TypeInfo[TupleType3[Address, DynamicBytes, DynamicBytes]] + val typeInfos: List[TypeInfo[SolType]] = List(typeInfo32, typeInfo33, typeInfo34, typeInfo35) + Event.decode(typeInfos, log) + } + def subscribeCancel: F[SubscriptionResult[F, Event]] = { + for (result <- impl.subscribeLogs(Bytes32.from("0xdc47b3613d9fe400085f6dbdc99453462279057e6207385042827ed6b1a62cf7"))) yield SubscriptionResult[F, Event](result.id, result.stream.map(decodeCancel)) + } + private def decodeCancelUpTo(log: Log): Event = { + val typeInfo36 = TypeInfo[Address] + val typeInfo37 = TypeInfo[Address] + val typeInfo38 = TypeInfo[TupleType1[Uint256]] + val typeInfos: List[TypeInfo[SolType]] = List(typeInfo36, typeInfo37, typeInfo38) + Event.decode(typeInfos, log) + } + def subscribeCancelUpTo: F[SubscriptionResult[F, Event]] = { + for (result <- impl.subscribeLogs(Bytes32.from("0x82af639571738f4ebd4268fb0363d8957ebe1bbb9e78dba5ebd69eed39b154f0"))) yield SubscriptionResult[F, Event](result.id, result.stream.map(decodeCancelUpTo)) + } + private def decodeAssetProxyRegistered(log: Log): Event = { + val typeInfo39 = TypeInfo[TupleType2[Bytes4, Address]] + val typeInfos: List[TypeInfo[SolType]] = List(typeInfo39) + Event.decode(typeInfos, log) + } + def subscribeAssetProxyRegistered: F[SubscriptionResult[F, Event]] = { + for (result <- impl.subscribeLogs(Bytes32.from("0xd2c6b762299c609bdb96520b58a49bfb80186934d4f71a86a367571a15c03194"))) yield SubscriptionResult[F, Event](result.id, result.stream.map(decodeAssetProxyRegistered)) + } +} +object Exchange { + def apply[F[_]: ConcurrentEffect: Timer](endpoint: String)(implicit CS: ContextShift[F]): Resource[F, Exchange[F]] = { + Contract[F](endpoint).flatMap(impl => Resource.liftF(Applicative[F].pure(new Exchange(impl)))) + } } \ No newline at end of file diff --git a/examples/src/main/scala/examples/kvstore/KVStore.scala b/examples/src/main/scala/examples/kvstore/KVStore.scala new file mode 100644 index 0000000..eaac5b7 --- /dev/null +++ b/examples/src/main/scala/examples/kvstore/KVStore.scala @@ -0,0 +1,65 @@ +// AUTO GENERATED, DO NOT EDIT + +package examples.kvstore +import ethabi.util._ +import ethabi.types._ +import ethabi.types.generated._ +import ethabi.protocol._ +import ethabi.protocol.Request._ +import ethabi.protocol.Response.Log +import ethabi.protocol.Subscription.SubscriptionResult +import cats.implicits._ +import cats.Applicative +import cats.effect._ +import cats.effect.concurrent._ +final class KVStore[F[_]: ConcurrentEffect: Timer] private (private val impl: Contract[F]) { self => + private val binary = "6080604052610542806100136000396000f300608060405260043610610057576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630df584f11461005c578063385ddf0014610106578063b3fd15a014610170575b600080fd5b34801561006857600080fd5b5061008b600480360381019080803561ffff16906020019092919050505061021a565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100cb5780820151818401526020810190506100b0565b50505050905090810190601f1680156100f85780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61016e600480360381019080803561ffff169060200190929190803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091929192905050506102ca565b005b34801561017c57600080fd5b5061019f600480360381019080803561ffff1690602001909291905050506103b5565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156101df5780820151818401526020810190506101c4565b50505050905090810190601f16801561020c5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b60006020528060005260406000206000915090508054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156102c25780601f10610297576101008083540402835291602001916102c2565b820191906000526020600020905b8154815290600101906020018083116102a557829003601f168201915b505050505081565b806000808461ffff1661ffff16815260200190815260200160002090805190602001906102f8929190610471565b508161ffff163373ffffffffffffffffffffffffffffffffffffffff167f5f9f5e049bc49bc3d2067c5c5077871baab0b4e104a1554c8ac68f7c9fd81884836040518080602001828103825283818151815260200191508051906020019080838360005b8381101561037757808201518184015260208101905061035c565b50505050905090810190601f1680156103a45780820380516001836020036101000a031916815260200191505b509250505060405180910390a35050565b60606000808361ffff1661ffff1681526020019081526020016000208054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156104655780601f1061043a57610100808354040283529160200191610465565b820191906000526020600020905b81548152906001019060200180831161044857829003601f168201915b50505050509050919050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106104b257805160ff19168380011785556104e0565b828001600101855582156104e0579182015b828111156104df5782518255916020019190600101906104c4565b5b5090506104ed91906104f1565b5090565b61051391905b8082111561050f5760008160009055506001016104f7565b5090565b905600a165627a7a7230582061428827ea316a5ddc2cc0c135b621c5525c61031d2556d7586f05dd0764c9810029" + def client: F[Client[F]] = impl.client + def subscriber: F[Subscriber[F]] = impl.subscriber + def isDeployed: F[Boolean] = impl.isDeployed + def address: F[Option[Address]] = impl.address + def loadFrom(address: Address): F[Unit] = impl.load(address) + def data(fresh1: Uint16, sender: Address, opt: TransactionOpt): F[TupleType1[DynamicBytes]] = { + val paramsEncoded = TypeInfo[TupleType1[Uint16]].encode(TupleType1.apply[Uint16](fresh1)) + val functionId = Hex.hex2Bytes("0df584f1") + val encoded = functionId ++ paramsEncoded + for (promise <- impl.call(CallArgs(encoded, sender, opt)); data <- promise.get) yield { + val result = TypeInfo[TupleType1[DynamicBytes]].decode(data, 0) + result._1 + } + } + def set(_key: Uint16, _value: DynamicBytes, sender: Address, opt: TransactionOpt): F[Deferred[F, Hash]] = { + val paramsEncoded = TypeInfo[TupleType2[Uint16, DynamicBytes]].encode(TupleType2.apply[Uint16, DynamicBytes](_key, _value)) + val functionId = Hex.hex2Bytes("385ddf00") + val encoded = functionId ++ paramsEncoded + impl.sendTransaction(CallArgs(encoded, sender, opt)) + } + def get(_key: Uint16, sender: Address, opt: TransactionOpt): F[TupleType1[DynamicBytes]] = { + val paramsEncoded = TypeInfo[TupleType1[Uint16]].encode(TupleType1.apply[Uint16](_key)) + val functionId = Hex.hex2Bytes("b3fd15a0") + val encoded = functionId ++ paramsEncoded + for (promise <- impl.call(CallArgs(encoded, sender, opt)); data <- promise.get) yield { + val result = TypeInfo[TupleType1[DynamicBytes]].decode(data, 0) + result._1 + } + } + def deploy(sender: Address, opt: TransactionOpt): F[Deferred[F, Hash]] = { + val encoded = Hex.hex2Bytes(binary) + impl.deploy(CallArgs(encoded, sender, opt)) + } + private def decodeRecord(log: Log): Event = { + val typeInfo4 = TypeInfo[Address] + val typeInfo5 = TypeInfo[Uint16] + val typeInfo6 = TypeInfo[TupleType1[DynamicBytes]] + val typeInfos: List[TypeInfo[SolType]] = List(typeInfo4, typeInfo5, typeInfo6) + Event.decode(typeInfos, log) + } + def subscribeRecord: F[SubscriptionResult[F, Event]] = { + for (result <- impl.subscribeLogs(Bytes32.from("0x5f9f5e049bc49bc3d2067c5c5077871baab0b4e104a1554c8ac68f7c9fd81884"))) yield SubscriptionResult[F, Event](result.id, result.stream.map(decodeRecord)) + } +} +object KVStore { + def apply[F[_]: ConcurrentEffect: Timer](endpoint: String)(implicit CS: ContextShift[F]): Resource[F, KVStore[F]] = { + Contract[F](endpoint).flatMap(impl => Resource.liftF(Applicative[F].pure(new KVStore(impl)))) + } +} \ No newline at end of file diff --git a/examples/src/main/scala/examples/kvstore/Main.scala b/examples/src/main/scala/examples/kvstore/Main.scala new file mode 100644 index 0000000..d46b57f --- /dev/null +++ b/examples/src/main/scala/examples/kvstore/Main.scala @@ -0,0 +1,63 @@ +package examples +package kvstore + +import cats.effect._ +import ethabi.types._ +import ethabi.types.generated._ +import ethabi.protocol._ +import ethabi.protocol.Request._ +import ethabi.protocol.Response._ +import retry.RetryPolicies +import scala.concurrent.duration._ + +object Main extends IOApp { + + private def log(str: String): IO[Unit] = IO.delay(println(s"${Thread.currentThread.getName}, $str")) + + override def run(args: List[String]): IO[ExitCode] = { + val sender = Address("60f7947aef8bbc9bc314a9b8db8096099345fba3") + val transactionOpt = TransactionOpt(Some(400000), Some(1000), None, None) + val retryPolicy = retry.RetryPolicies.limitRetries[IO](5).join(RetryPolicies.constantDelay[IO](5 seconds)) + KVStore[IO]("ws://127.0.0.1:8546").use { kvStore => + val task = for { + client <- kvStore.client + peerCount <- client.peerCount.flatMap(_.get) + _ <- log(s"peer count: $peerCount") + cliVersion <- client.clientVersion.flatMap(_.get) + _ <- log(s"client version: $cliVersion") + work <- client.getWork.flatMap(_.get) + _ <- log(s"work response: $work") + protocolV <- client.protocolVersion.flatMap(_.get) + _ <- log(s"protocol version: $protocolV") + coinbase <- client.coinbase.flatMap(_.get) + _ <- log(s"coinbase address: $coinbase") + syncStatus <- client.syncing.flatMap(_.get) + _ <- log(s"sync status: $syncStatus") + deployHash <- kvStore.deploy(sender, transactionOpt) + address <- retryUntil[IO, Option[Address]]("wait contract deployed", retryPolicy, kvStore.address, _.isDefined).map(_.get) + _ <- log(s"contract deploy succeed, address: $address") + contractTx <- deployHash.get.flatMap(client.getTransactionByHash).flatMap(_.get) + _ <- log(s"contract deploy tx: ${contractTx.get}") + result <- kvStore.subscribeRecord + _ <- log(s"subscription id: ${result.id}") + fiber <- result.stream.forall { event => + println(event) + true + }.compile.drain.start + txHash <- kvStore.set(Uint16(12), DynamicBytes.from("0x010203040506070809"), sender, transactionOpt).flatMap(_.get) + receipt <- retryUntil[IO, Option[TransactionReceipt]]( + "wait tx receipt", + retryPolicy, + client.getTransactionReceipt(txHash).flatMap(_.get), + _.isDefined + ).map(_.get) + _ <- log(s"tx receipt: $receipt") + result <- kvStore.get(Uint16(12), sender, transactionOpt) + _ <- log(s"key: 12, value: $result") + _ <- fiber.cancel + _ <- log("quit now") + } yield () + task.handleErrorWith(exp => IO.delay(exp.printStackTrace())) *> IO.delay(ExitCode.Success) + } + } +} diff --git a/examples/src/main/scala/examples/token/Token.scala b/examples/src/main/scala/examples/token/Token.scala index 1696ee6..d75dc04 100644 --- a/examples/src/main/scala/examples/token/Token.scala +++ b/examples/src/main/scala/examples/token/Token.scala @@ -1,137 +1,141 @@ // AUTO GENERATED, DO NOT EDIT package examples.token -import akka.NotUsed -import akka.stream.scaladsl.Source -import ethabi.util.{ Hex, Hash } +import ethabi.util._ import ethabi.types._ import ethabi.types.generated._ -import ethabi.protocol.{ Contract, EventValue } +import ethabi.protocol._ import ethabi.protocol.Request._ import ethabi.protocol.Response.Log -import scala.concurrent.Future -final class Token(endpoint: String) { self => - private val impl = Contract(endpoint) - import impl.dispatcher - def service = impl.service +import ethabi.protocol.Subscription.SubscriptionResult +import cats.implicits._ +import cats.Applicative +import cats.effect._ +import cats.effect.concurrent._ +final class Token[F[_]: ConcurrentEffect: Timer] private (private val impl: Contract[F]) { self => private val binary = "60806040523480156200001157600080fd5b506040805190810160405280600b81526020017f53696d706c65546f6b656e0000000000000000000000000000000000000000008152506040805190810160405280600381526020017f53494d0000000000000000000000000000000000000000000000000000000000815250601282600390805190602001906200009892919062000293565b508160049080519060200190620000b192919062000293565b5080600560006101000a81548160ff021916908360ff160217905550505050620000f633601260ff16600a0a61271002620000fc640100000000026401000000009004565b62000342565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16141515156200013957600080fd5b6200015e81600254620002716401000000000262001159179091906401000000009004565b600281905550620001c5816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054620002716401000000000262001159179091906401000000009004565b6000808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508173ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040518082815260200191505060405180910390a35050565b60008082840190508381101515156200028957600080fd5b8091505092915050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10620002d657805160ff191683800117855562000307565b8280016001018555821562000307579182015b8281111562000306578251825591602001919060010190620002e9565b5b5090506200031691906200031a565b5090565b6200033f91905b808211156200033b57600081600090555060010162000321565b5090565b90565b6111a680620003526000396000f3fe608060405234801561001057600080fd5b50600436106100ec576000357c010000000000000000000000000000000000000000000000000000000090048063313ce567116100a957806395d89b411161008357806395d89b41146103a2578063a457c2d714610425578063a9059cbb1461048b578063dd62ed3e146104f1576100ec565b8063313ce567146102c057806339509351146102e457806370a082311461034a576100ec565b806306fdde03146100f1578063095ea7b31461017457806318160ddd146101da57806323b872dd146101f85780632e0f26251461027e5780632ff2e9dc146102a2575b600080fd5b6100f9610569565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561013957808201518184015260208101905061011e565b50505050905090810190601f1680156101665780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6101c06004803603604081101561018a57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919050505061060b565b604051808215151515815260200191505060405180910390f35b6101e2610738565b6040518082815260200191505060405180910390f35b6102646004803603606081101561020e57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610742565b604051808215151515815260200191505060405180910390f35b61028661094a565b604051808260ff1660ff16815260200191505060405180910390f35b6102aa61094f565b6040518082815260200191505060405180910390f35b6102c861095e565b604051808260ff1660ff16815260200191505060405180910390f35b610330600480360360408110156102fa57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610975565b604051808215151515815260200191505060405180910390f35b61038c6004803603602081101561036057600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610bac565b6040518082815260200191505060405180910390f35b6103aa610bf4565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156103ea5780820151818401526020810190506103cf565b50505050905090810190601f1680156104175780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6104716004803603604081101561043b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610c96565b604051808215151515815260200191505060405180910390f35b6104d7600480360360408110156104a157600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610ecd565b604051808215151515815260200191505060405180910390f35b6105536004803603604081101561050757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610ee4565b6040518082815260200191505060405180910390f35b606060038054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156106015780601f106105d657610100808354040283529160200191610601565b820191906000526020600020905b8154815290600101906020018083116105e457829003601f168201915b5050505050905090565b60008073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161415151561064857600080fd5b81600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b6000600254905090565b60006107d382600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054610f6b90919063ffffffff16565b600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555061085e848484610f8d565b3373ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925600160008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546040518082815260200191505060405180910390a3600190509392505050565b601281565b601260ff16600a0a6127100281565b6000600560009054906101000a900460ff16905090565b60008073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16141515156109b257600080fd5b610a4182600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205461115990919063ffffffff16565b600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546040518082815260200191505060405180910390a36001905092915050565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b606060048054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610c8c5780601f10610c6157610100808354040283529160200191610c8c565b820191906000526020600020905b815481529060010190602001808311610c6f57829003601f168201915b5050505050905090565b60008073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1614151515610cd357600080fd5b610d6282600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054610f6b90919063ffffffff16565b600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546040518082815260200191505060405180910390a36001905092915050565b6000610eda338484610f8d565b6001905092915050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b6000828211151515610f7c57600080fd5b600082840390508091505092915050565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614151515610fc957600080fd5b61101a816000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054610f6b90919063ffffffff16565b6000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055506110ad816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205461115990919063ffffffff16565b6000808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040518082815260200191505060405180910390a3505050565b600080828401905083811015151561117057600080fd5b809150509291505056fea165627a7a7230582008f3a090533ddacdd7c8c5ec44ffd7b040994ae055e7f95b00e42d19cbc274e20029" - def name(sender: Address, opt: TransactionOpt): Future[TupleType1[StringType]] = { + def client: F[Client[F]] = impl.client + def subscriber: F[Subscriber[F]] = impl.subscriber + def isDeployed: F[Boolean] = impl.isDeployed + def address: F[Option[Address]] = impl.address + def loadFrom(address: Address): F[Unit] = impl.load(address) + def name(sender: Address, opt: TransactionOpt): F[TupleType1[StringType]] = { val encoded = Hex.hex2Bytes("06fdde03") - impl.call(encoded, sender, opt).map { bytes => - val result = implicitly[TypeInfo[TupleType1[StringType]]].decode(bytes, 0) + for (promise <- impl.call(CallArgs(encoded, sender, opt)); data <- promise.get) yield { + val result = TypeInfo[TupleType1[StringType]].decode(data, 0) result._1 } } - def approve(spender: Address, value: Uint256, sender: Address, opt: TransactionOpt): Future[Hash] = { - val paramsEncoded = implicitly[TypeInfo[TupleType2[Address, Uint256]]].encode(TupleType2.apply[Address, Uint256](spender, value)) + def approve(spender: Address, value: Uint256, sender: Address, opt: TransactionOpt): F[Deferred[F, Hash]] = { + val paramsEncoded = TypeInfo[TupleType2[Address, Uint256]].encode(TupleType2.apply[Address, Uint256](spender, value)) val functionId = Hex.hex2Bytes("095ea7b3") val encoded = functionId ++ paramsEncoded - impl.sendTransaction(encoded, sender, opt) + impl.sendTransaction(CallArgs(encoded, sender, opt)) } - def totalSupply(sender: Address, opt: TransactionOpt): Future[TupleType1[Uint256]] = { + def totalSupply(sender: Address, opt: TransactionOpt): F[TupleType1[Uint256]] = { val encoded = Hex.hex2Bytes("18160ddd") - impl.call(encoded, sender, opt).map { bytes => - val result = implicitly[TypeInfo[TupleType1[Uint256]]].decode(bytes, 0) + for (promise <- impl.call(CallArgs(encoded, sender, opt)); data <- promise.get) yield { + val result = TypeInfo[TupleType1[Uint256]].decode(data, 0) result._1 } } - def transferFrom(from: Address, to: Address, value: Uint256, sender: Address, opt: TransactionOpt): Future[Hash] = { - val paramsEncoded = implicitly[TypeInfo[TupleType3[Address, Address, Uint256]]].encode(TupleType3.apply[Address, Address, Uint256](from, to, value)) + def transferFrom(from: Address, to: Address, value: Uint256, sender: Address, opt: TransactionOpt): F[Deferred[F, Hash]] = { + val paramsEncoded = TypeInfo[TupleType3[Address, Address, Uint256]].encode(TupleType3.apply[Address, Address, Uint256](from, to, value)) val functionId = Hex.hex2Bytes("23b872dd") val encoded = functionId ++ paramsEncoded - impl.sendTransaction(encoded, sender, opt) + impl.sendTransaction(CallArgs(encoded, sender, opt)) } - def DECIMALS(sender: Address, opt: TransactionOpt): Future[TupleType1[Uint8]] = { + def DECIMALS(sender: Address, opt: TransactionOpt): F[TupleType1[Uint8]] = { val encoded = Hex.hex2Bytes("2e0f2625") - impl.call(encoded, sender, opt).map { bytes => - val result = implicitly[TypeInfo[TupleType1[Uint8]]].decode(bytes, 0) + for (promise <- impl.call(CallArgs(encoded, sender, opt)); data <- promise.get) yield { + val result = TypeInfo[TupleType1[Uint8]].decode(data, 0) result._1 } } - def INITIAL_SUPPLY(sender: Address, opt: TransactionOpt): Future[TupleType1[Uint256]] = { + def INITIAL_SUPPLY(sender: Address, opt: TransactionOpt): F[TupleType1[Uint256]] = { val encoded = Hex.hex2Bytes("2ff2e9dc") - impl.call(encoded, sender, opt).map { bytes => - val result = implicitly[TypeInfo[TupleType1[Uint256]]].decode(bytes, 0) + for (promise <- impl.call(CallArgs(encoded, sender, opt)); data <- promise.get) yield { + val result = TypeInfo[TupleType1[Uint256]].decode(data, 0) result._1 } } - def decimals(sender: Address, opt: TransactionOpt): Future[TupleType1[Uint8]] = { + def decimals(sender: Address, opt: TransactionOpt): F[TupleType1[Uint8]] = { val encoded = Hex.hex2Bytes("313ce567") - impl.call(encoded, sender, opt).map { bytes => - val result = implicitly[TypeInfo[TupleType1[Uint8]]].decode(bytes, 0) + for (promise <- impl.call(CallArgs(encoded, sender, opt)); data <- promise.get) yield { + val result = TypeInfo[TupleType1[Uint8]].decode(data, 0) result._1 } } - def increaseAllowance(spender: Address, addedValue: Uint256, sender: Address, opt: TransactionOpt): Future[Hash] = { - val paramsEncoded = implicitly[TypeInfo[TupleType2[Address, Uint256]]].encode(TupleType2.apply[Address, Uint256](spender, addedValue)) + def increaseAllowance(spender: Address, addedValue: Uint256, sender: Address, opt: TransactionOpt): F[Deferred[F, Hash]] = { + val paramsEncoded = TypeInfo[TupleType2[Address, Uint256]].encode(TupleType2.apply[Address, Uint256](spender, addedValue)) val functionId = Hex.hex2Bytes("39509351") val encoded = functionId ++ paramsEncoded - impl.sendTransaction(encoded, sender, opt) + impl.sendTransaction(CallArgs(encoded, sender, opt)) } - def balanceOf(owner: Address, sender: Address, opt: TransactionOpt): Future[TupleType1[Uint256]] = { - val paramsEncoded = implicitly[TypeInfo[TupleType1[Address]]].encode(TupleType1.apply[Address](owner)) + def balanceOf(owner: Address, sender: Address, opt: TransactionOpt): F[TupleType1[Uint256]] = { + val paramsEncoded = TypeInfo[TupleType1[Address]].encode(TupleType1.apply[Address](owner)) val functionId = Hex.hex2Bytes("70a08231") val encoded = functionId ++ paramsEncoded - impl.call(encoded, sender, opt).map { bytes => - val result = implicitly[TypeInfo[TupleType1[Uint256]]].decode(bytes, 0) + for (promise <- impl.call(CallArgs(encoded, sender, opt)); data <- promise.get) yield { + val result = TypeInfo[TupleType1[Uint256]].decode(data, 0) result._1 } } - def symbol(sender: Address, opt: TransactionOpt): Future[TupleType1[StringType]] = { + def symbol(sender: Address, opt: TransactionOpt): F[TupleType1[StringType]] = { val encoded = Hex.hex2Bytes("95d89b41") - impl.call(encoded, sender, opt).map { bytes => - val result = implicitly[TypeInfo[TupleType1[StringType]]].decode(bytes, 0) + for (promise <- impl.call(CallArgs(encoded, sender, opt)); data <- promise.get) yield { + val result = TypeInfo[TupleType1[StringType]].decode(data, 0) result._1 } } - def decreaseAllowance(spender: Address, subtractedValue: Uint256, sender: Address, opt: TransactionOpt): Future[Hash] = { - val paramsEncoded = implicitly[TypeInfo[TupleType2[Address, Uint256]]].encode(TupleType2.apply[Address, Uint256](spender, subtractedValue)) + def decreaseAllowance(spender: Address, subtractedValue: Uint256, sender: Address, opt: TransactionOpt): F[Deferred[F, Hash]] = { + val paramsEncoded = TypeInfo[TupleType2[Address, Uint256]].encode(TupleType2.apply[Address, Uint256](spender, subtractedValue)) val functionId = Hex.hex2Bytes("a457c2d7") val encoded = functionId ++ paramsEncoded - impl.sendTransaction(encoded, sender, opt) + impl.sendTransaction(CallArgs(encoded, sender, opt)) } - def transfer(to: Address, value: Uint256, sender: Address, opt: TransactionOpt): Future[Hash] = { - val paramsEncoded = implicitly[TypeInfo[TupleType2[Address, Uint256]]].encode(TupleType2.apply[Address, Uint256](to, value)) + def transfer(to: Address, value: Uint256, sender: Address, opt: TransactionOpt): F[Deferred[F, Hash]] = { + val paramsEncoded = TypeInfo[TupleType2[Address, Uint256]].encode(TupleType2.apply[Address, Uint256](to, value)) val functionId = Hex.hex2Bytes("a9059cbb") val encoded = functionId ++ paramsEncoded - impl.sendTransaction(encoded, sender, opt) + impl.sendTransaction(CallArgs(encoded, sender, opt)) } - def allowance(owner: Address, spender: Address, sender: Address, opt: TransactionOpt): Future[TupleType1[Uint256]] = { - val paramsEncoded = implicitly[TypeInfo[TupleType2[Address, Address]]].encode(TupleType2.apply[Address, Address](owner, spender)) + def allowance(owner: Address, spender: Address, sender: Address, opt: TransactionOpt): F[TupleType1[Uint256]] = { + val paramsEncoded = TypeInfo[TupleType2[Address, Address]].encode(TupleType2.apply[Address, Address](owner, spender)) val functionId = Hex.hex2Bytes("dd62ed3e") val encoded = functionId ++ paramsEncoded - impl.call(encoded, sender, opt).map { bytes => - val result = implicitly[TypeInfo[TupleType1[Uint256]]].decode(bytes, 0) + for (promise <- impl.call(CallArgs(encoded, sender, opt)); data <- promise.get) yield { + val result = TypeInfo[TupleType1[Uint256]].decode(data, 0) result._1 } } - def deploy(sender: Address, opt: TransactionOpt): Unit = { + def deploy(sender: Address, opt: TransactionOpt): F[Deferred[F, Hash]] = { val encoded = Hex.hex2Bytes(binary) - impl.deploy(encoded, sender, opt) - } - def decodeTransfer(log: Log): EventValue = { - var typeInfos = Seq.empty[TypeInfo[SolType]] - typeInfos = typeInfos :+ (implicitly[TypeInfo[Address]]) - typeInfos = typeInfos :+ (implicitly[TypeInfo[Address]]) - typeInfos = typeInfos :+ (implicitly[TypeInfo[TupleType1[Uint256]]]) - EventValue.decodeEvent(typeInfos, log) - } - def subscribeTransfer: Source[EventValue, NotUsed] = { - val query = LogQuery.from(contractAddress, Hash("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")) - impl.subscribeLogs(query).map(decodeTransfer) - } - def decodeApproval(log: Log): EventValue = { - var typeInfos = Seq.empty[TypeInfo[SolType]] - typeInfos = typeInfos :+ (implicitly[TypeInfo[Address]]) - typeInfos = typeInfos :+ (implicitly[TypeInfo[Address]]) - typeInfos = typeInfos :+ (implicitly[TypeInfo[TupleType1[Uint256]]]) - EventValue.decodeEvent(typeInfos, log) - } - def subscribeApproval: Source[EventValue, NotUsed] = { - val query = LogQuery.from(contractAddress, Hash("0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925")) - impl.subscribeLogs(query).map(decodeApproval) - } - def isDeployed: Boolean = impl.isDeployed - def contractAddress: Address = impl.address.get - def loadFrom(contractAddress: Address) = impl.load(contractAddress) + impl.deploy(CallArgs(encoded, sender, opt)) + } + private def decodeTransfer(log: Log): Event = { + val typeInfo14 = TypeInfo[Address] + val typeInfo15 = TypeInfo[Address] + val typeInfo16 = TypeInfo[TupleType1[Uint256]] + val typeInfos: List[TypeInfo[SolType]] = List(typeInfo14, typeInfo15, typeInfo16) + Event.decode(typeInfos, log) + } + def subscribeTransfer: F[SubscriptionResult[F, Event]] = { + for (result <- impl.subscribeLogs(Bytes32.from("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"))) yield SubscriptionResult[F, Event](result.id, result.stream.map(decodeTransfer)) + } + private def decodeApproval(log: Log): Event = { + val typeInfo17 = TypeInfo[Address] + val typeInfo18 = TypeInfo[Address] + val typeInfo19 = TypeInfo[TupleType1[Uint256]] + val typeInfos: List[TypeInfo[SolType]] = List(typeInfo17, typeInfo18, typeInfo19) + Event.decode(typeInfos, log) + } + def subscribeApproval: F[SubscriptionResult[F, Event]] = { + for (result <- impl.subscribeLogs(Bytes32.from("0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925"))) yield SubscriptionResult[F, Event](result.id, result.stream.map(decodeApproval)) + } +} +object Token { + def apply[F[_]: ConcurrentEffect: Timer](endpoint: String)(implicit CS: ContextShift[F]): Resource[F, Token[F]] = { + Contract[F](endpoint).flatMap(impl => Resource.liftF(Applicative[F].pure(new Token(impl)))) + } } \ No newline at end of file diff --git a/examples/src/main/scala/examples/trivial/Trivial.scala b/examples/src/main/scala/examples/trivial/Trivial.scala deleted file mode 100644 index 182506a..0000000 --- a/examples/src/main/scala/examples/trivial/Trivial.scala +++ /dev/null @@ -1,42 +0,0 @@ -// AUTO GENERATED, DO NOT EDIT - -package examples.trivial -import akka.NotUsed -import akka.stream.scaladsl.Source -import ethabi.util.{ Hex, Hash } -import ethabi.types._ -import ethabi.types.generated._ -import ethabi.protocol.{ Contract, EventValue } -import ethabi.protocol.Request._ -import ethabi.protocol.Response.Log -import scala.concurrent.Future -final class Trivial(endpoint: String) { self => - private val impl = Contract(endpoint) - import impl.dispatcher - def service = impl.service - private val binary = "608060405234801561001057600080fd5b50610374806100206000396000f300608060405260043610610041576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806385799c3614610046575b600080fd5b34801561005257600080fd5b5061006d600480360361006891908101906101cb565b61006f565b005b806040015181600001517f174e799db4473820d2ac095205358bba921759d7f2fb09509751a40a543842aa836020015184606001516040516100b2929190610242565b60405180910390a350565b600082601f83011215156100d057600080fd5b81356100e36100de826102a6565b610279565b915080825260208301602083018583830111156100ff57600080fd5b61010a8382846102e7565b50505092915050565b60006080828403121561012557600080fd5b61012f6080610279565b9050600061013f848285016101b7565b600083015250602082013567ffffffffffffffff81111561015f57600080fd5b61016b848285016100bd565b602083015250604061017f848285016101b7565b604083015250606082013567ffffffffffffffff81111561019f57600080fd5b6101ab848285016100bd565b60608301525092915050565b60006101c382356102dd565b905092915050565b6000602082840312156101dd57600080fd5b600082013567ffffffffffffffff8111156101f757600080fd5b61020384828501610113565b91505092915050565b6000610217826102d2565b80845261022b8160208601602086016102f6565b61023481610329565b602085010191505092915050565b6000604082019050818103600083015261025c818561020c565b90508181036020830152610270818461020c565b90509392505050565b6000604051905081810181811067ffffffffffffffff8211171561029c57600080fd5b8060405250919050565b600067ffffffffffffffff8211156102bd57600080fd5b601f19601f8301169050602081019050919050565b600081519050919050565b6000819050919050565b82818337600083830152505050565b60005b838110156103145780820151818401526020810190506102f9565b83811115610323576000848401525b50505050565b6000601f19601f83011690509190505600a265627a7a723058201ce538708976bc5eb642066442b9156e9bf442c7f38e3977bac329d2191e7b236c6578706572696d656e74616cf50037" - def trigger(t: TupleType4[Uint256, DynamicBytes, Uint256, DynamicBytes], sender: Address, opt: TransactionOpt): Future[Hash] = { - val paramsEncoded = implicitly[TypeInfo[TupleType1[TupleType4[Uint256, DynamicBytes, Uint256, DynamicBytes]]]].encode(TupleType1.apply[TupleType4[Uint256, DynamicBytes, Uint256, DynamicBytes]](t)) - val functionId = Hex.hex2Bytes("85799c36") - val encoded = functionId ++ paramsEncoded - impl.sendTransaction(encoded, sender, opt) - } - def deploy(sender: Address, opt: TransactionOpt): Unit = { - val encoded = Hex.hex2Bytes(binary) - impl.deploy(encoded, sender, opt) - } - def decodeTestEvent(log: Log): EventValue = { - var typeInfos = Seq.empty[TypeInfo[SolType]] - typeInfos = typeInfos :+ (implicitly[TypeInfo[Uint256]]) - typeInfos = typeInfos :+ (implicitly[TypeInfo[Uint256]]) - typeInfos = typeInfos :+ (implicitly[TypeInfo[TupleType2[DynamicBytes, DynamicBytes]]]) - EventValue.decodeEvent(typeInfos, log) - } - def subscribeTestEvent: Source[EventValue, NotUsed] = { - val query = LogQuery.from(contractAddress, Hash("0x174e799db4473820d2ac095205358bba921759d7f2fb09509751a40a543842aa")) - impl.subscribeLogs(query).map(decodeTestEvent) - } - def isDeployed: Boolean = impl.isDeployed - def contractAddress: Address = impl.address.get - def loadFrom(contractAddress: Address) = impl.load(contractAddress) -} \ No newline at end of file diff --git a/examples/src/test/scala/examples/trivial/SimpleSpec.scala b/examples/src/test/scala/examples/trivial/SimpleSpec.scala deleted file mode 100644 index 5f0b1a4..0000000 --- a/examples/src/test/scala/examples/trivial/SimpleSpec.scala +++ /dev/null @@ -1,40 +0,0 @@ -package examples.trivial - -import akka.actor.ActorSystem -import akka.stream.Materializer -import org.scalatest.{Matchers, WordSpec} -import ethabi.protocol.Request.TransactionOpt -import ethabi.types.{Address, DynamicBytes} -import ethabi.types.generated.{TupleType4, Uint256} -import scala.util.{Failure, Success} - -class SimpleSpec extends WordSpec with Matchers { - "test simple contract" in { - implicit val system = ActorSystem() - implicit val materializer = Materializer(system) - import system.dispatcher - - val sender = Address("0xe538b17ebf20efcf1c426cf1480e8a2a4b87cb1b") - val contract = new Trivial("ws://127.0.0.1:8546") - val opt = TransactionOpt(Some(BigInt(1000000)), Some(BigInt(10000)), None, None) - contract.deploy(sender, opt) - while (!contract.isDeployed) { - Thread.sleep(1000) - } - println("deploy succeed, address is: " + contract.contractAddress) - - contract.subscribeTestEvent.runForeach(event => println(event.toString)) - - val a = Uint256(BigInt(1000)) - val b = DynamicBytes(Array[Byte](0x01, 0x02, 0x03, 0x04)) - val c = Uint256(BigInt(3333)) - val d = DynamicBytes(Array[Byte](0x11, 0x22, 0x33, 0x44)) - val t = TupleType4[Uint256, DynamicBytes, Uint256, DynamicBytes](a, b, c, d) - contract.trigger(t, sender, opt) onComplete { - case Success(txHash) => - println("call trigger succeed: " + txHash) - system.terminate() - case Failure(exception) => println("call trigger failed: " + exception) - } - } -} diff --git a/project/Dependencies.scala b/project/Dependencies.scala index a546aee..4b07fe6 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -10,9 +10,6 @@ object Dependencies { val scalaTest = "org.scalatest" %% "scalatest" % "3.1.1" % "test" val scalaMeta = "org.scalameta" %% "scalameta" % scalaMetaVersion val fastParser = "com.lihaoyi" %% "fastparse" % "2.3.0" - val actor = "com.typesafe.akka" %% "akka-actor" % akkaVersion - val stream = "com.typesafe.akka" %% "akka-stream" % akkaVersion - val akkaHttp = "com.typesafe.akka" %% "akka-http" % "10.1.11" val scopt = "com.github.scopt" %% "scopt" % "4.0.0-RC2" val circeCore = "io.circe" %% "circe-core" % circeVersion val circeGeneric = "io.circe" %% "circe-generic" % circeVersion @@ -20,10 +17,15 @@ object Dependencies { val scrypto = "org.scorexfoundation" %% "scrypto" % "2.1.8" val ammTerminal = "com.lihaoyi" %% "ammonite-terminal" % "2.1.0" val fansi = "com.lihaoyi" %% "fansi" % "0.2.9" + val httpClient = "org.http4s" %% "http4s-jdk-http-client" % "0.3.0" + val http4sCirce = "org.http4s" %% "http4s-circe" % "0.21.3" + val catsCore = "org.typelevel" %% "cats-core" % "2.1.1" + val catsEffect = "org.typelevel" %% "cats-effect" % "2.1.3" + val catsRetry = "com.github.cb372" %% "cats-retry" % "1.1.1" val l = libraryDependencies - val deps = l ++= Seq(scalaTest, actor, stream, scrypto, akkaHttp, circeCore, circeGeneric, circeParser) + val deps = l ++= Seq(catsCore, catsEffect, httpClient, http4sCirce, + catsRetry, scalaTest, scrypto, circeCore, circeGeneric, circeParser) val codegenDeps = l ++= Seq(scalaMeta, circeCore, circeGeneric, circeParser, fastParser, scopt, fansi, ammTerminal, scalaTest) - val examplesDpes = l ++= Seq(actor, stream, scalaTest) } diff --git a/src/main/scala/ethabi/protocol/Contract.scala b/src/main/scala/ethabi/protocol/Contract.scala deleted file mode 100644 index 7ff2bb1..0000000 --- a/src/main/scala/ethabi/protocol/Contract.scala +++ /dev/null @@ -1,107 +0,0 @@ -package ethabi.protocol - -import scala.concurrent.Future -import scala.concurrent.duration._ -import scala.util.{Failure, Success} -import akka.actor.ActorSystem -import akka.stream.Materializer -import ethabi.types.{Address, SolType, TupleType, TypeInfo} -import ethabi.util.{Hash, Hex} -import ethabi.protocol.ws.Client -import ethabi.protocol.Request._ -import ethabi.protocol.Response.Log -import ethabi.types.generated.Bytes32 - -final class Contract(val endpoint: String) { - private implicit val system = ActorSystem() - private implicit val materializer = Materializer(system) - private var contractCreator: Option[Address] = None - private var contractAddress: Option[Address] = None - private val client = Client(endpoint) - - implicit def dispatcher = system.dispatcher - def address: Option[Address] = contractAddress - def creator: Option[Address] = contractCreator - def load(address: Address): Unit = contractAddress = Some(address) - def isDeployed: Boolean = contractAddress.isDefined - def service: Service = client - - def sendTransaction(data: Array[Byte], sender: Address, opt: TransactionOpt): Future[Hash] = { - if (contractAddress.isEmpty) throw new RuntimeException("contract address is empty when call contract method") - val transaction = Transaction(sender, contractAddress, data, opt) - client.sendTransaction(transaction).map { - case Left(responseError) => throw new RuntimeException(s"send transaction failed, $responseError") - case Right(None) => throw new RuntimeException(s"no transaction hash after send succeed") - case Right(Some(txHash)) => txHash - } - } - - def call(data: Array[Byte], sender: Address, opt: TransactionOpt): Future[Array[Byte]] = { - if (contractAddress.isEmpty) throw new RuntimeException("contract address is empty when call contract method") - val callData = Transaction(sender, contractAddress, data, opt) - client.call(callData).map { - case Left(responseError) => throw new RuntimeException(s"call contract failed, $responseError") - case Right(None) => Array.empty[Byte] - case Right(Some(value)) => value - } - } - - // data include all constructor arguments - def deploy(data: Array[Byte], sender: Address, opt: TransactionOpt): Unit = { - contractCreator = Some(sender) - val transaction = Transaction(sender, None, data, opt) - client.sendTransaction(transaction) onComplete { - case Success(response) => response match { - case Right(Some(txHash)) => afterDeploy(txHash) - case Left(responseError) => throw new RuntimeException(s"deploy contract failed: $responseError") - case Right(None) => throw new RuntimeException("deploy contract failed, no tx hash return") - } - case Failure(exception) => throw new RuntimeException(s"deploy contract failed: $exception") - } - } - - private def afterDeploy(txHash: Hash): Unit = - client.transactionReceipt(txHash) onComplete { - case Success(response) => response match { - case Left(responseError) => throw new RuntimeException(s"deploy contract failed: $responseError") - case Right(None) => system.scheduler.scheduleOnce(2 seconds)(afterDeploy(txHash)) - case Right(Some(receipt)) => - assert(receipt.contractAddress.isDefined) - // call `get` explicitly - contractAddress = Some(Address(receipt.contractAddress.get)) - } - case Failure(exception) => throw exception - } - - def subscribeLogs(logQuery: LogQuery) = client.subscribeLogs(logQuery) -} - -object Contract { - def apply(endpoint: String) = new Contract(endpoint) -} - -final case class EventValue(indexedValues: Seq[SolType], nonIndexedValues: Seq[SolType]) { - override def toString: String = { - s""" - |{ - | indexedValues: ${indexedValues.mkString("[", ", ", "]")}, - | nonIndexedValues: ${nonIndexedValues.mkString("[", ", ", "]")} - |} - """.stripMargin - } -} - -object EventValue { - def decodeEvent(typeInfos: Seq[TypeInfo[_ <: SolType]], log: Log): EventValue = { - val topics = log.topics.slice(1, log.topics.length).map(Hex.hex2Bytes) - val data = Hex.hex2Bytes(log.data) - val indexedValues = topics.zip(typeInfos).map { - case (bytes, typeInfo) => - if (typeInfo.isStatic) typeInfo.decode(bytes, 0)._1 - else Bytes32(bytes) - } - val nonIndexedTypeInfo = typeInfos.slice(topics.length, typeInfos.length).headOption - val nonIndexedValues = nonIndexedTypeInfo.map(_.decode(data, 0)._1.asInstanceOf[TupleType].toSeq) - EventValue(indexedValues, nonIndexedValues.getOrElse(Seq.empty)) - } -} diff --git a/src/main/scala/ethabi/protocol/Notifier.scala b/src/main/scala/ethabi/protocol/Notifier.scala deleted file mode 100644 index 987a89b..0000000 --- a/src/main/scala/ethabi/protocol/Notifier.scala +++ /dev/null @@ -1,61 +0,0 @@ -package ethabi.protocol - -import akka.actor.{ActorRef, Status} -import akka.stream.{Attributes, Outlet, SourceShape} -import akka.stream.stage.{GraphStage, GraphStageLogic, OutHandler} -import io.circe.Decoder -import ethabi.protocol.Subscription.{Notification, SubscriptionId, UpstreamStopped} -import scala.collection.mutable - -final class Notifier[T : Decoder](coordinator: ActorRef, request: Request) extends GraphStage[SourceShape[T]] { - import Notifier._ - private val outlet = Outlet[T]("notifier.out") - private val queue = mutable.Queue.empty[T] - override val shape = SourceShape(outlet) - - override def createLogic(inheritedAttributes: Attributes): GraphStageLogic = new GraphStageLogic(shape) { - private var subscriptionId: Option[SubscriptionId] = None - implicit def self = stageActor.ref - - setHandler(outlet, new OutHandler { - override def onPull(): Unit = { - if (queue.nonEmpty) { - val element = queue.dequeue() - push(outlet, element) - } - } - }) - - override def preStart(): Unit = { - val target = getStageActor(handler).ref - coordinator ! StartSubscribe(target, request) - } - - override def postStop(): Unit = { - subscriptionId.foreach(coordinator ! Unsubscribe(_)) - } - - private def handler(receive: (ActorRef, Any)): Unit = { - receive match { - case (_, SubscribeSucceed(id)) => subscriptionId = Some(id) - case (_, notification: Notification) => - val element = notification.as[T] - if (isAvailable(outlet) && queue.isEmpty) push(outlet, element) - else queue.enqueue(element.asInstanceOf[T]) - case (_, UpstreamStopped) => - if (queue.nonEmpty) emitMultiple(outlet, queue.toList, () => completeStage()) - else completeStage() - case (_, failure: Status.Failure) => - if (queue.nonEmpty) emitMultiple(outlet, queue.toList, () => fail(outlet, failure.cause)) - else fail(outlet, failure.cause) - case _ => // log - } - } - } -} - -object Notifier { - final case class StartSubscribe(target: ActorRef, request: Request) - final case class SubscribeSucceed(subscriptionId: SubscriptionId) - final case class Unsubscribe(subscriptionId: SubscriptionId) -} diff --git a/src/main/scala/ethabi/protocol/Request.scala b/src/main/scala/ethabi/protocol/Request.scala deleted file mode 100644 index 3540f06..0000000 --- a/src/main/scala/ethabi/protocol/Request.scala +++ /dev/null @@ -1,214 +0,0 @@ -package ethabi.protocol - -import io.circe.Json -import io.circe.syntax._ -import ethabi.util.{Hex, Hash} -import ethabi.types.Address -import scala.collection.mutable -import java.util.concurrent.atomic.AtomicLong - -final case class Request(jsonrpc: String, id: Long, params: Seq[Json], method: String) { - def withId(id: Long): Request = copy(id = id) -} - -object Request { - private val jsonrpcVersion = "2.0" - private val nextId: AtomicLong = new AtomicLong(1) - - def apply(method: String, params: Seq[Json] = Seq.empty[Json]): Request = { - Request(jsonrpcVersion, nextId.getAndIncrement(), params, method) - } - - sealed trait BlockTag - case object Latest extends BlockTag { - override def toString = "latest" - } - case object Earliest extends BlockTag { - override def toString = "earliest" - } - case object Pending extends BlockTag { - override def toString = "pending" - } - final case class BlockNumber(height: Long) extends BlockTag { - override def toString = Hex.long2Hex(height, withPrefix = true) - } - - final case class TransactionOpt(gas: Option[BigInt], gasPrice: Option[BigInt], value: Option[BigInt], nonce: Option[Long]) { - def withGas(gas: BigInt): TransactionOpt = copy(gas = Some(gas)) - def withGasPrice(gasPrice: BigInt): TransactionOpt = copy(gasPrice = Some(gasPrice)) - def withValue(value: BigInt): TransactionOpt = copy(value = Some(value)) - def withNonce(nonce: Long): TransactionOpt = copy(nonce = Some(nonce)) - } - - final case class Transaction(from: Address, to: Option[Address], data: Array[Byte], opt: TransactionOpt) { - def toJson: Json = { - val json = mutable.Map.empty[String, String] - json("from") = from.toString - if (to.isDefined) json("to") = to.get.toString - if (!data.isEmpty) json("data") = Hex.bytes2Hex(data, withPrefix = true) - if (opt.gas.isDefined) json("gas") = Hex.bigInt2Hex(opt.gas.get, withPrefix = true) - if (opt.gasPrice.isDefined) json("gasPrice") = Hex.bigInt2Hex(opt.gasPrice.get, withPrefix = true) - if (opt.value.isDefined) json("value") = Hex.bigInt2Hex(opt.value.get, withPrefix = true) - if (opt.nonce.isDefined) json("nonce") = Hex.bigInt2Hex(opt.nonce.get, withPrefix = true) - json.asJson - } - override def toString = toJson.spaces2 - } - - final case class LogFilter(fromBlock: Option[BlockTag], toBlock: Option[BlockTag], addresses: Seq[Address], topics: Option[Array[Array[Hash]]]) { - def toJson: Json = { - val json = mutable.Map.empty[String, Json] - if (fromBlock.isDefined) json("fromBlock") = Json.fromString(fromBlock.get.toString) - if (toBlock.isDefined) json("toBlock") = Json.fromString(toBlock.get.toString) - json("address") = Json.fromValues(addresses.map(addr => Json.fromString(addr.toString))) - if (topics.isDefined) json("topics") = Json.fromValues(topics.get.map(arr => Json.fromValues(arr.map(hash => Json.fromString(hash.toString))))) - json.asJson - } - override def toString = toJson.spaces2 - } - - final case class LogQuery(fromBlock: Option[BlockTag], toBlock: Option[BlockTag], addresses: Seq[Address], topics: Option[Array[Array[Hash]]], blockHash: Option[Hash]) { - def toJson: Json = { - val json = mutable.Map.empty[String, Json] - if (fromBlock.isDefined) json("fromBlock") = Json.fromString(fromBlock.get.toString) - if (toBlock.isDefined) json("toBlock") = Json.fromString(toBlock.get.toString) - json("address") = Json.fromValues(addresses.map(addr => Json.fromString(addr.toString))) - if (topics.isDefined) json("topics") = Json.fromValues(topics.get.map(arr => Json.fromValues(arr.map(hash => Json.fromString(hash.toString))))) - if (blockHash.isDefined) json("blockHash") = Json.fromString(blockHash.get.toString) - json.asJson - } - override def toString = toJson.spaces2 - } - - object LogQuery { - def from(address: Address, topic: Hash): LogQuery = - LogQuery(None, None, Seq(address), Some(Array(Array(topic))), None) - } - - def clientVersion(): Request = Request(method = "web3_clientVersion") - def sha3(data: Array[Byte]): Request = { - Request(method = "web3_sha3", params = Seq(Json.fromString(Hex.bytes2Hex(data, withPrefix = true)))) - } - def netVersion(): Request = Request(method = "net_version") - def netListening(): Request = Request(method = "net_listening") - def netPeerCount(): Request = Request(method = "net_peerCount") - def protocolVersion(): Request = Request(method = "eth_protocolVersion") - def syncing(): Request = Request(method = "eth_syncing") - def coinbase(): Request = Request(method = "eth_coinbase") - def mining(): Request = Request(method = "eth_mining") - def hashRate(): Request = Request(method = "eth_hashrate") - def gasPrice(): Request = Request(method = "eth_gasPrice") - def accounts(): Request = Request(method = "eth_accounts") - def blockNumber(): Request = Request(method = "eth_blockNumber") - def balance(address: Address, blockTag: BlockTag): Request = { - Request(method = "eth_getBalance", params = Seq(address.toString, blockTag.toString).map(Json.fromString)) - } - def storageAt(address: Address, position: Int, blockTag: BlockTag): Request = { - val positionHex = Hex.int2Hex(position, withPrefix = true) - Request(method = "eth_getStorageAt", params = Seq(address.toString, positionHex, blockTag.toString).map(Json.fromString)) - } - def transactionCount(address: Address, blockTag: BlockTag): Request = { - Request(method = "eth_getTransactionCount", params = Seq(address.toString, blockTag.toString).map(Json.fromString)) - } - def blockTransactionCountByHash(blockHash: String): Request = { - Request(method = "eth_getBlockTransactionCountByHash", params = Seq(Json.fromString(blockHash))) - } - def blockTransactionCountByNumber(blockTag: BlockTag = Latest): Request = { - Request(method = "eth_getBlockTransactionCountByNumber", params = Seq(Json.fromString(blockTag.toString))) - } - def blockTransactionCountByNumber(height: Long): Request = { - blockTransactionCountByNumber(BlockNumber(height)) - } - def uncleCountByHash(blockHash: String): Request = { - Request(method = "eth_getUncleCountByBlockHash", params = Seq(Json.fromString(blockHash))) - } - def uncleCountByNumber(blockTag: BlockTag = Latest): Request = { - Request(method = "eth_getUncleCountByBlockNumber", params = Seq(Json.fromString(blockTag.toString))) - } - def uncleCountByNumber(height: Long): Request = { - uncleCountByNumber(BlockNumber(height)) - } - def code(address: Address, blockTag: BlockTag = Latest): Request = { - Request(method = "eth_getCode", params = Seq(address.toString, blockTag.toString).map(Json.fromString)) - } - def code(address: Address, height: Long): Request = { - code(address, BlockNumber(height)) - } - def sign(address: Address, data: String): Request = { - Request(method = "eth_sign", params = Seq(address.toString, data).map(Json.fromString)) - } - def sing(address: Address, data: Array[Byte]): Request = { - val dataHex = Hex.bytes2Hex(data, withPrefix = true) - Request(method = "eth_sign", params = Seq(address.toString, dataHex).map(Json.fromString)) - } - def sendTransaction(transaction: Transaction): Request = { - Request(method = "eth_sendTransaction", params = Seq(transaction.toJson)) - } - def sendRawTransaction(rawTx: Array[Byte]): Request = { - val txHex = Hex.bytes2Hex(rawTx, withPrefix = true) - Request(method = "eth_sendRawTransaction", params = Seq(Json.fromString(txHex))) - } - def call(callData: Transaction, blockTag: BlockTag): Request = - Request(method = "eth_call", params = Seq(callData.toJson, Json.fromString(blockTag.toString))) - def estimateGas(callData: Transaction, blockTag: BlockTag = Latest): Request = - Request(method = "eth_estimateGas", params = Seq(callData.toJson, Json.fromString(blockTag.toString))) - def estimateGas(callData: Transaction, height: Long): Request = estimateGas(callData, BlockNumber(height)) - def blockByHash(hash: Hash, detail: Boolean = false): Request = - Request(method = "eth_getBlockByHash", params = Seq(Json.fromString(hash.toString), Json.fromBoolean(detail))) - def blockByNumber(blockTag: BlockTag = Latest, detail: Boolean = false): Request = { - Request(method = "eth_getBlockByNumber", params = Seq(Json.fromString(blockTag.toString), Json.fromBoolean(detail))) - } - def transactionByHash(hash: String): Request = Request(method = "eth_getTransactionByHash", params = Seq(Json.fromString(hash))) - def transactionByHash(hash: Hash): Request = transactionByHash(hash.toString) - def transactionByBlockHashAndIndex(hash: String, index: Int): Request = { - val indexHex = Hex.int2Hex(index, withPrefix = true) - Request(method = "eth_getTransactionByBlockHashAndIndex", params = Seq(hash, indexHex).map(Json.fromString)) - } - def transactionByBlockHashAndIndex(hash: Hash, index: Int): Request = transactionByBlockHashAndIndex(hash.toString, index) - def transactionByBlockNumberAndIndex(blockTag: BlockTag = Latest, index: Int): Request = { - val indexHex = Hex.int2Hex(index, withPrefix = true) - Request(method = "eth_getTransactionByBlockNumberAndIndex", params = Seq(blockTag.toString, indexHex).map(Json.fromString)) - } - def transactionByBlockNumberAndIndex(height: Long, index: Int): Request = transactionByBlockNumberAndIndex(BlockNumber(height), index) - def transactionReceipt(hash: String): Request = Request(method = "eth_getTransactionReceipt", params = Seq(Json.fromString(hash))) - def transactionReceipt(hash: Hash): Request = transactionReceipt(hash.toString) - def uncleByBlockHashAndIndex(hash: String, index: Int): Request = { - val indexHex = Hex.int2Hex(index, withPrefix = true) - Request(method = "eth_getUncleByBlockHashAndIndex", params = Seq(hash, indexHex).map(Json.fromString)) - } - def uncleByBlockHashAndIndex(hash: Hash, index: Int): Request = uncleByBlockHashAndIndex(hash.toString, index) - def uncleByBlockNumberAndIndex(blockTag: BlockTag = Latest, index: Int): Request = { - val indexHex = Hex.int2Hex(index, withPrefix = true) - Request(method = "eth_getUncleByBlockNumberAndIndex", params = Seq(blockTag.toString, indexHex).map(Json.fromString)) - } - def uncleByBlockNumberAndIndex(height: Long, index: Int): Request = { - uncleByBlockNumberAndIndex(BlockNumber(height), index) - } - def newFilter(logFilter: LogFilter): Request = Request(method = "eth_newFilter", params = Seq(logFilter.toJson)) - def newBlockFilter(): Request = Request(method = "eth_newBlockFilter") - def newPendingTransactionFilter(): Request = Request(method = "eth_newPendingTransactionFilter") - def uninstallFilter(filterId: Int): Request = { - val filterIdHex = Hex.int2Hex(filterId, withPrefix = true) - Request(method = "eth_uninstallFilter", params = Seq(Json.fromString(filterIdHex))) - } - def filterChanges(filterId: Int): Request = { - val filterIdHex = Hex.int2Hex(filterId, withPrefix = true) - Request(method = "eth_getFilterChanges", params = Seq(Json.fromString(filterIdHex))) - } - def filterLogs(filterId: Int): Request = { - val filterIdHex = Hex.int2Hex(filterId, withPrefix = true) - Request(method = "eth_getFilterLogs", params = Seq(Json.fromString(filterIdHex))) - } - def logs(logQuery: LogQuery): Request = Request(method = "eth_getLogs", params = Seq(logQuery.toJson)) - // TODO: more rpc api - - def subscribeNewHeader(includeTransactions: Boolean = false): Request = - Request(method = "eth_subscribe", params = Seq(Json.fromString("newHeads"), Map("includeTransactions" -> includeTransactions).asJson)) - def subscribeLogs(logQuery: LogQuery): Request = - Request(method = "eth_subscribe", params = Seq(Json.fromString("logs"), logQuery.toJson)) - def subscribeNewPendingTransactions(): Request = - Request(method = "eth_subscribe", params = Seq(Json.fromString("newPendingTransactions"))) - def subscribeSyncStatus(): Request = Request(method = "eth_subscribe", params = Seq(Json.fromString("syncing"))) - def unsubscribe(subscriptionId: String): Request = - Request(method = "eth_unsubscribe", params = Seq(Json.fromString(subscriptionId))) -} diff --git a/src/main/scala/ethabi/protocol/Response.scala b/src/main/scala/ethabi/protocol/Response.scala deleted file mode 100644 index b6fe20f..0000000 --- a/src/main/scala/ethabi/protocol/Response.scala +++ /dev/null @@ -1,42 +0,0 @@ -package ethabi.protocol - -import io.circe.{Json, Decoder} - -final case class Response(jsonrpc: String, id: Int, result: Json, error: Option[ResponseError]) { - private val response: Either[ResponseError, Json] = if (error.isDefined) Left(error.get) else Right(result) - def as[T : Decoder]: Either[ResponseError, Option[T]] = response.map { json => - if (json.isNull) None - else json.as[T] match { - case Left(decodingFailure) => throw decodingFailure - case Right(value) => Some(value) - } - } - - // auto generated Decoder[Either[A, B]] doesn't work, construct with `either` op manually - def decodeWith[T](decoder: Decoder[T]): Either[ResponseError, Option[T]] = response.map { json => - if (json.isNull) None - else decoder.decodeJson(json) match { - case Left(decodingFailure) => throw decodingFailure - case Right(value) => Some(value) - } - } -} - -final case class ResponseError(code: Int, message: String, data: Option[Json]) - -object Response { - final case class Syncing(startingBlock: Long, currentBlock: Long, highestBlock: Long) - final case class Header(parentHash: String, sha3Uncles: String, miner: String, stateRoot: String, transactionsRoot: String, - receiptsRoot: String, logsBloom: String, difficulty: String, number: String, gasLimit: String, gasUsed: String, - timestamp: String, extraData: String, mixHash: String, nonce: String, hash:String) - final case class Log(address: String, topics: Seq[String], data: String, blockNumber: String, transactionHash: String, - transactionIndex: String, blockHash: String, logIndex: String, removed: Boolean) - final case class SyncProgress(startingBlock: Long, currentBlock: Long, highestBlock: Long, pulledStates: Long, knownStates: Long) - final case class SyncStatus(syncing: Boolean, status: Option[SyncProgress]) - final case class TransactionReceipt(transactionHash: String, transactionIndex: String, blockHash: String, blockNumber: String, - cumulativeGasUsed: String, gasUsed: String, contractAddress: Option[String], root: Option[String], - status: Option[String], from: String, to: Option[String], logs: Seq[Log], logsBloom: String) - final case class Transaction(blockHash: Option[String], blockNumber: Option[String], from: String, gas: String, gasPrice: String, - hash: String, input: String, nonce: String, to: String, transactionIndex: String, value: String, - v: String, r: String, s: String) -} diff --git a/src/main/scala/ethabi/protocol/Service.scala b/src/main/scala/ethabi/protocol/Service.scala deleted file mode 100644 index d1af5e0..0000000 --- a/src/main/scala/ethabi/protocol/Service.scala +++ /dev/null @@ -1,53 +0,0 @@ -package ethabi.protocol - -import io.circe.Decoder -import io.circe.generic.auto._ -import ethabi.protocol.Request.{Transaction => ReqTransaction, _} -import ethabi.protocol.Response._ - -import scala.concurrent.Future -import ethabi.types.Address -import ethabi.util.{Hash, Hex} - -private [protocol] trait Service { - import scala.concurrent.ExecutionContext.Implicits.global - import Service._ - def allowSubscribe: Boolean - def doRequest(req: Request): Future[Response] - - def clientVersion: Result[String] = doRequest(Request.clientVersion()).map(_.as[String]) - def sha3(data: Array[Byte]): Result[Hash] = doRequest(Request.sha3(data)).map(_.as[String].map(_.map(Hash.apply))) - def netVersion: Result[String] = doRequest(Request.netVersion()).map(_.as[String]) - def netListening: Result[Boolean] = doRequest(Request.netListening()).map(_.as[Boolean]) - def peerCount: Result[Int] = doRequest(Request.netPeerCount()).map(_.as[String].map(_.map(Hex.hex2Int))) - def protocolVersion: Result[String] = doRequest(Request.protocolVersion()).map(_.as[String]) - def syncing: Result[Either[Boolean, Response.Syncing]] = doRequest(Request.syncing()).map(_.decodeWith(Decoder[Boolean].either(Decoder[Response.Syncing]))) - def coinbase: Result[Address] = doRequest(Request.coinbase()).map(_.as[String].map(_.map(Address.apply))) - def mining: Result[Boolean] = doRequest(Request.mining()).map(_.as[Boolean]) - def hashRate: Result[Long] = doRequest(Request.hashRate()).map(_.as[String].map(_.map(Hex.hex2Long))) - def gasPrice: Result[BigInt] = doRequest(Request.gasPrice()).map(_.as[String].map(_.map(Hex.hex2BigInt))) - def accounts: Result[Seq[Address]] = doRequest(Request.accounts()).map(_.as[Seq[String]].map(_.map(_.map(Address.apply)))) - def blockNumber: Result[Long] = doRequest(Request.blockNumber()).map(_.as[String].map(_.map(Hex.hex2Long))) - def balance(address: Address, blockTag: BlockTag = Latest): Result[BigInt] = - doRequest(Request.balance(address, blockTag)).map(_.as[String].map(_.map(Hex.hex2BigInt))) - def balance(address: Address, height: Long): Result[BigInt] = balance(address, BlockNumber(height)) - def storageAt(address: Address, position: Int, blockTag: BlockTag = Latest): Result[Array[Byte]] = - doRequest(Request.storageAt(address, position, blockTag)).map(_.as[String].map(_.map(Hex.hex2Bytes))) - def storageAt(address: Address, position: Int, height: Long): Result[Array[Byte]] = storageAt(address, position, BlockNumber(height)) - def transactionCount(address: Address, blockTag: BlockTag = Latest): Result[Int] = - doRequest(Request.transactionCount(address, blockTag)).map(_.as[String].map(_.map(Hex.hex2Int))) - def transactionCount(address: Address, height: Long): Result[Int] = transactionCount(address, BlockNumber(height)) - def blockTransactionCountByHash(blockHash: Hash): Result[Int] = - doRequest(Request.blockTransactionCountByHash(blockHash.toString)).map(_.as[String].map(_.map(Hex.hex2Int))) - - def sendTransaction(transaction: ReqTransaction): Result[Hash] = doRequest(Request.sendTransaction(transaction)).map(_.as[String].map(_.map(Hash.apply))) - def transactionReceipt(txHash: Hash): Result[TransactionReceipt] = doRequest(Request.transactionReceipt(txHash)).map(_.as[TransactionReceipt]) - def call(callData: ReqTransaction, blockTag: BlockTag = Latest): Result[Array[Byte]] = - doRequest(Request.call(callData, blockTag)).map(_.as[String].map(_.map(Hex.hex2Bytes))) - def call(callData: ReqTransaction, height: Long): Result[Array[Byte]] = call(callData, BlockNumber(height)) - def logs(logQuery: LogQuery): Result[Seq[Log]] = doRequest(Request.logs(logQuery)).map(_.as[Seq[Log]]) -} - -object Service { - type Result[T] = Future[Either[ResponseError, Option[T]]] -} \ No newline at end of file diff --git a/src/main/scala/ethabi/protocol/Subscription.scala b/src/main/scala/ethabi/protocol/Subscription.scala deleted file mode 100644 index 10e860b..0000000 --- a/src/main/scala/ethabi/protocol/Subscription.scala +++ /dev/null @@ -1,30 +0,0 @@ -package ethabi.protocol - -import akka.NotUsed -import akka.stream.scaladsl.Source -import io.circe.{Decoder, Json} -import ethabi.protocol.Request.LogQuery -import ethabi.protocol.Response._ - -private [protocol] trait Subscription { self: Service => - def subscribeNewHeaders(includeTransactions: Boolean = false): Source[Header, NotUsed] - def subscribeLogs(logQuery: LogQuery): Source[Log, NotUsed] - def subscribeNewPendingTransaction(): Source[String, NotUsed] - def subscribeSyncStatus(): Source[SyncStatus, NotUsed] -} - -object Subscription { - type SubscriptionId = String - private [protocol] case object UpstreamStopped - - private [protocol] case class NotificationParam(result: Json, subscription: String) - private [protocol] case class Notification(jsonrpc: String, method: String, params: NotificationParam) { - def as[T : Decoder]: T = { - val decoder = implicitly[Decoder[T]] - decoder.decodeJson(params.result) match { - case Left(decodingFailure) => throw decodingFailure - case Right(value) => value - } - } - } -} diff --git a/src/main/scala/ethabi/protocol/http/Client.scala b/src/main/scala/ethabi/protocol/http/Client.scala deleted file mode 100644 index f978973..0000000 --- a/src/main/scala/ethabi/protocol/http/Client.scala +++ /dev/null @@ -1,31 +0,0 @@ -package ethabi.protocol.http - -import akka.actor.ActorSystem -import akka.http.scaladsl._ -import akka.http.scaladsl.model._ -import akka.stream.Materializer -import akka.util.ByteString -import io.circe.jawn.decode -import io.circe.generic.auto._ -import io.circe.syntax._ -import ethabi.protocol.{Request, Response, Service} -import scala.concurrent.Future - -final class Client(url: String)(implicit system: ActorSystem, materializer: Materializer) extends Service { - import system.dispatcher - - override def allowSubscribe: Boolean = false - override def doRequest(req: Request): Future[Response] = { - val entity = HttpEntity(ContentTypes.`application/json`, req.asJson.toString) - val resp = Http().singleRequest(HttpRequest(method = HttpMethods.POST, uri = url, entity = entity)) - resp.flatMap { response => - response.entity.dataBytes.runFold(ByteString(""))(_ ++ _).map { raw => - decode[Response](raw.utf8String).getOrElse(throw new NoSuchElementException("decode response error")) - } - } - } -} - -object Client { - def apply(url: String)(implicit system: ActorSystem, materializer: Materializer) = new Client(url) -} diff --git a/src/main/scala/ethabi/protocol/ws/Client.scala b/src/main/scala/ethabi/protocol/ws/Client.scala deleted file mode 100644 index 15f9280..0000000 --- a/src/main/scala/ethabi/protocol/ws/Client.scala +++ /dev/null @@ -1,152 +0,0 @@ -package ethabi.protocol.ws - -import akka.NotUsed -import akka.actor.{Actor, ActorRef, ActorSystem, PoisonPill, Props, Status} -import akka.stream.{Materializer, OverflowStrategy} -import akka.stream.scaladsl.{Keep, Sink, Source} -import akka.http.scaladsl.model.StatusCodes -import akka.http.scaladsl.model.ws.{Message, TextMessage, WebSocketRequest} -import akka.http.scaladsl.Http -import io.circe.syntax._ -import io.circe.generic.auto._ -import io.circe.{Decoder, jawn} -import ethabi.protocol.Notifier.{StartSubscribe, SubscribeSucceed, Unsubscribe} -import ethabi.protocol.Subscription._ -import ethabi.protocol.Response._ -import ethabi.protocol._ -import ethabi.protocol.ws.Client.NewRequest -import scala.concurrent.{Future, Promise} -import scala.collection.mutable -import scala.util.{Failure, Success} - -final class Client(url: String)(implicit system: ActorSystem, materializer: Materializer) extends Service with Subscription { - import system.dispatcher - - private val listener = system.actorOf(Props(new Listener)) - // TODO: configurable - private val (requestReceiver, upgradeResponse) = - Source.actorRef[Message](PartialFunction.empty, PartialFunction.empty, 1024, OverflowStrategy.dropTail) - .viaMat(Http().webSocketClientFlow(WebSocketRequest(url)))(Keep.both) - .to(Sink.actorRef(listener, UpstreamStopped, exp => throw exp)) - .run() - - upgradeResponse onComplete { - case Success(upgrade) => - if (upgrade.response.status != StatusCodes.SwitchingProtocols) - throw new RuntimeException(s"connect failed: ${upgrade.response.status}") - case Failure(exception) => throw new RuntimeException(s"connect failed: $exception") - } - - // default supervisor strategy is ok - final class Listener extends Actor { - private val requests = mutable.Map.empty[Long, Promise[Response]] - private val subscribers = mutable.Map.empty[SubscriptionId, ActorRef] - - override def receive: Receive = { - case message: TextMessage.Strict => onMessage(message) - case message: TextMessage.Streamed => - message - .textStream - .runFold(new mutable.StringBuilder())((b, e) => b.append(e)) - .map(b => TextMessage.Strict.apply(b.toString)) - .onComplete { - case Success(value) => onMessage(value) - case Failure(exp) => throw exp - } - case UpstreamStopped => - subscribers.values.foreach(_ ! UpstreamStopped) - self ! PoisonPill - case failure: Status.Failure => - subscribers.values.foreach(_ ! failure) - self ! PoisonPill - case StartSubscribe(target, request) => onSubscribe(target, request) - case NewRequest(request, promise) => onNewRequest(request, promise) - case Unsubscribe(id) => onUnsubscribe(id) - case msg => throw new RuntimeException(s"unknown message $msg") - } - - private def onNewRequest(request: Request, promise: Promise[Response]): Unit = { - requests(request.id) = promise - val message = TextMessage(request.asJson.toString) - requestReceiver ! message - } - - private def onMessage(message: TextMessage.Strict): Unit = { - val json = jawn.parse(message.text) match { - case Left(parsingFailure) => throw parsingFailure - case Right(result) => result - } - Decoder[Response].either(Decoder[Notification]).decodeJson(json) match { - case Left(decodingFailure) => throw decodingFailure - case Right(result) => result match { - case Left(response) => onResponse(response) - case Right(notification) => onNotification(notification) - } - } - } - - private def onSubscribe(target: ActorRef, request: Request): Unit = { - val promise = Promise[Response] - self ! NewRequest(request, promise) - promise.future onComplete { - case Success(response) => response.as[String] match { - case Right(Some(id)) => subscribers(id) = target; target ! SubscribeSucceed(id) - case _ => throw new RuntimeException(s"subscribe failed") - } - case Failure(exception) => throw exception - } - } - - private def onUnsubscribe(id: String): Unit = { - subscribers.remove(id) - val promise = Promise[Response] - self ! NewRequest(Request.unsubscribe(id), promise) - promise.future onComplete { - case Success(_) => - case Failure(_) => onUnsubscribe(id) // try again when failed - } - } - - private def onResponse(response: Response): Unit = { - requests(response.id).trySuccess(response) - requests.remove(response.id) - } - - private def onNotification(notification: Notification): Unit = { - subscribers.get(notification.params.subscription).foreach(_ ! notification) - } - } - - override def allowSubscribe: Boolean = true - override def doRequest(req: Request): Future[Response] = { - val promise = Promise[Response] - listener ! NewRequest(req, promise) - promise.future - } - - override def subscribeNewHeaders(includeTransactions: Boolean = false): Source[Header, NotUsed] = { - val request = Request.subscribeNewHeader(includeTransactions) - Source.fromGraph(new Notifier[Header](listener, request)) - } - - override def subscribeLogs(logQuery: Request.LogQuery): Source[Log, NotUsed] = { - val request = Request.subscribeLogs(logQuery) - Source.fromGraph(new Notifier[Log](listener, request)) - } - - override def subscribeNewPendingTransaction(): Source[String, NotUsed] = { - val request = Request.subscribeNewPendingTransactions() - Source.fromGraph(new Notifier[String](listener, request)) - } - - override def subscribeSyncStatus(): Source[SyncStatus, NotUsed] = { - val request = Request.subscribeSyncStatus() - Source.fromGraph(new Notifier[SyncStatus](listener, request)) - } -} - -object Client { - private case class NewRequest(request: Request, promise: Promise[Response]) - - def apply(url: String)(implicit system: ActorSystem, materialzier: Materializer) = new Client(url) -} diff --git a/src/test/scala/ethabi/types/IntTypeSpec.scala b/src/test/scala/ethabi/types/IntTypeSpec.scala deleted file mode 100644 index adf6f6c..0000000 --- a/src/test/scala/ethabi/types/IntTypeSpec.scala +++ /dev/null @@ -1,30 +0,0 @@ -package ethabi.types - -import org.scalatest.{WordSpec, Matchers} -import ethabi.types.generated._ - -class IntTypeSpec extends WordSpec with Matchers { - "test int8 encode" in { - // fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff4 - Int8.typeInfo.encode(Int8(BigInt(-12))).map("%02x" format _).mkString shouldBe "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff4" - } - - "test int8 decode" in { - val encoded = "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff4".sliding(2, 2).toArray.map(Integer.parseInt(_, 16).toByte) - val (result, consumed) = Int8.typeInfo.decode(encoded, 0) - result.value.toInt shouldBe -12 - consumed shouldBe 32 - } - - "test int16 encode" in { - // 0000000000000000000000000000000000000000000000000000000000002710 - Int16.typeInfo.encode(Int16(BigInt(10000))).map("%02x" format _).mkString shouldBe "0000000000000000000000000000000000000000000000000000000000002710" - } - - "test int16 decode" in { - val encoded = "0000000000000000000000000000000000000000000000000000000000002710".sliding(2, 2).toArray.map(Integer.parseInt(_, 16).toByte) - val (result, consumed) = Int16.typeInfo.decode(encoded, 0) - result.value.toInt shouldBe 10000 - consumed shouldBe 32 - } -} diff --git a/src/test/scala/ethabi/types/UintTypeSpec.scala b/src/test/scala/ethabi/types/UintTypeSpec.scala deleted file mode 100644 index a949939..0000000 --- a/src/test/scala/ethabi/types/UintTypeSpec.scala +++ /dev/null @@ -1,30 +0,0 @@ -package ethabi.types - -import org.scalatest.{Matchers, WordSpec} -import ethabi.types.generated._ - -class UintTypeSpec extends WordSpec with Matchers { - // 000000000000000000000000000000000000000000000000000000000000000c - "test uint8 encode" in { - Uint8.typeInfo.encode(Uint8(BigInt(12))).map("%02x" format _).mkString shouldBe "000000000000000000000000000000000000000000000000000000000000000c" - } - - "test uint8 decode" in { - val encoded = "000000000000000000000000000000000000000000000000000000000000000c".sliding(2, 2).toArray.map(Integer.parseInt(_, 16).toByte) - val (result, consumed) = Uint8.typeInfo.decode(encoded, 0) - result.value.toInt shouldBe 12 - consumed shouldBe 32 - } - - "test uint16 encode" in { - // 0000000000000000000000000000000000000000000000000000000000002710 - Uint16.typeInfo.encode(Uint16(BigInt(10000))).map("%02x" format _).mkString shouldBe "0000000000000000000000000000000000000000000000000000000000002710" - } - - "test uint16 decode" in { - val encoded = "0000000000000000000000000000000000000000000000000000000000002710".sliding(2, 2).toArray.map(Integer.parseInt(_, 16).toByte) - val (result, consumed) = Uint16.typeInfo.decode(encoded, 0) - result.value.toInt shouldBe 10000 - consumed shouldBe 32 - } -} diff --git a/src/test/scala/ethabi/util/HexSpec.scala b/src/test/scala/ethabi/util/HexSpec.scala deleted file mode 100644 index eca0e07..0000000 --- a/src/test/scala/ethabi/util/HexSpec.scala +++ /dev/null @@ -1,10 +0,0 @@ -package ethabi.util - -import org.scalatest.{WordSpec, Matchers} - -class HexSpec extends WordSpec with Matchers { - "test empty hex to bytes" in { - val bytes = Hex.hex2Bytes("") - bytes.length shouldBe 0 - } -}