[
  {
    "path": ".github/workflows/tests.yaml",
    "content": "on:\n  # Trigger the workflow on push or pull request,\n  # but only for the main branch\n  push:\n    branches:\n      - master\n  pull_request:\n    branches:\n      - master\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n    steps:\n\n      - name: Checkout repository and submodules\n        uses: actions/checkout@v3\n        with:\n          submodules: recursive\n\n      - name: setup python\n        uses: actions/setup-python@v4\n        with:\n          python-version: '3.8'\n\n      - name: install python packages\n        run: |\n          python -m pip install --upgrade pip\n          pip install virtualenv --upgrade\n          pip install -r requirements.txt\n          pip install -r requirements-dev.txt\n      - name: execute tests\n        run: ./test.sh\n"
  },
  {
    "path": ".gitignore",
    "content": ".idea\n*.iml\n\n__pycache__\n.cache\n.coverage\n.pytest_cache\n\n.DS_Store\n\nlogs/*.json.log\ntests/config/keys/UnlimitedChain/address_book.json\n\ndocs/_doc\n_virtualenv\n"
  },
  {
    "path": ".python-version",
    "content": "3.6.6\n"
  },
  {
    "path": "COPYING",
    "content": "                    GNU AFFERO GENERAL PUBLIC LICENSE\n                       Version 3, 19 November 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The GNU Affero General Public License is a free, copyleft license for\nsoftware and other kinds of works, specifically designed to ensure\ncooperation with the community in the case of network server software.\n\n  The licenses for most software and other practical works are designed\nto take away your freedom to share and change the works.  By contrast,\nour General Public Licenses are intended to guarantee your freedom to\nshare and change all versions of a program--to make sure it remains free\nsoftware for all its users.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthem if you wish), that you receive source code or can get it if you\nwant it, that you can change the software or use pieces of it in new\nfree programs, and that you know you can do these things.\n\n  Developers that use our General Public Licenses protect your rights\nwith two steps: (1) assert copyright on the software, and (2) offer\nyou this License which gives you legal permission to copy, distribute\nand/or modify the software.\n\n  A secondary benefit of defending all users' freedom is that\nimprovements made in alternate versions of the program, if they\nreceive widespread use, become available for other developers to\nincorporate.  Many developers of free software are heartened and\nencouraged by the resulting cooperation.  However, in the case of\nsoftware used on network servers, this result may fail to come about.\nThe GNU General Public License permits making a modified version and\nletting the public access it on a server without ever releasing its\nsource code to the public.\n\n  The GNU Affero General Public License is designed specifically to\nensure that, in such cases, the modified source code becomes available\nto the community.  It requires the operator of a network server to\nprovide the source code of the modified version running there to the\nusers of that server.  Therefore, public use of a modified version, on\na publicly accessible server, gives the public access to the source\ncode of the modified version.\n\n  An older license, called the Affero General Public License and\npublished by Affero, was designed to accomplish similar goals.  This is\na different license, not a version of the Affero GPL, but Affero has\nreleased a new version of the Affero GPL which permits relicensing under\nthis license.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                       TERMS AND CONDITIONS\n\n  0. Definitions.\n\n  \"This License\" refers to version 3 of the GNU Affero General Public License.\n\n  \"Copyright\" also means copyright-like laws that apply to other kinds of\nworks, such as semiconductor masks.\n\n  \"The Program\" refers to any copyrightable work licensed under this\nLicense.  Each licensee is addressed as \"you\".  \"Licensees\" and\n\"recipients\" may be individuals or organizations.\n\n  To \"modify\" a work means to copy from or adapt all or part of the work\nin a fashion requiring copyright permission, other than the making of an\nexact copy.  The resulting work is called a \"modified version\" of the\nearlier work or a work \"based on\" the earlier work.\n\n  A \"covered work\" means either the unmodified Program or a work based\non the Program.\n\n  To \"propagate\" a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy.  Propagation includes copying,\ndistribution (with or without modification), making available to the\npublic, and in some countries other activities as well.\n\n  To \"convey\" a work means any kind of propagation that enables other\nparties to make or receive copies.  Mere interaction with a user through\na computer network, with no transfer of a copy, is not conveying.\n\n  An interactive user interface displays \"Appropriate Legal Notices\"\nto the extent that it includes a convenient and prominently visible\nfeature that (1) displays an appropriate copyright notice, and (2)\ntells the user that there is no warranty for the work (except to the\nextent that warranties are provided), that licensees may convey the\nwork under this License, and how to view a copy of this License.  If\nthe interface presents a list of user commands or options, such as a\nmenu, a prominent item in the list meets this criterion.\n\n  1. Source Code.\n\n  The \"source code\" for a work means the preferred form of the work\nfor making modifications to it.  \"Object code\" means any non-source\nform of a work.\n\n  A \"Standard Interface\" means an interface that either is an official\nstandard defined by a recognized standards body, or, in the case of\ninterfaces specified for a particular programming language, one that\nis widely used among developers working in that language.\n\n  The \"System Libraries\" of an executable work include anything, other\nthan the work as a whole, that (a) is included in the normal form of\npackaging a Major Component, but which is not part of that Major\nComponent, and (b) serves only to enable use of the work with that\nMajor Component, or to implement a Standard Interface for which an\nimplementation is available to the public in source code form.  A\n\"Major Component\", in this context, means a major essential component\n(kernel, window system, and so on) of the specific operating system\n(if any) on which the executable work runs, or a compiler used to\nproduce the work, or an object code interpreter used to run it.\n\n  The \"Corresponding Source\" for a work in object code form means all\nthe source code needed to generate, install, and (for an executable\nwork) run the object code and to modify the work, including scripts to\ncontrol those activities.  However, it does not include the work's\nSystem Libraries, or general-purpose tools or generally available free\nprograms which are used unmodified in performing those activities but\nwhich are not part of the work.  For example, Corresponding Source\nincludes interface definition files associated with source files for\nthe work, and the source code for shared libraries and dynamically\nlinked subprograms that the work is specifically designed to require,\nsuch as by intimate data communication or control flow between those\nsubprograms and other parts of the work.\n\n  The Corresponding Source need not include anything that users\ncan regenerate automatically from other parts of the Corresponding\nSource.\n\n  The Corresponding Source for a work in source code form is that\nsame work.\n\n  2. Basic Permissions.\n\n  All rights granted under this License are granted for the term of\ncopyright on the Program, and are irrevocable provided the stated\nconditions are met.  This License explicitly affirms your unlimited\npermission to run the unmodified Program.  The output from running a\ncovered work is covered by this License only if the output, given its\ncontent, constitutes a covered work.  This License acknowledges your\nrights of fair use or other equivalent, as provided by copyright law.\n\n  You may make, run and propagate covered works that you do not\nconvey, without conditions so long as your license otherwise remains\nin force.  You may convey covered works to others for the sole purpose\nof having them make modifications exclusively for you, or provide you\nwith facilities for running those works, provided that you comply with\nthe terms of this License in conveying all material for which you do\nnot control copyright.  Those thus making or running the covered works\nfor you must do so exclusively on your behalf, under your direction\nand control, on terms that prohibit them from making any copies of\nyour copyrighted material outside their relationship with you.\n\n  Conveying under any other circumstances is permitted solely under\nthe conditions stated below.  Sublicensing is not allowed; section 10\nmakes it unnecessary.\n\n  3. Protecting Users' Legal Rights From Anti-Circumvention Law.\n\n  No covered work shall be deemed part of an effective technological\nmeasure under any applicable law fulfilling obligations under article\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\nsimilar laws prohibiting or restricting circumvention of such\nmeasures.\n\n  When you convey a covered work, you waive any legal power to forbid\ncircumvention of technological measures to the extent such circumvention\nis effected by exercising rights under this License with respect to\nthe covered work, and you disclaim any intention to limit operation or\nmodification of the work as a means of enforcing, against the work's\nusers, your or third parties' legal rights to forbid circumvention of\ntechnological measures.\n\n  4. Conveying Verbatim Copies.\n\n  You may convey verbatim copies of the Program's source code as you\nreceive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice;\nkeep intact all notices stating that this License and any\nnon-permissive terms added in accord with section 7 apply to the code;\nkeep intact all notices of the absence of any warranty; and give all\nrecipients a copy of this License along with the Program.\n\n  You may charge any price or no price for each copy that you convey,\nand you may offer support or warranty protection for a fee.\n\n  5. Conveying Modified Source Versions.\n\n  You may convey a work based on the Program, or the modifications to\nproduce it from the Program, in the form of source code under the\nterms of section 4, provided that you also meet all of these conditions:\n\n    a) The work must carry prominent notices stating that you modified\n    it, and giving a relevant date.\n\n    b) The work must carry prominent notices stating that it is\n    released under this License and any conditions added under section\n    7.  This requirement modifies the requirement in section 4 to\n    \"keep intact all notices\".\n\n    c) You must license the entire work, as a whole, under this\n    License to anyone who comes into possession of a copy.  This\n    License will therefore apply, along with any applicable section 7\n    additional terms, to the whole of the work, and all its parts,\n    regardless of how they are packaged.  This License gives no\n    permission to license the work in any other way, but it does not\n    invalidate such permission if you have separately received it.\n\n    d) If the work has interactive user interfaces, each must display\n    Appropriate Legal Notices; however, if the Program has interactive\n    interfaces that do not display Appropriate Legal Notices, your\n    work need not make them do so.\n\n  A compilation of a covered work with other separate and independent\nworks, which are not by their nature extensions of the covered work,\nand which are not combined with it such as to form a larger program,\nin or on a volume of a storage or distribution medium, is called an\n\"aggregate\" if the compilation and its resulting copyright are not\nused to limit the access or legal rights of the compilation's users\nbeyond what the individual works permit.  Inclusion of a covered work\nin an aggregate does not cause this License to apply to the other\nparts of the aggregate.\n\n  6. Conveying Non-Source Forms.\n\n  You may convey a covered work in object code form under the terms\nof sections 4 and 5, provided that you also convey the\nmachine-readable Corresponding Source under the terms of this License,\nin one of these ways:\n\n    a) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by the\n    Corresponding Source fixed on a durable physical medium\n    customarily used for software interchange.\n\n    b) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by a\n    written offer, valid for at least three years and valid for as\n    long as you offer spare parts or customer support for that product\n    model, to give anyone who possesses the object code either (1) a\n    copy of the Corresponding Source for all the software in the\n    product that is covered by this License, on a durable physical\n    medium customarily used for software interchange, for a price no\n    more than your reasonable cost of physically performing this\n    conveying of source, or (2) access to copy the\n    Corresponding Source from a network server at no charge.\n\n    c) Convey individual copies of the object code with a copy of the\n    written offer to provide the Corresponding Source.  This\n    alternative is allowed only occasionally and noncommercially, and\n    only if you received the object code with such an offer, in accord\n    with subsection 6b.\n\n    d) Convey the object code by offering access from a designated\n    place (gratis or for a charge), and offer equivalent access to the\n    Corresponding Source in the same way through the same place at no\n    further charge.  You need not require recipients to copy the\n    Corresponding Source along with the object code.  If the place to\n    copy the object code is a network server, the Corresponding Source\n    may be on a different server (operated by you or a third party)\n    that supports equivalent copying facilities, provided you maintain\n    clear directions next to the object code saying where to find the\n    Corresponding Source.  Regardless of what server hosts the\n    Corresponding Source, you remain obligated to ensure that it is\n    available for as long as needed to satisfy these requirements.\n\n    e) Convey the object code using peer-to-peer transmission, provided\n    you inform other peers where the object code and Corresponding\n    Source of the work are being offered to the general public at no\n    charge under subsection 6d.\n\n  A separable portion of the object code, whose source code is excluded\nfrom the Corresponding Source as a System Library, need not be\nincluded in conveying the object code work.\n\n  A \"User Product\" is either (1) a \"consumer product\", which means any\ntangible personal property which is normally used for personal, family,\nor household purposes, or (2) anything designed or sold for incorporation\ninto a dwelling.  In determining whether a product is a consumer product,\ndoubtful cases shall be resolved in favor of coverage.  For a particular\nproduct received by a particular user, \"normally used\" refers to a\ntypical or common use of that class of product, regardless of the status\nof the particular user or of the way in which the particular user\nactually uses, or expects or is expected to use, the product.  A product\nis a consumer product regardless of whether the product has substantial\ncommercial, industrial or non-consumer uses, unless such uses represent\nthe only significant mode of use of the product.\n\n  \"Installation Information\" for a User Product means any methods,\nprocedures, authorization keys, or other information required to install\nand execute modified versions of a covered work in that User Product from\na modified version of its Corresponding Source.  The information must\nsuffice to ensure that the continued functioning of the modified object\ncode is in no case prevented or interfered with solely because\nmodification has been made.\n\n  If you convey an object code work under this section in, or with, or\nspecifically for use in, a User Product, and the conveying occurs as\npart of a transaction in which the right of possession and use of the\nUser Product is transferred to the recipient in perpetuity or for a\nfixed term (regardless of how the transaction is characterized), the\nCorresponding Source conveyed under this section must be accompanied\nby the Installation Information.  But this requirement does not apply\nif neither you nor any third party retains the ability to install\nmodified object code on the User Product (for example, the work has\nbeen installed in ROM).\n\n  The requirement to provide Installation Information does not include a\nrequirement to continue to provide support service, warranty, or updates\nfor a work that has been modified or installed by the recipient, or for\nthe User Product in which it has been modified or installed.  Access to a\nnetwork may be denied when the modification itself materially and\nadversely affects the operation of the network or violates the rules and\nprotocols for communication across the network.\n\n  Corresponding Source conveyed, and Installation Information provided,\nin accord with this section must be in a format that is publicly\ndocumented (and with an implementation available to the public in\nsource code form), and must require no special password or key for\nunpacking, reading or copying.\n\n  7. Additional Terms.\n\n  \"Additional permissions\" are terms that supplement the terms of this\nLicense by making exceptions from one or more of its conditions.\nAdditional permissions that are applicable to the entire Program shall\nbe treated as though they were included in this License, to the extent\nthat they are valid under applicable law.  If additional permissions\napply only to part of the Program, that part may be used separately\nunder those permissions, but the entire Program remains governed by\nthis License without regard to the additional permissions.\n\n  When you convey a copy of a covered work, you may at your option\nremove any additional permissions from that copy, or from any part of\nit.  (Additional permissions may be written to require their own\nremoval in certain cases when you modify the work.)  You may place\nadditional permissions on material, added by you to a covered work,\nfor which you have or can give appropriate copyright permission.\n\n  Notwithstanding any other provision of this License, for material you\nadd to a covered work, you may (if authorized by the copyright holders of\nthat material) supplement the terms of this License with terms:\n\n    a) Disclaiming warranty or limiting liability differently from the\n    terms of sections 15 and 16 of this License; or\n\n    b) Requiring preservation of specified reasonable legal notices or\n    author attributions in that material or in the Appropriate Legal\n    Notices displayed by works containing it; or\n\n    c) Prohibiting misrepresentation of the origin of that material, or\n    requiring that modified versions of such material be marked in\n    reasonable ways as different from the original version; or\n\n    d) Limiting the use for publicity purposes of names of licensors or\n    authors of the material; or\n\n    e) Declining to grant rights under trademark law for use of some\n    trade names, trademarks, or service marks; or\n\n    f) Requiring indemnification of licensors and authors of that\n    material by anyone who conveys the material (or modified versions of\n    it) with contractual assumptions of liability to the recipient, for\n    any liability that these contractual assumptions directly impose on\n    those licensors and authors.\n\n  All other non-permissive additional terms are considered \"further\nrestrictions\" within the meaning of section 10.  If the Program as you\nreceived it, or any part of it, contains a notice stating that it is\ngoverned by this License along with a term that is a further\nrestriction, you may remove that term.  If a license document contains\na further restriction but permits relicensing or conveying under this\nLicense, you may add to a covered work material governed by the terms\nof that license document, provided that the further restriction does\nnot survive such relicensing or conveying.\n\n  If you add terms to a covered work in accord with this section, you\nmust place, in the relevant source files, a statement of the\nadditional terms that apply to those files, or a notice indicating\nwhere to find the applicable terms.\n\n  Additional terms, permissive or non-permissive, may be stated in the\nform of a separately written license, or stated as exceptions;\nthe above requirements apply either way.\n\n  8. Termination.\n\n  You may not propagate or modify a covered work except as expressly\nprovided under this License.  Any attempt otherwise to propagate or\nmodify it is void, and will automatically terminate your rights under\nthis License (including any patent licenses granted under the third\nparagraph of section 11).\n\n  However, if you cease all violation of this License, then your\nlicense from a particular copyright holder is reinstated (a)\nprovisionally, unless and until the copyright holder explicitly and\nfinally terminates your license, and (b) permanently, if the copyright\nholder fails to notify you of the violation by some reasonable means\nprior to 60 days after the cessation.\n\n  Moreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.\n\n  Termination of your rights under this section does not terminate the\nlicenses of parties who have received copies or rights from you under\nthis License.  If your rights have been terminated and not permanently\nreinstated, you do not qualify to receive new licenses for the same\nmaterial under section 10.\n\n  9. Acceptance Not Required for Having Copies.\n\n  You are not required to accept this License in order to receive or\nrun a copy of the Program.  Ancillary propagation of a covered work\noccurring solely as a consequence of using peer-to-peer transmission\nto receive a copy likewise does not require acceptance.  However,\nnothing other than this License grants you permission to propagate or\nmodify any covered work.  These actions infringe copyright if you do\nnot accept this License.  Therefore, by modifying or propagating a\ncovered work, you indicate your acceptance of this License to do so.\n\n  10. Automatic Licensing of Downstream Recipients.\n\n  Each time you convey a covered work, the recipient automatically\nreceives a license from the original licensors, to run, modify and\npropagate that work, subject to this License.  You are not responsible\nfor enforcing compliance by third parties with this License.\n\n  An \"entity transaction\" is a transaction transferring control of an\norganization, or substantially all assets of one, or subdividing an\norganization, or merging organizations.  If propagation of a covered\nwork results from an entity transaction, each party to that\ntransaction who receives a copy of the work also receives whatever\nlicenses to the work the party's predecessor in interest had or could\ngive under the previous paragraph, plus a right to possession of the\nCorresponding Source of the work from the predecessor in interest, if\nthe predecessor has it or can get it with reasonable efforts.\n\n  You may not impose any further restrictions on the exercise of the\nrights granted or affirmed under this License.  For example, you may\nnot impose a license fee, royalty, or other charge for exercise of\nrights granted under this License, and you may not initiate litigation\n(including a cross-claim or counterclaim in a lawsuit) alleging that\nany patent claim is infringed by making, using, selling, offering for\nsale, or importing the Program or any portion of it.\n\n  11. Patents.\n\n  A \"contributor\" is a copyright holder who authorizes use under this\nLicense of the Program or a work on which the Program is based.  The\nwork thus licensed is called the contributor's \"contributor version\".\n\n  A contributor's \"essential patent claims\" are all patent claims\nowned or controlled by the contributor, whether already acquired or\nhereafter acquired, that would be infringed by some manner, permitted\nby this License, of making, using, or selling its contributor version,\nbut do not include claims that would be infringed only as a\nconsequence of further modification of the contributor version.  For\npurposes of this definition, \"control\" includes the right to grant\npatent sublicenses in a manner consistent with the requirements of\nthis License.\n\n  Each contributor grants you a non-exclusive, worldwide, royalty-free\npatent license under the contributor's essential patent claims, to\nmake, use, sell, offer for sale, import and otherwise run, modify and\npropagate the contents of its contributor version.\n\n  In the following three paragraphs, a \"patent license\" is any express\nagreement or commitment, however denominated, not to enforce a patent\n(such as an express permission to practice a patent or covenant not to\nsue for patent infringement).  To \"grant\" such a patent license to a\nparty means to make such an agreement or commitment not to enforce a\npatent against the party.\n\n  If you convey a covered work, knowingly relying on a patent license,\nand the Corresponding Source of the work is not available for anyone\nto copy, free of charge and under the terms of this License, through a\npublicly available network server or other readily accessible means,\nthen you must either (1) cause the Corresponding Source to be so\navailable, or (2) arrange to deprive yourself of the benefit of the\npatent license for this particular work, or (3) arrange, in a manner\nconsistent with the requirements of this License, to extend the patent\nlicense to downstream recipients.  \"Knowingly relying\" means you have\nactual knowledge that, but for the patent license, your conveying the\ncovered work in a country, or your recipient's use of the covered work\nin a country, would infringe one or more identifiable patents in that\ncountry that you have reason to believe are valid.\n\n  If, pursuant to or in connection with a single transaction or\narrangement, you convey, or propagate by procuring conveyance of, a\ncovered work, and grant a patent license to some of the parties\nreceiving the covered work authorizing them to use, propagate, modify\nor convey a specific copy of the covered work, then the patent license\nyou grant is automatically extended to all recipients of the covered\nwork and works based on it.\n\n  A patent license is \"discriminatory\" if it does not include within\nthe scope of its coverage, prohibits the exercise of, or is\nconditioned on the non-exercise of one or more of the rights that are\nspecifically granted under this License.  You may not convey a covered\nwork if you are a party to an arrangement with a third party that is\nin the business of distributing software, under which you make payment\nto the third party based on the extent of your activity of conveying\nthe work, and under which the third party grants, to any of the\nparties who would receive the covered work from you, a discriminatory\npatent license (a) in connection with copies of the covered work\nconveyed by you (or copies made from those copies), or (b) primarily\nfor and in connection with specific products or compilations that\ncontain the covered work, unless you entered into that arrangement,\nor that patent license was granted, prior to 28 March 2007.\n\n  Nothing in this License shall be construed as excluding or limiting\nany implied license or other defenses to infringement that may\notherwise be available to you under applicable patent law.\n\n  12. No Surrender of Others' Freedom.\n\n  If conditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot convey a\ncovered work so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you may\nnot convey it at all.  For example, if you agree to terms that obligate you\nto collect a royalty for further conveying from those to whom you convey\nthe Program, the only way you could satisfy both those terms and this\nLicense would be to refrain entirely from conveying the Program.\n\n  13. Remote Network Interaction; Use with the GNU General Public License.\n\n  Notwithstanding any other provision of this License, if you modify the\nProgram, your modified version must prominently offer all users\ninteracting with it remotely through a computer network (if your version\nsupports such interaction) an opportunity to receive the Corresponding\nSource of your version by providing access to the Corresponding Source\nfrom a network server at no charge, through some standard or customary\nmeans of facilitating copying of software.  This Corresponding Source\nshall include the Corresponding Source for any work covered by version 3\nof the GNU General Public License that is incorporated pursuant to the\nfollowing paragraph.\n\n  Notwithstanding any other provision of this License, you have\npermission to link or combine any covered work with a work licensed\nunder version 3 of the GNU General Public License into a single\ncombined work, and to convey the resulting work.  The terms of this\nLicense will continue to apply to the part which is the covered work,\nbut the work with which it is combined will remain governed by version\n3 of the GNU General Public License.\n\n  14. Revised Versions of this License.\n\n  The Free Software Foundation may publish revised and/or new versions of\nthe GNU Affero General Public License from time to time.  Such new versions\nwill be similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\n  Each version is given a distinguishing version number.  If the\nProgram specifies that a certain numbered version of the GNU Affero General\nPublic License \"or any later version\" applies to it, you have the\noption of following the terms and conditions either of that numbered\nversion or of any later version published by the Free Software\nFoundation.  If the Program does not specify a version number of the\nGNU Affero General Public License, you may choose any version ever published\nby the Free Software Foundation.\n\n  If the Program specifies that a proxy can decide which future\nversions of the GNU Affero General Public License can be used, that proxy's\npublic statement of acceptance of a version permanently authorizes you\nto choose that version for the Program.\n\n  Later license versions may give you additional or different\npermissions.  However, no additional obligations are imposed on any\nauthor or copyright holder as a result of your choosing to follow a\nlater version.\n\n  15. Disclaimer of Warranty.\n\n  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\nAPPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\nOF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\nIS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\nALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n  16. Limitation of Liability.\n\n  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\nTHE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\nGENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\nUSE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\nDATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\nPARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\nEVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\nSUCH DAMAGES.\n\n  17. Interpretation of Sections 15 and 16.\n\n  If the disclaimer of warranty and limitation of liability provided\nabove cannot be given local legal effect according to their terms,\nreviewing courts shall apply local law that most closely approximates\nan absolute waiver of all civil liability in connection with the\nProgram, unless a warranty or assumption of liability accompanies a\ncopy of the Program in return for a fee.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nstate the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU Affero General Public License as published by\n    the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU Affero General Public License for more details.\n\n    You should have received a copy of the GNU Affero General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nAlso add information on how to contact you by electronic and paper mail.\n\n  If your software can interact with users remotely through a computer\nnetwork, you should also make sure that it provides a way for users to\nget its source.  For example, if your program is a web application, its\ninterface could display a \"Source\" link that leads users to an archive\nof the code.  There are many ways you could offer source, and different\nsolutions will be better for different programs; see section 13 for the\nspecific requirements.\n\n  You should also get your employer (if you work as a programmer) or school,\nif any, to sign a \"copyright disclaimer\" for the program, if necessary.\nFor more information on this, and how to apply and follow the GNU AGPL, see\n<http://www.gnu.org/licenses/>.\n"
  },
  {
    "path": "Makefile",
    "content": "help:           ## Show this help.\n\t@fgrep -h \"##\" $(MAKEFILE_LIST) | fgrep -v fgrep | sed -e 's/\\\\$$//' | sed -e 's/##//'\n\ndoc-clean:      ## Clean the documentation output directory\n\tcd docs; rm -rf _doc\n\ndoc-build: doc-clean        ## Build the documentation\n\tcd docs; sphinx-build . _doc\n\ndoc-open: doc-build         ## Open the documentation\n\tcd docs; open _doc/index.html\n\ndoc-deploy: doc-build       ## Deploy the documentation at http://maker-keeper-docs.surge.sh\n\tcd docs; surge --project _doc --domain maker-keeper-docs.surge.sh\n"
  },
  {
    "path": "README.md",
    "content": "# pymaker\n\nPython API for Maker contracts.\n\n![Build Status](https://github.com/makerdao/pymaker/actions/workflows/.github/workflows/tests.yaml/badge.svg?branch=master)\n\n<https://chat.makerdao.com/channel/keeper>\n\n## Introduction\n\nThe _DAI Stablecoin System_ incentivizes external agents, called _keepers_,\nto automate certain operations around the Ethereum blockchain. In order to ease their\ndevelopment, an API around most of the Maker contracts has been created. It can be used\nnot only by keepers, but may also be found useful by authors of some other, unrelated\nutilities aiming to interact with these contracts.\n\nBased on this API, a set of reference Maker keepers is being developed. They all used to reside\nin this repository, but now each of them has an individual one: \n[bite-keeper](https://github.com/makerdao/bite-keeper) (SCD only),\n[arbitrage-keeper](https://github.com/makerdao/arbitrage-keeper),\n[auction-keeper](https://github.com/makerdao/auction-keeper) (MCD only),\n[cdp-keeper](https://github.com/makerdao/cdp-keeper) (SCD only),\n[market-maker-keeper](https://github.com/makerdao/market-maker-keeper).\n\nYou only need to install this project directly if you want to build your own keepers,\nor if you want to play with this API library itself. If you just want to install\none of reference keepers, go to one of the repositories linked above and start from there.\nEach of these keepers references some version of `pymaker` via a Git submodule.\n\n## Installation\n\nThis project uses *Python 3.6.6*.\n\nIn order to clone the project and install required third-party packages please execute:\n```\ngit clone https://github.com/makerdao/pymaker.git\ncd pymaker\npip3 install -r requirements.txt\n```\n\n### Known Ubuntu issues\n\nIn order for the `secp256k` Python dependency to compile properly, following packages will need to be installed:\n```\nsudo apt-get install build-essential automake libtool pkg-config libffi-dev python-dev python-pip libsecp256k1-dev\n```\n\n(for Ubuntu 18.04 Server)\n\n### Known macOS issues\n\nIn order for the Python requirements to install correctly on _macOS_, please install\n`openssl`, `libtool`, `pkg-config` and `automake` using [Homebrew](https://brew.sh/):\n```\nbrew install openssl libtool pkg-config automake\n```\n\nand set the `LDFLAGS` environment variable before you run `pip3 install -r requirements.txt`:\n```\nexport LDFLAGS=\"-L$(brew --prefix openssl)/lib\" CFLAGS=\"-I$(brew --prefix openssl)/include\" \n```\n\n### Known node issues\n * `pymaker` has been tested against **Parity/OpenEthereum** and **Geth**.  It has not been tested against **Hyperledger Besu**.\n * Many Ethereum node providers do not support the full [JSON-RPC API](https://eth.wiki/json-rpc/API#json-rpc-methods). \nAs such, certain JSON-RPC calls in `__init__.py` may not function properly.  \n * Some node providers only support certain calls using websocket endpoints.  Unfortunately, Web3.py's \n `WebsocketProvider` [does not support](https://github.com/ethereum/web3.py/issues/1413) multiple threads awaiting a \n response from the websocket, breaking some core `pymaker` functionality in `Lifecycle` and `Transact` classes.\n * When using an **Infura** node to pull event logs, ensure your requests are batched into a small enough chunks such \n that no more than 10,000 results will be returned for each request.\n * Asynchronous submission of simultaneous transactions often doesn't work on third-party node providers because RPC \n calls to `parity_nextNonce` and `getTransactionCount` are inappropriately proxied, cached, or just plain not \n supported.  To remedy this, a serial-incrementing nonce is used for these providers' URLs.  The downside to a serial-\n incrementing nonce is that transactions submitted for the same account from another wallet or keeper will bring the \n next nonce out-of-alignment, causing transaction failures or unexpected replacements.  To work around this, stop the \n application, wait for pending transactions for the account to be mined, and then restart the application.  \n * Recovery of pending transactions does not work on certain third-party node providers.\n\n\n## Available APIs\n\nThe current version provides APIs around:\n* `ERC20Token`,\n* `Tub`, `Tap`,`Top` and `Vox` (<https://github.com/makerdao/sai>),\n* `Vat`, `Cat`, `Vow`, `Jug`, `Flipper`, `Flapper`, `Flopper` (<https://github.com/makerdao/dss>)\n* `SimpleMarket`, `ExpiringMarket` and `MatchingMarket` (<https://github.com/makerdao/maker-otc>),\n* `TxManager` (<https://github.com/makerdao/tx-manager>),\n* `DSGuard` (<https://github.com/dapphub/ds-guard>),\n* `DSToken` (<https://github.com/dapphub/ds-token>),\n* `DSEthToken` (<https://github.com/dapphub/ds-eth-token>),\n* `DSValue` (<https://github.com/dapphub/ds-value>),\n* `DSVault` (<https://github.com/dapphub/ds-vault>),\n* `EtherDelta` (<https://github.com/etherdelta/etherdelta.github.io>),\n* `0x v1` (<https://etherscan.io/address/0x12459c951127e0c374ff9105dda097662a027093#code>, <https://github.com/0xProject/standard-relayer-api>),\n* `0x v2`.\n\nAPIs around the following functionality have not been implemented:\n* Governance (`DSAuth`, `DSGuard`, `DSSpell`, `Mom`)\n\nContributions from the community are appreciated.\n\n## Code samples\n\nBelow you can find some code snippets demonstrating how the API can be used both for developing\nyour own keepers and for creating some other utilities interacting with the _DAI Stablecoin_\necosystem contracts.\n\n### Token transfer\n\nThis snippet demonstrates how to transfer some SAI from our default address. The SAI token address\nis discovered by querying the `Tub`, so all we need as a `Tub` address:\n\n```python\nfrom web3 import HTTPProvider, Web3\n\nfrom pymaker import Address\nfrom pymaker.token import ERC20Token\nfrom pymaker.numeric import Wad\nfrom pymaker.sai import Tub\n\n\nweb3 = Web3(HTTPProvider(endpoint_uri=\"http://localhost:8545\"))\n\ntub = Tub(web3=web3, address=Address('0xb7ae5ccabd002b5eebafe6a8fad5499394f67980'))\nsai = ERC20Token(web3=web3, address=tub.sai())\n\nsai.transfer(address=Address('0x0000000000111111111100000000001111111111'),\n             value=Wad.from_number(10)).transact()\n``` \n\n### Updating a DSValue\n\nThis snippet demonstrates how to update a `DSValue` with the ETH/USD rate pulled from _CryptoCompare_: \n\n```python\nimport json\nimport urllib.request\n\nfrom web3 import HTTPProvider, Web3\n\nfrom pymaker import Address\nfrom pymaker.feed import DSValue\nfrom pymaker.numeric import Wad\n\n\ndef cryptocompare_rate() -> Wad:\n    with urllib.request.urlopen(\"https://min-api.cryptocompare.com/data/price?fsym=ETH&tsyms=USD\") as url:\n        data = json.loads(url.read().decode())\n        return Wad.from_number(data['USD'])\n\n\nweb3 = Web3(HTTPProvider(endpoint_uri=\"http://localhost:8545\"))\n\ndsvalue = DSValue(web3=web3, address=Address('0x038b3d8288df582d57db9be2106a27be796b0daf'))\ndsvalue.poke_with_int(cryptocompare_rate().value).transact()\n```\n\n### SAI introspection\n\nThis snippet demonstrates how to fetch data from `Tub` and `Tap` contracts:\n\n```python\nfrom web3 import HTTPProvider, Web3\n\nfrom pymaker import Address\nfrom pymaker.token import ERC20Token\nfrom pymaker.numeric import Ray\nfrom pymaker.sai import Tub, Tap\n\n\nweb3 = Web3(HTTPProvider(endpoint_uri=\"http://localhost:8545\"))\n\ntub = Tub(web3=web3, address=Address('0x448a5065aebb8e423f0896e6c5d525c040f59af3'))\ntap = Tap(web3=web3, address=Address('0xbda109309f9fafa6dd6a9cb9f1df4085b27ee8ef'))\nsai = ERC20Token(web3=web3, address=tub.sai())\nskr = ERC20Token(web3=web3, address=tub.skr())\ngem = ERC20Token(web3=web3, address=tub.gem())\n\nprint(f\"\")\nprint(f\"Token summary\")\nprint(f\"-------------\")\nprint(f\"SAI total supply       : {sai.total_supply()} SAI\")\nprint(f\"SKR total supply       : {skr.total_supply()} SKR\")\nprint(f\"GEM total supply       : {gem.total_supply()} GEM\")\nprint(f\"\")\nprint(f\"Collateral summary\")\nprint(f\"------------------\")\nprint(f\"GEM collateral         : {tub.pie()} GEM\")\nprint(f\"SKR collateral         : {tub.air()} SKR\")\nprint(f\"SKR pending liquidation: {tap.fog()} SKR\")\nprint(f\"\")\nprint(f\"Debt summary\")\nprint(f\"------------\")\nprint(f\"Debt ceiling           : {tub.cap()} SAI\")\nprint(f\"Good debt              : {tub.din()} SAI\")\nprint(f\"Bad debt               : {tap.woe()} SAI\")\nprint(f\"Surplus                : {tap.joy()} SAI\")\nprint(f\"\")\nprint(f\"Feed summary\")\nprint(f\"------------\")\nprint(f\"REF per GEM feed       : {tub.pip()}\")\nprint(f\"REF per SKR price      : {tub.tag()}\")\nprint(f\"GEM per SKR price      : {tub.per()}\")\nprint(f\"\")\nprint(f\"Tub parameters\")\nprint(f\"--------------\")\nprint(f\"Liquidation ratio      : {tub.mat()*100} %\")\nprint(f\"Liquidation penalty    : {tub.axe()*100 - Ray.from_number(100)} %\")\nprint(f\"Stability fee          : {tub.tax()} %\")\nprint(f\"\")\nprint(f\"All cups\")\nprint(f\"--------\")\nfor cup_id in range(1, tub.cupi()+1):\n    cup = tub.cups(cup_id)\n    print(f\"Cup #{cup_id}, lad={cup.lad}, ink={cup.ink} SKR, tab={tub.tab(cup_id)} SAI, safe={tub.safe(cup_id)}\")\n```\n\n### Multi-collateral Dai\n\nThis snippet demonstrates how to create a CDP and draw Dai.\n\n```python\nimport sys\nfrom web3 import Web3, HTTPProvider\n\nfrom pymaker import Address\nfrom pymaker.deployment import DssDeployment\nfrom pymaker.keys import register_keys\nfrom pymaker.numeric import Wad\n\n\nweb3 = Web3(HTTPProvider(endpoint_uri=\"https://localhost:8545\",\n                         request_kwargs={\"timeout\": 10}))\nweb3.eth.defaultAccount = sys.argv[1]   # ex: 0x0000000000000000000000000000000aBcdef123\nregister_keys(web3, [sys.argv[2]])      # ex: key_file=~keys/default-account.json,pass_file=~keys/default-account.pass\n\nmcd = DssDeployment.from_json(web3=web3, conf=open(\"tests/config/kovan-addresses.json\", \"r\").read())\nour_address = Address(web3.eth.defaultAccount)\n\n\n# Choose the desired collateral; in this case we'll wrap some Eth\ncollateral = mcd.collaterals['ETH-A']\nilk = collateral.ilk\ncollateral.gem.deposit(Wad.from_number(3)).transact()\n\n# Add collateral and allocate the desired amount of Dai\ncollateral.approve(our_address)\ncollateral.adapter.join(our_address, Wad.from_number(3)).transact()\nmcd.vat.frob(ilk, our_address, dink=Wad.from_number(3), dart=Wad.from_number(153)).transact()\nprint(f\"CDP Dai balance before withdrawal: {mcd.vat.dai(our_address)}\")\n\n# Mint and withdraw our Dai\nmcd.approve_dai(our_address)\nmcd.dai_adapter.exit(our_address, Wad.from_number(153)).transact()\nprint(f\"CDP Dai balance after withdrawal:  {mcd.vat.dai(our_address)}\")\n\n# Repay (and burn) our Dai\nassert mcd.dai_adapter.join(our_address, Wad.from_number(153)).transact()\nprint(f\"CDP Dai balance after repayment:   {mcd.vat.dai(our_address)}\")\n\n# Withdraw our collateral\nmcd.vat.frob(ilk, our_address, dink=Wad(0), dart=Wad.from_number(-153)).transact()\nmcd.vat.frob(ilk, our_address, dink=Wad.from_number(-3), dart=Wad(0)).transact()\ncollateral.adapter.exit(our_address, Wad.from_number(3)).transact()\nprint(f\"CDP Dai balance w/o collateral:    {mcd.vat.dai(our_address)}\")\n```\n\n### Asynchronous invocation of Ethereum transactions\n\nThis snippet demonstrates how multiple token transfers can be executed asynchronously:\n\n```python\nfrom web3 import HTTPProvider\nfrom web3 import Web3\n\nfrom pymaker import Address, synchronize\nfrom pymaker.numeric import Wad\nfrom pymaker.sai import Tub\nfrom pymaker.token import ERC20Token\n\n\nweb3 = Web3(HTTPProvider(endpoint_uri=\"http://localhost:8545\"))\n\ntub = Tub(web3=web3, address=Address('0x448a5065aebb8e423f0896e6c5d525c040f59af3'))\nsai = ERC20Token(web3=web3, address=tub.sai())\nskr = ERC20Token(web3=web3, address=tub.skr())\n\nsynchronize([sai.transfer(Address('0x0101010101020202020203030303030404040404'), Wad.from_number(1.5)).transact_async(),\n             skr.transfer(Address('0x0303030303040404040405050505050606060606'), Wad.from_number(2.5)).transact_async()])\n```\n\n### Multiple invocations in one Ethereum transaction\n\nThis snippet demonstrates how multiple token transfers can be executed in one Ethereum transaction.\nA `TxManager` instance has to be deployed and owned by the caller.\n\n```python\nfrom web3 import HTTPProvider\nfrom web3 import Web3\n\nfrom pymaker import Address\nfrom pymaker.approval import directly\nfrom pymaker.numeric import Wad\nfrom pymaker.sai import Tub\nfrom pymaker.token import ERC20Token\nfrom pymaker.transactional import TxManager\n\n\nweb3 = Web3(HTTPProvider(endpoint_uri=\"http://localhost:8545\"))\n\ntub = Tub(web3=web3, address=Address('0x448a5065aebb8e423f0896e6c5d525c040f59af3'))\nsai = ERC20Token(web3=web3, address=tub.sai())\nskr = ERC20Token(web3=web3, address=tub.skr())\n\ntx = TxManager(web3=web3, address=Address('0x57bFE16ae8fcDbD46eDa9786B2eC1067cd7A8f48'))\ntx.approve([sai, skr], directly())\n\ntx.execute([sai.address, skr.address],\n           [sai.transfer(Address('0x0101010101020202020203030303030404040404'), Wad.from_number(1.5)).invocation(),\n            skr.transfer(Address('0x0303030303040404040405050505050606060606'), Wad.from_number(2.5)).invocation()]).transact()\n```\n\n### Ad-hoc increasing of gas price for asynchronous transactions\n\n```python\nimport asyncio\nfrom random import randint\n\nfrom web3 import Web3, HTTPProvider\n\nfrom pymaker import Address\nfrom pymaker.gas import FixedGasPrice\nfrom pymaker.oasis import SimpleMarket\n\n\nweb3 = Web3(HTTPProvider(endpoint_uri=f\"http://localhost:8545\"))\notc = SimpleMarket(web3=web3, address=Address('0x375d52588c3f39ee7710290237a95C691d8432E7'))\n\n\nasync def bump_with_increasing_gas_price(order_id):\n    gas_price = FixedGasPrice(gas_price=1000000000)\n    task = asyncio.ensure_future(otc.bump(order_id).transact_async(gas_price=gas_price))\n\n    while not task.done():\n        await asyncio.sleep(1)\n        gas_price.update_gas_price(gas_price.gas_price + randint(0, gas_price.gas_price))\n\n    return task.result()\n\n\nbump_task = asyncio.ensure_future(bump_with_increasing_gas_price(otc.get_orders()[-1].order_id))\nevent_loop = asyncio.get_event_loop()\nbump_result = event_loop.run_until_complete(bump_task)\n\nprint(bump_result)\nprint(bump_result.transaction_hash)\n```\n\n## Testing\n\nPrerequisites:\n* [docker and docker-compose](https://www.docker.com/get-started) - for containerized deployments of Ganache and Parity\n* [seth](https://dapp.tools/seth/) - to enable the token faucet\n\nThis project uses [pytest](https://docs.pytest.org/en/latest/) for unit testing.  Testing of Multi-collateral Dai is \nperformed on a Dockerized local testchain included in `tests\\config`.\n\nIn order to be able to run tests, please install development dependencies first by executing:\n```\npip3 install -r requirements-dev.txt\n```\n\nYou can then run all tests with:\n```\n./test.sh\n```\n\nBy default, `pymaker` will not send a transaction to the chain if gas estimation fails, because this means the \ntransaction would revert.  For testing purposes, it is sometimes useful to send bad transactions to the chain.  To \naccomplish this, set class variable `gas_estimate_for_bad_txs` in your application.  For example:\n```\nfrom pymaker import Transact\nTransact.gas_estimate_for_bad_txs = 200000\n```\n\n## License\n\nSee [COPYING](https://github.com/makerdao/pymaker/blob/master/COPYING) file.\n"
  },
  {
    "path": "config/kovan-addresses.json",
    "content": "{\n  \"CHANGELOG\": \"0xdA0Ab1e0017DEbCd72Be8599041a2aa3bA7e740F\",\n  \"MULTICALL\": \"0xC6D81A2e375Eee15a20E6464b51c5FC6Bb949fdA\",\n  \"FAUCET\": \"0x57aAeAE905376a4B1899bA81364b4cE2519CBfB3\",\n  \"MCD_DEPLOY\": \"0x13141b8a5E4A82Ebc6b636849dd6A515185d6236\",\n  \"FLIP_FAB\": \"0x7c890e1e492FDDA9096353D155eE1B26C1656a62\",\n  \"CLIP_FAB\": \"0x54659eebDFB1Dd0b53ed2252ed344a8dCbCC96CD\",\n  \"CALC_FAB\": \"0xA81598667AC561986b70ae11bBE2dd5348ed4327\",\n  \"LERP_FAB\": \"0xa6766Ed3574bAFc6114618E74035C7bb5e9a6aa9\",\n  \"MCD_GOV\": \"0xAaF64BFCC32d0F15873a02163e7E500671a4ffcD\",\n  \"GOV_GUARD\": \"0xE50303C6B67a2d869684EFb09a62F6aaDD06387B\",\n  \"MCD_ADM\": \"0x27E0c9567729Ea6e3241DE74B3dE499b7ddd3fe6\",\n  \"VOTE_PROXY_FACTORY\": \"0x1400798AA746457E467A1eb9b3F3f72C25314429\",\n  \"MCD_VAT\": \"0xbA987bDB501d131f766fEe8180Da5d81b34b69d9\",\n  \"MCD_JUG\": \"0xcbB7718c9F39d05aEEDE1c472ca8Bf804b2f1EaD\",\n  \"MCD_CAT\": \"0xdDb5F7A3A5558b9a6a1f3382BD75E2268d1c6958\",\n  \"MCD_DOG\": \"0x121D0953683F74e9a338D40d9b4659C0EBb539a0\",\n  \"MCD_VOW\": \"0x0F4Cbe6CBA918b7488C26E29d9ECd7368F38EA3b\",\n  \"MCD_JOIN_DAI\": \"0x5AA71a3ae1C0bd6ac27A1f28e1415fFFB6F15B8c\",\n  \"MCD_FLAP\": \"0xc6d3C83A080e2Ef16E4d7d4450A869d0891024F5\",\n  \"MCD_FLOP\": \"0x52482a3100F79FC568eb2f38C4a45ba457FBf5fA\",\n  \"MCD_PAUSE\": \"0x8754E6ecb4fe68DaA5132c2886aB39297a5c7189\",\n  \"MCD_PAUSE_PROXY\": \"0x0e4725db88Bb038bBa4C4723e91Ba183BE11eDf3\",\n  \"MCD_GOV_ACTIONS\": \"0x0Ca17E81073669741714354f16D800af64e95C75\",\n  \"MCD_DAI\": \"0x4F96Fe3b7A6Cf9725f59d353F723c1bDb64CA6Aa\",\n  \"MCD_SPOT\": \"0x3a042de6413eDB15F2784f2f97cC68C7E9750b2D\",\n  \"MCD_POT\": \"0xEA190DBDC7adF265260ec4dA6e9675Fd4f5A78bb\",\n  \"MCD_END\": \"0x3d9603037FF096af03B83725dFdB1CDA9EA02CE4\",\n  \"MCD_ESM\": \"0xD5D728446275B0A12E4a4038527974b92353B4a9\",\n  \"PROXY_ACTIONS\": \"0xd1D24637b9109B7f61459176EdcfF9Be56283a7B\",\n  \"PROXY_ACTIONS_END\": \"0x7c3f28f174F2b0539C202a5307Ff48efa61De982\",\n  \"PROXY_ACTIONS_DSR\": \"0xc5CC1Dfb64A62B9C7Bb6Cbf53C2A579E2856bf92\",\n  \"CDP_MANAGER\": \"0x1476483dD8C35F25e568113C5f70249D3976ba21\",\n  \"DSR_MANAGER\": \"0x7f5d60432DE4840a3E7AE7218f7D6b7A2412683a\",\n  \"GET_CDPS\": \"0x592301a23d37c591C5856f28726AF820AF8e7014\",\n  \"ILK_REGISTRY\": \"0xc3F42deABc0C506e8Ae9356F2d4fc1505196DCDB\",\n  \"OSM_MOM\": \"0x5dA9D1C3d4f1197E5c52Ff963916Fe84D2F5d8f3\",\n  \"FLIPPER_MOM\": \"0x50dC6120c67E456AdA2059cfADFF0601499cf681\",\n  \"CLIPPER_MOM\": \"0x96E9a19Be6EA91d1C0908e5E207f944dc2E7B878\",\n  \"MCD_IAM_AUTO_LINE\": \"0xe7D7d61c0ed9306B6c93E7C65F6C9DDF38b9320b\",\n  \"PROXY_FACTORY\": \"0xe11E3b391F7E8bC47247866aF32AF67Dd58Dc800\",\n  \"PROXY_REGISTRY\": \"0x64A436ae831C1672AE81F674CAb8B6775df3475C\",\n  \"ETH\": \"0xd0A1E359811322d97991E03f863a0C30C2cF029C\",\n  \"PIP_ETH\": \"0x75dD74e8afE8110C8320eD397CcCff3B8134d981\",\n  \"MCD_JOIN_ETH_A\": \"0x775787933e92b709f2a3C70aa87999696e74A9F8\",\n  \"MCD_CLIP_ETH_A\": \"0x7dD1Fb6b9aFdBA9F28DB89c81723b8c6B27A2Fbe\",\n  \"MCD_CLIP_CALC_ETH_A\": \"0x46bE29C1993d64f0C93e81D69FfAFDF4881806f2\",\n  \"MCD_JOIN_ETH_B\": \"0xd19A770F00F89e6Dd1F12E6D6E6839b95C084D85\",\n  \"MCD_CLIP_ETH_B\": \"0x004676c737FC75A2799dFe745d23F5597620Ad43\",\n  \"MCD_CLIP_CALC_ETH_B\": \"0x4672215ADF0556Af60261e97E221c875ce9F0863\",\n  \"MCD_JOIN_ETH_C\": \"0xD166b57355BaCE25e5dEa5995009E68584f60767\",\n  \"MCD_CLIP_ETH_C\": \"0x86D5eA244cf6c79227CA73004C963b72431f23ac\",\n  \"MCD_CLIP_CALC_ETH_C\": \"0xa8AfB2680cced6de0E1dfe5C35F0FEdFB8E95720\",\n  \"BAT\": \"0x9f8cFB61D3B2aF62864408DD703F9C3BEB55dff7\",\n  \"PIP_BAT\": \"0x5C40C9Eb35c76069fA4C3A00EA59fAc6fFA9c113\",\n  \"MCD_JOIN_BAT_A\": \"0x2a4C485B1B8dFb46acCfbeCaF75b6188A59dBd0a\",\n  \"MCD_CLIP_BAT_A\": \"0x332B44A24e2CF8A258E8A1932b13296b9316a74c\",\n  \"MCD_CLIP_CALC_BAT_A\": \"0x4AB9058A9cAB0B18B4b40621Fa44B2131836Ad32\",\n  \"USDC\": \"0xBD84be3C303f6821ab297b840a99Bd0d4c4da6b5\",\n  \"PIP_USDC\": \"0x4c51c2584309b7BF328F89609FDd03B3b95fC677\",\n  \"MCD_JOIN_USDC_A\": \"0x4c514656E7dB7B859E994322D2b511d99105C1Eb\",\n  \"MCD_CLIP_USDC_A\": \"0x09D45087c035DbcD8d6fB5e9d4c5341b9101E626\",\n  \"MCD_CLIP_CALC_USDC_A\": \"0xF8D26c26Ac481794E4Aebf4F35B10d8E9748086a\",\n  \"MCD_JOIN_USDC_B\": \"0xaca10483e7248453BB6C5afc3e403e8b7EeDF314\",\n  \"MCD_CLIP_USDC_B\": \"0xedFc36f75faafa80e39cd4623def15da6CF2B5C0\",\n  \"MCD_CLIP_CALC_USDC_B\": \"0x275076c9c101AF880BD944991258d564FA31D61B\",\n  \"WBTC\": \"0x7419f744bBF35956020C1687fF68911cD777f865\",\n  \"PIP_WBTC\": \"0x2f38a1bD385A9B395D01f2Cbf767b4527663edDB\",\n  \"MCD_JOIN_WBTC_A\": \"0xB879c7d51439F8e7AC6b2f82583746A0d336e63F\",\n  \"MCD_CLIP_WBTC_A\": \"0x5518C2f409Bed4bD5FF3542d9D5002251EEDA892\",\n  \"MCD_CLIP_CALC_WBTC_A\": \"0x2c39F8C9aE16B84076D7fEA15CE5855925a09DA6\",\n  \"TUSD\": \"0xD6CE59F06Ff2070Dd5DcAd0866A7D8cd9270041a\",\n  \"PIP_TUSD\": \"0xE4bAECdba7A8Ff791E14c6BF7e8089Dfdf75C7E7\",\n  \"MCD_JOIN_TUSD_A\": \"0xe53f6755A031708c87d80f5B1B43c43892551c17\",\n  \"MCD_CLIP_TUSD_A\": \"0x9D547d599489B3950485cBa119FC37Bba9c15c13\",\n  \"MCD_CLIP_CALC_TUSD_A\": \"0x4AE93701287b8C86f17E5a0Cb4D0732b5ae6EFBD\",\n  \"ZRX\": \"0xC2C08A566aD44129E69f8FC98684EAA28B01a6e7\",\n  \"PIP_ZRX\": \"0x218037a42947E634191A231fcBAEAE8b16a39b3f\",\n  \"MCD_JOIN_ZRX_A\": \"0x85D38fF6a6FCf98bD034FB5F9D72cF15e38543f2\",\n  \"MCD_CLIP_ZRX_A\": \"0x9072C477FEb67eEFd8865737206e87570444885E\",\n  \"MCD_CLIP_CALC_ZRX_A\": \"0xCd8Aa54176A333C3B668f65Ff8F11ee909f9A698\",\n  \"KNC\": \"0x9800a0a3c7e9682e1AEb7CAA3200854eFD4E9327\",\n  \"PIP_KNC\": \"0x10799280EF9d7e2d037614F5165eFF2cB8522651\",\n  \"MCD_JOIN_KNC_A\": \"0xE42427325A0e4c8e194692FfbcACD92C2C381598\",\n  \"MCD_CLIP_KNC_A\": \"0x09EA13E49885C29dD270B5c3F557D71A30479333\",\n  \"MCD_CLIP_CALC_KNC_A\": \"0x8D11DC42F5Cc6fE19FeE799e3e24b506cEadAB4b\",\n  \"MANA\": \"0x221F4D62636b7B51b99e36444ea47Dc7831c2B2f\",\n  \"PIP_MANA\": \"0xE97D2b077Fe19c80929718d377981d9F754BF36e\",\n  \"MCD_JOIN_MANA_A\": \"0xdC9Fe394B27525e0D9C827EE356303b49F607aaF\",\n  \"MCD_CLIP_MANA_A\": \"0xFd79e5881CC59F4637ddb3799D302BF089dEE832\",\n  \"MCD_CLIP_CALC_MANA_A\": \"0x14cd62bB700d3cDe2bC45Db2875b58200DDD2503\",\n  \"USDT\": \"0x9245BD36FA20fcD292F4765c4b5dF83Dc3fD5e86\",\n  \"PIP_USDT\": \"0x3588A7973D41AaeA7B203549553C991C4311951e\",\n  \"MCD_JOIN_USDT_A\": \"0x9B011a74a690dFd9a1e4996168d3EcBDE73c2226\",\n  \"MCD_CLIP_USDT_A\": \"0xBDd2d10dAF8D86dA1f02bB7c7C7841bC9A4F62D4\",\n  \"MCD_CLIP_CALC_USDT_A\": \"0xa3a5163Fa4d46D799fE4B036349f0289D69A4445\",\n  \"PAXUSD\": \"0xa6383AF46c36219a472b9549d70E4768dfA8894c\",\n  \"PIP_PAXUSD\": \"0xD01fefed46eb21cd057bAa14Ff466842C31a0Cd9\",\n  \"MCD_JOIN_PAXUSD_A\": \"0x3d6a14C9542B429a4e3d255F6687754d4898D897\",\n  \"MCD_CLIP_PAXUSD_A\": \"0x3939B686a0A7265512D38Ea3fe700812A703BF31\",\n  \"MCD_CLIP_CALC_PAXUSD_A\": \"0x784863edC4C28D73192bf56944D8803c0b5E0CbF\",\n  \"COMP\": \"0x1dDe24ACE93F9F638Bfd6fCE1B38b842703Ea1Aa\",\n  \"PIP_COMP\": \"0xcc10b1C53f4BFFEE19d0Ad00C40D7E36a454D5c4\",\n  \"MCD_JOIN_COMP_A\": \"0x16D567c1F6824ffFC460A11d48F61E010ae43766\",\n  \"MCD_CLIP_COMP_A\": \"0xCDe79465D0B98775c1831957b88BFa12b8A3f020\",\n  \"MCD_CLIP_CALC_COMP_A\": \"0x3e41fCB2DC5370F8612884CB2928E74FED77Cb4B\",\n  \"LRC\": \"0xF070662e48843934b5415f150a18C250d4D7B8aB\",\n  \"PIP_LRC\": \"0xcEE47Bb8989f625b5005bC8b9f9A0B0892339721\",\n  \"MCD_JOIN_LRC_A\": \"0x436286788C5dB198d632F14A20890b0C4D236800\",\n  \"MCD_CLIP_LRC_A\": \"0xaF94A206A3f3948c0BDB6a195a119862F26F5e92\",\n  \"MCD_CLIP_CALC_LRC_A\": \"0xD47DF2Cae1a86fC22e8A8b9B06b22f27860Cb333\",\n  \"LINK\": \"0xa36085F69e2889c224210F603D836748e7dC0088\",\n  \"PIP_LINK\": \"0x20D5A457e49D05fac9729983d9701E0C3079Efac\",\n  \"MCD_JOIN_LINK_A\": \"0xF4Df626aE4fb446e2Dcce461338dEA54d2b9e09b\",\n  \"MCD_CLIP_LINK_A\": \"0x1eB71cC879960606F8ab0E02b3668EEf92CE6D98\",\n  \"MCD_CLIP_CALC_LINK_A\": \"0xbd586d6352Fcf0C45f77FC9348F4Ee7539F6e2bD\",\n  \"BAL\": \"0x630D82Cbf82089B09F71f8d3aAaff2EBA6f47B15\",\n  \"PIP_BAL\": \"0x4fd34872F3AbC07ea6C45c7907f87041C0801DdE\",\n  \"MCD_JOIN_BAL_A\": \"0x8De5EA9251E0576e3726c8766C56E27fAb2B6597\",\n  \"MCD_CLIP_BAL_A\": \"0x8F6C48A26ebf4006Ab542d030D4090DfeC39652E\",\n  \"MCD_CLIP_CALC_BAL_A\": \"0xd041ED45EC5e4539BbbCd91B97D36C76F9d678C9\",\n  \"YFI\": \"0x251F1c3077FEd1770cB248fB897100aaE1269FFC\",\n  \"PIP_YFI\": \"0x9D8255dc4e25bB85e49c65B21D8e749F2293862a\",\n  \"MCD_JOIN_YFI_A\": \"0x5b683137481F2FE683E2f2385792B1DeB018050F\",\n  \"MCD_CLIP_YFI_A\": \"0x9020C96B06d2ac59e98A0F35f131D491EEcAa2C2\",\n  \"MCD_CLIP_CALC_YFI_A\": \"0x54A18C6ceEBDf42D8532EBf5e0a67C430a51b2f6\",\n  \"GUSD\": \"0x31D8EdbF6F33ef858c80d68D06Ec83f33c2aA150\",\n  \"PIP_GUSD\": \"0xb6630DE6Eda0f3f3d96Db4639914565d6b82CfEF\",\n  \"MCD_JOIN_GUSD_A\": \"0x0c6B26e6AB583D2e4528034037F74842ea988909\",\n  \"MCD_CLIP_GUSD_A\": \"0x448eD0ff4e154C1cBefE2c8057906Dd3dA194dA5\",\n  \"MCD_CLIP_CALC_GUSD_A\": \"0x4DD8AaB74a710E7a95937ef1b2618ee76F829Ba6\",\n  \"UNI\": \"0x0C527850e5D6B2B406F1d65895d5b17c5A29Ce51\",\n  \"PIP_UNI\": \"0xe573a75BF4827658F6D600FD26C205a3fe34ee28\",\n  \"MCD_JOIN_UNI_A\": \"0xb6E6EE050B4a74C8cc1DfdE62cAC8C6d9D8F4CAa\",\n  \"MCD_CLIP_UNI_A\": \"0xed3D15e390750f0808E64e0Af1F791e6c5b47c2e\",\n  \"MCD_CLIP_CALC_UNI_A\": \"0x1ee2ecD5149F4b46257a37195994337F4a35E5e8\",\n  \"RENBTC\": \"0xe3dD56821f8C422849AF4816fE9B3c53c6a2F0Bd\",\n  \"PIP_RENBTC\": \"0x2f38a1bD385A9B395D01f2Cbf767b4527663edDB\",\n  \"MCD_JOIN_RENBTC_A\": \"0x12F1F6c7E5fDF1B671CebFBDE974341847d0Caa4\",\n  \"MCD_CLIP_RENBTC_A\": \"0xEf9EEb37CDB15eaD336440BebC30C4CD37Da1891\",\n  \"MCD_CLIP_CALC_RENBTC_A\": \"0xF47749299BCCe427cFd9d015D543aEF83D3BD4Da\",\n  \"AAVE\": \"0x7B339a530Eed72683F56868deDa87BbC64fD9a12\",\n  \"PIP_AAVE\": \"0xd2d9B1355Ea96567E7D6C7A6945f5c7ec8150Cc9\",\n  \"MCD_JOIN_AAVE_A\": \"0x9f1Ed3219035e6bDb19E0D95d316c7c39ad302EC\",\n  \"MCD_CLIP_AAVE_A\": \"0xC8D2d6692981abc7DC5Bf4E345ce3Ce462FA90c9\",\n  \"MCD_CLIP_CALC_AAVE_A\": \"0x0FdF9CecFF267a49f4e9f67014AFEc873143677D\",\n  \"MATIC\": \"0x688E1A8830Ea8dd8fe389FA2228997C663b3807A\",\n  \"PIP_MATIC\": \"0x13594bF4E0C61946936674217c415c6d555Fec50\",\n  \"MCD_JOIN_MATIC_A\": \"0x4Af8801fbDD5ae4FDe2cbC9F844b09c6777525CE\",\n  \"MCD_CLIP_MATIC_A\": \"0x75FE5CD0c23894C8424ac835C054aCA92B994445\",\n  \"MCD_CLIP_CALC_MATIC_A\": \"0x0AB67AA706F1cECD3df457016E822a09bFf18f23\",\n  \"UNIV2DAIETH\": \"0xB10cf58E08b94480fCb81d341A63295eBb2062C2\",\n  \"PIP_UNIV2DAIETH\": \"0xED9201cd545F1d2457D2D48981E7832C754959e9\",\n  \"MCD_JOIN_UNIV2DAIETH_A\": \"0x03f18d97D25c13FecB15aBee143276D3bD2742De\",\n  \"MCD_CLIP_UNIV2DAIETH_A\": \"0xfcFd4255F67C70Cf5fB534535eBe8152Ba6DC5Cd\",\n  \"MCD_CLIP_CALC_UNIV2DAIETH_A\": \"0x0Aa53A82182dd60a630A49eCc286b295fEC5Ba98\",\n  \"MIP21_LIQUIDATION_ORACLE\": \"0x2881c5dF65A8D81e38f7636122aFb456514804CC\",\n  \"RWA001\": \"0x8F9A8cbBdfb93b72d646c8DEd6B4Fe4D86B315cB\",\n  \"PIP_RWA001\": \"0x09710C9440e5FF5c473efe61d5a2f14cA05A6752\",\n  \"MCD_JOIN_RWA001_A\": \"0x029A554f252373e146f76Fa1a7455f73aBF4d38e\",\n  \"RWA001_A_URN\": \"0x3Ba90D86f7E3218C48b7E0FCa959EcF43d9A30F4\",\n  \"RWA001_A_INPUT_CONDUIT\": \"0xB944B07EC3B680b2cEA753125667F7663d424DC3\",\n  \"RWA001_A_OUTPUT_CONDUIT\": \"0xc54fEee07421EAB8000AC8c921c0De9DbfbE780B\",\n  \"RWA002\": \"0xea8a2f6DC9236edb3f53744f5019a444e24F4379\",\n  \"PIP_RWA002\": \"0xaD6495E5918C5F66650EDf291C97b31aBaf5Cd7B\",\n  \"MCD_JOIN_RWA002_A\": \"0x3B3fAD77D6977a19cc7B156143056a3E9C6Ca329\",\n  \"RWA002_A_URN\": \"0xc615F4188C255445290fB9E6dB5E021fe4CA8ECf\",\n  \"RWA002_A_INPUT_CONDUIT\": \"0x2CfADbd094a4D650049C53832B15842a3c59Db34\",\n  \"RWA002_A_OUTPUT_CONDUIT\": \"0x2CfADbd094a4D650049C53832B15842a3c59Db34\",\n  \"RWA003\": \"0xDBC559F5058E593981C48f4f09fA34323df42d51\",\n  \"PIP_RWA003\": \"0xA6f7FBeCef878a8B0Fa9AcB214040e962840f209\",\n  \"MCD_JOIN_RWA003_A\": \"0x4CCc7fED3912A32B6Cf7Db2FdA1554a9FF574099\",\n  \"RWA003_A_URN\": \"0x993c239179D6858769996bcAb5989ab2DF75913F\",\n  \"RWA003_A_INPUT_CONDUIT\": \"0x45e17E350279a2f28243983053B634897BA03b64\",\n  \"RWA003_A_OUTPUT_CONDUIT\": \"0x45e17E350279a2f28243983053B634897BA03b64\",\n  \"RWA004\": \"0x146b0abaB80a60Bfa3b4fDDb5056bBcFa4f1fec1\",\n  \"PIP_RWA004\": \"0xF8E535B9C1c230342EAdD1fe2636a872BdC3d8b4\",\n  \"MCD_JOIN_RWA004_A\": \"0xa92D4082BabF785Ba02f9C419509B7d08f2ef271\",\n  \"RWA004_A_URN\": \"0xf22C7F5A2AecE1E85263e3cec522BDCD3e392B59\",\n  \"RWA004_A_INPUT_CONDUIT\": \"0x303dFE04Be5731207c5213FbB54488B3aD9B9FE3\",\n  \"RWA004_A_OUTPUT_CONDUIT\": \"0x303dFE04Be5731207c5213FbB54488B3aD9B9FE3\",\n  \"RWA005\": \"0xcB2A48D26970eE7193d66BAc6F1b3090f2E8f82B\",\n  \"PIP_RWA005\": \"0x0dA25CE01BA1eCBD907694D5237Df0A2740Ce9E7\",\n  \"MCD_JOIN_RWA005_A\": \"0x1233d0DBb55A4Bb41D711d4B584f8DDB15A2Ff88\",\n  \"RWA005_A_URN\": \"0xdB9f0700EbBac596CCeF5b14D5e23664Db2A184f\",\n  \"RWA005_A_INPUT_CONDUIT\": \"0x17E5954Cdd3611Dd84e444F0ed555CC3a06cB319\",\n  \"RWA005_A_OUTPUT_CONDUIT\": \"0x17E5954Cdd3611Dd84e444F0ed555CC3a06cB319\",\n  \"RWA006\": \"0x4E65F06574F1630B4fF756C898Fe02f276D53E86\",\n  \"PIP_RWA006\": \"0x8A4B5c3fDe49486CDEda4D7Fbb4dec6CC6Af8258\",\n  \"MCD_JOIN_RWA006_A\": \"0x039B74bD0Adc35046B67E88509900D41b9D95430\",\n  \"RWA006_A_URN\": \"0x6fa6F9C11f5F129f6ECA4B391D9d32038A9666cD\",\n  \"RWA006_A_INPUT_CONDUIT\": \"0x652A3B3b91459504A8D1d785B0c923A34D638218\",\n  \"RWA006_A_OUTPUT_CONDUIT\": \"0x652A3B3b91459504A8D1d785B0c923A34D638218\",\n  \"PROXY_PAUSE_ACTIONS\": \"0x7c52826c1efEAE3199BDBe68e3916CC3eA222E29\",\n  \"PROXY_DEPLOYER\": \"0xA9fCcB07DD3f774d5b9d02e99DE1a27f47F91189\",\n  \"MCD_FLASH\": \"0x5aA1323f61D679E52a90120DFDA2ed1A76E4475A\",\n  \"VOTE_DELEGATE_PROXY_FACTORY\": \"0x1740F3bD55b1900C816A0071F8972C201566e3a3\"\n}\n"
  },
  {
    "path": "config/mainnet-addresses.json",
    "content": "{\n  \"CHANGELOG\": \"0xdA0Ab1e0017DEbCd72Be8599041a2aa3bA7e740F\",\n  \"MULTICALL\": \"0x5e227AD1969Ea493B43F840cfF78d08a6fc17796\",\n  \"FAUCET\": \"0x0000000000000000000000000000000000000000\",\n  \"MCD_DEPLOY\": \"0xbaa65281c2FA2baAcb2cb550BA051525A480D3F4\",\n  \"FLIP_FAB\": \"0x4ACdbe9dd0d00b36eC2050E805012b8Fc9974f2b\",\n  \"CLIP_FAB\": \"0x0716F25fBaAae9b63803917b6125c10c313dF663\",\n  \"CALC_FAB\": \"0xE1820A2780193d74939CcA104087CADd6c1aA13A\",\n  \"LERP_FAB\": \"0x9175561733D138326FDeA86CdFdF53e92b588276\",\n  \"JOIN_FAB\": \"0xf1738d22140783707Ca71CB3746e0dc7Bf2b0264\",\n  \"MCD_GOV\": \"0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2\",\n  \"GOV_GUARD\": \"0x6eEB68B2C7A918f36B78E2DB80dcF279236DDFb8\",\n  \"MCD_ADM\": \"0x0a3f6849f78076aefaDf113F5BED87720274dDC0\",\n  \"VOTE_PROXY_FACTORY\": \"0x6FCD258af181B3221073A96dD90D1f7AE7eEc408\",\n  \"VOTE_DELEGATE_PROXY_FACTORY\": \"0xD897F108670903D1d6070fcf818f9db3615AF272\",\n  \"MCD_VAT\": \"0x35D1b3F3D7966A1DFe207aa4514C12a259A0492B\",\n  \"MCD_JUG\": \"0x19c0976f590D67707E62397C87829d896Dc0f1F1\",\n  \"MCD_CAT\": \"0xa5679C04fc3d9d8b0AaB1F0ab83555b301cA70Ea\",\n  \"MCD_DOG\": \"0x135954d155898D42C90D2a57824C690e0c7BEf1B\",\n  \"MCD_VOW\": \"0xA950524441892A31ebddF91d3cEEFa04Bf454466\",\n  \"MCD_JOIN_DAI\": \"0x9759A6Ac90977b93B58547b4A71c78317f391A28\",\n  \"MCD_FLAP\": \"0xC4269cC7acDEdC3794b221aA4D9205F564e27f0d\",\n  \"MCD_FLOP\": \"0xA41B6EF151E06da0e34B009B86E828308986736D\",\n  \"MCD_PAUSE\": \"0xbE286431454714F511008713973d3B053A2d38f3\",\n  \"MCD_PAUSE_PROXY\": \"0xBE8E3e3618f7474F8cB1d074A26afFef007E98FB\",\n  \"MCD_GOV_ACTIONS\": \"0x4F5f0933158569c026d617337614d00Ee6589B6E\",\n  \"MCD_DAI\": \"0x6B175474E89094C44Da98b954EedeAC495271d0F\",\n  \"MCD_SPOT\": \"0x65C79fcB50Ca1594B025960e539eD7A9a6D434A3\",\n  \"MCD_POT\": \"0x197E90f9FAD81970bA7976f33CbD77088E5D7cf7\",\n  \"MCD_END\": \"0xBB856d1742fD182a90239D7AE85706C2FE4e5922\",\n  \"MCD_ESM\": \"0x29CfBd381043D00a98fD9904a431015Fef07af2f\",\n  \"PROXY_ACTIONS\": \"0x82ecD135Dce65Fbc6DbdD0e4237E0AF93FFD5038\",\n  \"PROXY_ACTIONS_END\": \"0x7AfF9FC9faD225e3c88cDA06BC56d8Aca774bC57\",\n  \"PROXY_ACTIONS_DSR\": \"0x07ee93aEEa0a36FfF2A9B95dd22Bd6049EE54f26\",\n  \"CDP_MANAGER\": \"0x5ef30b9986345249bc32d8928B7ee64DE9435E39\",\n  \"DSR_MANAGER\": \"0x373238337Bfe1146fb49989fc222523f83081dDb\",\n  \"GET_CDPS\": \"0x36a724Bd100c39f0Ea4D3A20F7097eE01A8Ff573\",\n  \"ILK_REGISTRY\": \"0x5a464C28D19848f44199D003BeF5ecc87d090F87\",\n  \"OSM_MOM\": \"0x76416A4d5190d071bfed309861527431304aA14f\",\n  \"FLIPPER_MOM\": \"0xc4bE7F74Ee3743bDEd8E0fA218ee5cf06397f472\",\n  \"CLIPPER_MOM\": \"0x79FBDF16b366DFb14F66cE4Ac2815Ca7296405A0\",\n  \"MCD_IAM_AUTO_LINE\": \"0xC7Bdd1F2B16447dcf3dE045C4a039A60EC2f0ba3\",\n  \"MCD_FLASH\": \"0x1EB4CF3A948E7D72A198fe073cCb8C7a948cD853\",\n  \"PROXY_FACTORY\": \"0xA26e15C895EFc0616177B7c1e7270A4C7D51C997\",\n  \"PROXY_REGISTRY\": \"0x4678f0a6958e4D2Bc4F1BAF7Bc52E8F3564f3fE4\",\n  \"MCD_VEST_DAI\": \"0x2Cc583c0AaCDaC9e23CB601fDA8F1A0c56Cdcb71\",\n  \"MCD_VEST_MKR\": \"0x0fC8D4f2151453ca0cA56f07359049c8f07997Bd\",\n  \"MCD_VEST_MKR_TREASURY\": \"0x6D635c8d08a1eA2F1687a5E46b666949c977B7dd\",\n  \"ETH\": \"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2\",\n  \"PIP_ETH\": \"0x81FE72B5A8d1A857d176C3E7d5Bd2679A9B85763\",\n  \"MCD_JOIN_ETH_A\": \"0x2F0b23f53734252Bda2277357e97e1517d6B042A\",\n  \"MCD_CLIP_ETH_A\": \"0xc67963a226eddd77B91aD8c421630A1b0AdFF270\",\n  \"MCD_CLIP_CALC_ETH_A\": \"0x7d9f92DAa9254Bbd1f479DBE5058f74C2381A898\",\n  \"MCD_JOIN_ETH_B\": \"0x08638eF1A205bE6762A8b935F5da9b700Cf7322c\",\n  \"MCD_CLIP_ETH_B\": \"0x71eb894330e8a4b96b8d6056962e7F116F50e06F\",\n  \"MCD_CLIP_CALC_ETH_B\": \"0x19E26067c4a69B9534adf97ED8f986c49179dE18\",\n  \"MCD_JOIN_ETH_C\": \"0xF04a5cC80B1E94C69B48f5ee68a08CD2F09A7c3E\",\n  \"MCD_CLIP_ETH_C\": \"0xc2b12567523e3f3CBd9931492b91fe65b240bc47\",\n  \"MCD_CLIP_CALC_ETH_C\": \"0x1c4fC274D12b2e1BBDF97795193D3148fCDa6108\",\n  \"BAT\": \"0x0D8775F648430679A709E98d2b0Cb6250d2887EF\",\n  \"PIP_BAT\": \"0xB4eb54AF9Cc7882DF0121d26c5b97E802915ABe6\",\n  \"MCD_JOIN_BAT_A\": \"0x3D0B1912B66114d4096F48A8CEe3A56C231772cA\",\n  \"MCD_CLIP_BAT_A\": \"0x3D22e6f643e2F4c563fD9db22b229Cbb0Cd570fb\",\n  \"MCD_CLIP_CALC_BAT_A\": \"0x2e118153D304a0d9C5838D5FCb70CEfCbEc81DC2\",\n  \"USDC\": \"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48\",\n  \"PIP_USDC\": \"0x77b68899b99b686F415d074278a9a16b336085A0\",\n  \"MCD_JOIN_USDC_A\": \"0xA191e578a6736167326d05c119CE0c90849E84B7\",\n  \"MCD_CLIP_USDC_A\": \"0x046b1A5718da6A226D912cFd306BA19980772908\",\n  \"MCD_CLIP_CALC_USDC_A\": \"0x0FCa4ba0B80123b5d22dD3C8BF595F3E561d594D\",\n  \"MCD_JOIN_USDC_B\": \"0x2600004fd1585f7270756DDc88aD9cfA10dD0428\",\n  \"MCD_CLIP_USDC_B\": \"0x5590F23358Fe17361d7E4E4f91219145D8cCfCb3\",\n  \"MCD_CLIP_CALC_USDC_B\": \"0xD6FE411284b92d309F79e502Dd905D7A3b02F561\",\n  \"WBTC\": \"0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599\",\n  \"PIP_WBTC\": \"0xf185d0682d50819263941e5f4EacC763CC5C6C42\",\n  \"MCD_JOIN_WBTC_A\": \"0xBF72Da2Bd84c5170618Fbe5914B0ECA9638d5eb5\",\n  \"MCD_CLIP_WBTC_A\": \"0x0227b54AdbFAEec5f1eD1dFa11f54dcff9076e2C\",\n  \"MCD_CLIP_CALC_WBTC_A\": \"0x5f4CEa97ca1030C6Bd38429c8a0De7Cd4981C70A\",\n  \"MCD_JOIN_WBTC_B\": \"0xfA8c996e158B80D77FbD0082BB437556A65B96E0\",\n  \"MCD_CLIP_WBTC_B\": \"0xe30663C6f83A06eDeE6273d72274AE24f1084a22\",\n  \"MCD_CLIP_CALC_WBTC_B\": \"0xeb911E99D7ADD1350DC39d84D60835BA9B287D96\",\n  \"MCD_JOIN_WBTC_C\": \"0x7f62f9592b823331E012D3c5DdF2A7714CfB9de2\",\n  \"MCD_CLIP_WBTC_C\": \"0x39F29773Dcb94A32529d0612C6706C49622161D1\",\n  \"MCD_CLIP_CALC_WBTC_C\": \"0x4fa2A328E7f69D023fE83454133c273bF5ACD435\",\n  \"TUSD\": \"0x0000000000085d4780B73119b644AE5ecd22b376\",\n  \"PIP_TUSD\": \"0xeE13831ca96d191B688A670D47173694ba98f1e5\",\n  \"MCD_JOIN_TUSD_A\": \"0x4454aF7C8bb9463203b66C816220D41ED7837f44\",\n  \"MCD_CLIP_TUSD_A\": \"0x0F6f88f8A4b918584E3539182793a0C276097f44\",\n  \"MCD_CLIP_CALC_TUSD_A\": \"0x059acdf311E38aAF77139638228d393Ff27639bF\",\n  \"ZRX\": \"0xE41d2489571d322189246DaFA5ebDe1F4699F498\",\n  \"PIP_ZRX\": \"0x7382c066801E7Acb2299aC8562847B9883f5CD3c\",\n  \"MCD_JOIN_ZRX_A\": \"0xc7e8Cd72BDEe38865b4F5615956eF47ce1a7e5D0\",\n  \"MCD_CLIP_ZRX_A\": \"0xdc90d461E148552387f3aB3EBEE0Bdc58Aa16375\",\n  \"MCD_CLIP_CALC_ZRX_A\": \"0xebe5e9D77b9DBBA8907A197f4c2aB00A81fb0C4e\",\n  \"KNC\": \"0xdd974D5C2e2928deA5F71b9825b8b646686BD200\",\n  \"PIP_KNC\": \"0xf36B79BD4C0904A5F350F1e4f776B81208c13069\",\n  \"MCD_JOIN_KNC_A\": \"0x475F1a89C1ED844A08E8f6C50A00228b5E59E4A9\",\n  \"MCD_CLIP_KNC_A\": \"0x006Aa3eB5E666D8E006aa647D4afAB212555Ddea\",\n  \"MCD_CLIP_CALC_KNC_A\": \"0x82c41e2ADE28C066a5D3A1E3f5B444a4075C1584\",\n  \"MANA\": \"0x0F5D2fB29fb7d3CFeE444a200298f468908cC942\",\n  \"PIP_MANA\": \"0x8067259EA630601f319FccE477977E55C6078C13\",\n  \"MCD_JOIN_MANA_A\": \"0xA6EA3b9C04b8a38Ff5e224E7c3D6937ca44C0ef9\",\n  \"MCD_CLIP_MANA_A\": \"0xF5C8176E1eB0915359E46DEd16E52C071Bb435c0\",\n  \"MCD_CLIP_CALC_MANA_A\": \"0xABbCd14FeDbb2D39038327055D9e615e178Fd64D\",\n  \"USDT\": \"0xdAC17F958D2ee523a2206206994597C13D831ec7\",\n  \"PIP_USDT\": \"0x7a5918670B0C390aD25f7beE908c1ACc2d314A3C\",\n  \"MCD_JOIN_USDT_A\": \"0x0Ac6A1D74E84C2dF9063bDDc31699FF2a2BB22A2\",\n  \"MCD_CLIP_USDT_A\": \"0xFC9D6Dd08BEE324A5A8B557d2854B9c36c2AeC5d\",\n  \"MCD_CLIP_CALC_USDT_A\": \"0x1Cf3DE6D570291CDB88229E70037d1705d5be748\",\n  \"PAXUSD\": \"0x8E870D67F660D95d5be530380D0eC0bd388289E1\",\n  \"PAX\": \"0x8E870D67F660D95d5be530380D0eC0bd388289E1\",\n  \"PIP_PAXUSD\": \"0x043B963E1B2214eC90046167Ea29C2c8bDD7c0eC\",\n  \"PIP_PAX\": \"0x043B963E1B2214eC90046167Ea29C2c8bDD7c0eC\",\n  \"MCD_JOIN_PAXUSD_A\": \"0x7e62B7E279DFC78DEB656E34D6a435cC08a44666\",\n  \"MCD_CLIP_PAXUSD_A\": \"0xBCb396Cd139D1116BD89562B49b9D1d6c25378B0\",\n  \"MCD_CLIP_CALC_PAXUSD_A\": \"0xAB98De83840b8367046383D2Adef9959E130923e\",\n  \"COMP\": \"0xc00e94Cb662C3520282E6f5717214004A7f26888\",\n  \"PIP_COMP\": \"0xBED0879953E633135a48a157718Aa791AC0108E4\",\n  \"MCD_JOIN_COMP_A\": \"0xBEa7cDfB4b49EC154Ae1c0D731E4DC773A3265aA\",\n  \"MCD_CLIP_COMP_A\": \"0x2Bb690931407DCA7ecE84753EA931ffd304f0F38\",\n  \"MCD_CLIP_CALC_COMP_A\": \"0x1f546560EAa70985d962f1562B65D4B182341a63\",\n  \"LRC\": \"0xBBbbCA6A901c926F240b89EacB641d8Aec7AEafD\",\n  \"PIP_LRC\": \"0x9eb923339c24c40Bef2f4AF4961742AA7C23EF3a\",\n  \"MCD_JOIN_LRC_A\": \"0x6C186404A7A238D3d6027C0299D1822c1cf5d8f1\",\n  \"MCD_CLIP_LRC_A\": \"0x81C5CDf4817DBf75C7F08B8A1cdaB05c9B3f70F7\",\n  \"MCD_CLIP_CALC_LRC_A\": \"0x6856CCA4c881CAf29B6563bA046C7Bb73121fb9d\",\n  \"LINK\": \"0x514910771AF9Ca656af840dff83E8264EcF986CA\",\n  \"PIP_LINK\": \"0x9B0C694C6939b5EA9584e9b61C7815E8d97D9cC7\",\n  \"MCD_JOIN_LINK_A\": \"0xdFccAf8fDbD2F4805C174f856a317765B49E4a50\",\n  \"MCD_CLIP_LINK_A\": \"0x832Dd5f17B30078a5E46Fdb8130A68cBc4a74dC0\",\n  \"MCD_CLIP_CALC_LINK_A\": \"0x7B1696677107E48B152e9Bf400293e98B7D86Eb1\",\n  \"BAL\": \"0xba100000625a3754423978a60c9317c58a424e3D\",\n  \"PIP_BAL\": \"0x3ff860c0F28D69F392543A16A397D0dAe85D16dE\",\n  \"MCD_JOIN_BAL_A\": \"0x4a03Aa7fb3973d8f0221B466EefB53D0aC195f55\",\n  \"MCD_CLIP_BAL_A\": \"0x6AAc067bb903E633A422dE7BE9355E62B3CE0378\",\n  \"MCD_CLIP_CALC_BAL_A\": \"0x79564a41508DA86721eDaDac07A590b5A51B2c01\",\n  \"YFI\": \"0x0bc529c00C6401aEF6D220BE8C6Ea1667F6Ad93e\",\n  \"PIP_YFI\": \"0x5F122465bCf86F45922036970Be6DD7F58820214\",\n  \"MCD_JOIN_YFI_A\": \"0x3ff33d9162aD47660083D7DC4bC02Fb231c81677\",\n  \"MCD_CLIP_YFI_A\": \"0x9daCc11dcD0aa13386D295eAeeBBd38130897E6f\",\n  \"MCD_CLIP_CALC_YFI_A\": \"0x1f206d7916Fd3B1b5B0Ce53d5Cab11FCebc124DA\",\n  \"GUSD\": \"0x056Fd409E1d7A124BD7017459dFEa2F387b6d5Cd\",\n  \"PIP_GUSD\": \"0xf45Ae69CcA1b9B043dAE2C83A5B65Bc605BEc5F5\",\n  \"MCD_JOIN_GUSD_A\": \"0xe29A14bcDeA40d83675aa43B72dF07f649738C8b\",\n  \"MCD_CLIP_GUSD_A\": \"0xa47D68b9dB0A0361284fA04BA40623fcBd1a263E\",\n  \"MCD_CLIP_CALC_GUSD_A\": \"0xF7e80359Cb9C4E6D178E6689eD8A6A6f91060747\",\n  \"UNI\": \"0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984\",\n  \"PIP_UNI\": \"0xf363c7e351C96b910b92b45d34190650df4aE8e7\",\n  \"MCD_JOIN_UNI_A\": \"0x3BC3A58b4FC1CbE7e98bB4aB7c99535e8bA9b8F1\",\n  \"MCD_CLIP_UNI_A\": \"0x3713F83Ee6D138Ce191294C131148176015bC29a\",\n  \"MCD_CLIP_CALC_UNI_A\": \"0xeA7FE6610e6708E2AFFA202948cA19ace3F580AE\",\n  \"RENBTC\": \"0xEB4C2781e4ebA804CE9a9803C67d0893436bB27D\",\n  \"PIP_RENBTC\": \"0xf185d0682d50819263941e5f4EacC763CC5C6C42\",\n  \"MCD_JOIN_RENBTC_A\": \"0xFD5608515A47C37afbA68960c1916b79af9491D0\",\n  \"MCD_CLIP_RENBTC_A\": \"0x834719BEa8da68c46484E001143bDDe29370a6A3\",\n  \"MCD_CLIP_CALC_RENBTC_A\": \"0xcC89F368aad8D424d3e759c1525065e56019a0F4\",\n  \"AAVE\": \"0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9\",\n  \"PIP_AAVE\": \"0x8Df8f06DC2dE0434db40dcBb32a82A104218754c\",\n  \"MCD_JOIN_AAVE_A\": \"0x24e459F61cEAa7b1cE70Dbaea938940A7c5aD46e\",\n  \"MCD_CLIP_AAVE_A\": \"0x8723b74F598DE2ea49747de5896f9034CC09349e\",\n  \"MCD_CLIP_CALC_AAVE_A\": \"0x76024a8EfFCFE270e089964a562Ece6ea5f3a14C\",\n  \"MATIC\": \"0x7D1AfA7B718fb893dB30A3aBc0Cfc608AaCfeBB0\",\n  \"PIP_MATIC\": \"0x8874964279302e6d4e523Fb1789981C39a1034Ba\",\n  \"MCD_JOIN_MATIC_A\": \"0x885f16e177d45fC9e7C87e1DA9fd47A9cfcE8E13\",\n  \"MCD_CLIP_MATIC_A\": \"0x29342F530ed6120BDB219D602DaFD584676293d1\",\n  \"MCD_CLIP_CALC_MATIC_A\": \"0xdF8C347B06a31c6ED11f8213C2366348BFea68dB\",\n  \"STETH\": \"0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84\",\n  \"WSTETH\": \"0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0\",\n  \"PIP_WSTETH\": \"0xFe7a2aC0B945f12089aEEB6eCebf4F384D9f043F\",\n  \"MCD_JOIN_WSTETH_A\": \"0x10CD5fbe1b404B7E19Ef964B63939907bdaf42E2\",\n  \"MCD_CLIP_WSTETH_A\": \"0x49A33A28C4C7D9576ab28898F4C9ac7e52EA457A\",\n  \"MCD_CLIP_CALC_WSTETH_A\": \"0x15282b886675cc1Ce04590148f456428E87eaf13\",\n  \"RETH\":\"0xae78736Cd615f374D3085123A210448E74Fc6393\",\n  \"PIP_RETH\":\"0xeE7F0b350aA119b3d05DC733a4621a81972f7D47\",\n  \"MCD_JOIN_RETH_A\":\"0xC6424e862f1462281B0a5FAc078e4b63006bDEBF\",\n  \"MCD_CLIP_CALC_RETH_A\":\"0xc59B62AFC96cf9737F717B5e5815070C0f154396\",\n  \"MCD_CLIP_RETH_A\":\"0x27CA5E525ea473eD52Ea9423CD08cCc081d96a98\",\n  \"GNO\":\"0x6810e776880C02933D47DB1b9fc05908e5386b96\",\n  \"PIP_GNO\":\"0xd800ca44fFABecd159c7889c3bf64a217361AEc8\",\n  \"MCD_JOIN_GNO_A\":\"0x7bD3f01e24E0f0838788bC8f573CEA43A80CaBB5\",\n  \"MCD_CLIP_CALC_GNO_A\":\"0x17b6D0e4237ea7F880aF5F58257cd232a04171D9\",\n  \"MCD_CLIP_GNO_A\":\"0xd9e758bd239e5d568f44D0A748633f6a8d52CBbb\",\n  \"UNIV2DAIETH\": \"0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11\",\n  \"PIP_UNIV2DAIETH\": \"0xFc8137E1a45BAF0030563EC4F0F851bd36a85b7D\",\n  \"MCD_JOIN_UNIV2DAIETH_A\": \"0x2502F65D77cA13f183850b5f9272270454094A08\",\n  \"MCD_CLIP_UNIV2DAIETH_A\": \"0x9F6981bA5c77211A34B76c6385c0f6FA10414035\",\n  \"MCD_CLIP_CALC_UNIV2DAIETH_A\": \"0xf738C272D648Cc4565EaFb43c0C5B35BbA3bf29d\",\n  \"UNIV2WBTCETH\": \"0xBb2b8038a1640196FbE3e38816F3e67Cba72D940\",\n  \"PIP_UNIV2WBTCETH\": \"0x8400D2EDb8B97f780356Ef602b1BdBc082c2aD07\",\n  \"MCD_JOIN_UNIV2WBTCETH_A\": \"0xDc26C9b7a8fe4F5dF648E314eC3E6Dc3694e6Dd2\",\n  \"MCD_CLIP_UNIV2WBTCETH_A\": \"0xb15afaB996904170f87a64Fe42db0b64a6F75d24\",\n  \"MCD_CLIP_CALC_UNIV2WBTCETH_A\": \"0xC94ee71e909DbE08d63aA9e6EFbc9976751601B4\",\n  \"UNIV2USDCETH\": \"0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc\",\n  \"PIP_UNIV2USDCETH\": \"0xf751f24DD9cfAd885984D1bA68860F558D21E52A\",\n  \"MCD_JOIN_UNIV2USDCETH_A\": \"0x03Ae53B33FeeAc1222C3f372f32D37Ba95f0F099\",\n  \"MCD_CLIP_UNIV2USDCETH_A\": \"0x93AE03815BAF1F19d7F18D9116E4b637cc32A131\",\n  \"MCD_CLIP_CALC_UNIV2USDCETH_A\": \"0x022ff40643e8b94C43f0a1E54f51EF6D070AcbC4\",\n  \"UNIV2DAIUSDC\": \"0xAE461cA67B15dc8dc81CE7615e0320dA1A9aB8D5\",\n  \"PIP_UNIV2DAIUSDC\": \"0x25D03C2C928ADE19ff9f4FFECc07d991d0df054B\",\n  \"MCD_JOIN_UNIV2DAIUSDC_A\": \"0xA81598667AC561986b70ae11bBE2dd5348ed4327\",\n  \"MCD_CLIP_UNIV2DAIUSDC_A\": \"0x9B3310708af333f6F379FA42a5d09CBAA10ab309\",\n  \"MCD_CLIP_CALC_UNIV2DAIUSDC_A\": \"0xbEF2ab2aA5CC780A03bccf22AD3320c8CF35af6A\",\n  \"UNIV2ETHUSDT\": \"0x0d4a11d5EEaaC28EC3F61d100daF4d40471f1852\",\n  \"PIP_UNIV2ETHUSDT\": \"0x5f6dD5B421B8d92c59dC6D907C9271b1DBFE3016\",\n  \"MCD_JOIN_UNIV2ETHUSDT_A\": \"0x4aAD139a88D2dd5e7410b408593208523a3a891d\",\n  \"MCD_CLIP_UNIV2ETHUSDT_A\": \"0x2aC4C9b49051275AcB4C43Ec973082388D015D48\",\n  \"MCD_CLIP_CALC_UNIV2ETHUSDT_A\": \"0xA475582E3D6Ec35091EaE81da3b423C1B27fa029\",\n  \"UNIV2LINKETH\": \"0xa2107FA5B38d9bbd2C461D6EDf11B11A50F6b974\",\n  \"PIP_UNIV2LINKETH\": \"0xd7d31e62AE5bfC3bfaa24Eda33e8c32D31a1746F\",\n  \"MCD_JOIN_UNIV2LINKETH_A\": \"0xDae88bDe1FB38cF39B6A02b595930A3449e593A6\",\n  \"MCD_CLIP_UNIV2LINKETH_A\": \"0x6aa0520354d1b84e1C6ABFE64a708939529b619e\",\n  \"MCD_CLIP_CALC_UNIV2LINKETH_A\": \"0x8aCeC2d937a4A4cAF42565aFbbb05ac242134F14\",\n  \"UNIV2UNIETH\": \"0xd3d2E2692501A5c9Ca623199D38826e513033a17\",\n  \"PIP_UNIV2UNIETH\": \"0x8462A88f50122782Cc96108F476deDB12248f931\",\n  \"MCD_JOIN_UNIV2UNIETH_A\": \"0xf11a98339FE1CdE648e8D1463310CE3ccC3d7cC1\",\n  \"MCD_CLIP_UNIV2UNIETH_A\": \"0xb0ece6F5542A4577E2f1Be491A937Ccbbec8479e\",\n  \"MCD_CLIP_CALC_UNIV2UNIETH_A\": \"0xad609Ed16157014EF955C94553E40e94A09049f0\",\n  \"UNIV2WBTCDAI\": \"0x231B7589426Ffe1b75405526fC32aC09D44364c4\",\n  \"PIP_UNIV2WBTCDAI\": \"0x5bB72127a196392cf4aC00Cf57aB278394d24e55\",\n  \"MCD_JOIN_UNIV2WBTCDAI_A\": \"0xD40798267795Cbf3aeEA8E9F8DCbdBA9b5281fcC\",\n  \"MCD_CLIP_UNIV2WBTCDAI_A\": \"0x4fC53a57262B87ABDa61d6d0DB2bE7E9BE68F6b8\",\n  \"MCD_CLIP_CALC_UNIV2WBTCDAI_A\": \"0x863AEa7D2c4BF2B5Aa191B057240b6Dc29F532eB\",\n  \"UNIV2AAVEETH\": \"0xDFC14d2Af169B0D36C4EFF567Ada9b2E0CAE044f\",\n  \"PIP_UNIV2AAVEETH\": \"0x32d8416e8538Ac36272c44b0cd962cD7E0198489\",\n  \"MCD_JOIN_UNIV2AAVEETH_A\": \"0x42AFd448Df7d96291551f1eFE1A590101afB1DfF\",\n  \"MCD_CLIP_UNIV2AAVEETH_A\": \"0x854b252BA15eaFA4d1609D3B98e00cc10084Ec55\",\n  \"MCD_CLIP_CALC_UNIV2AAVEETH_A\": \"0x5396e541E1F648EC03faf338389045F1D7691960\",\n  \"UNIV2DAIUSDT\": \"0xB20bd5D04BE54f870D5C0d3cA85d82b34B836405\",\n  \"PIP_UNIV2DAIUSDT\": \"0x9A1CD705dc7ac64B50777BcEcA3529E58B1292F1\",\n  \"MCD_JOIN_UNIV2DAIUSDT_A\": \"0xAf034D882169328CAf43b823a4083dABC7EEE0F4\",\n  \"MCD_CLIP_UNIV2DAIUSDT_A\": \"0xe4B82Be84391b9e7c56a1fC821f47569B364dd4a\",\n  \"MCD_CLIP_CALC_UNIV2DAIUSDT_A\": \"0x4E88cE740F6bEa31C2b14134F6C5eB2a63104fcF\",\n  \"GUNIV3DAIUSDC1\": \"0xAbDDAfB225e10B90D798bB8A886238Fb835e2053\",\n  \"PIP_GUNIV3DAIUSDC1\": \"0x7F6d78CC0040c87943a0e0c140De3F77a273bd58\",\n  \"MCD_JOIN_GUNIV3DAIUSDC1_A\": \"0xbFD445A97e7459b0eBb34cfbd3245750Dba4d7a4\",\n  \"MCD_CLIP_GUNIV3DAIUSDC1_A\": \"0x5048c5Cd3102026472f8914557A1FD35c8Dc6c9e\",\n  \"MCD_CLIP_CALC_GUNIV3DAIUSDC1_A\": \"0x25B17065b94e3fDcD97d94A2DA29E7F77105aDd7\",\n  \"GUNIV3DAIUSDC2\": \"0x50379f632ca68D36E50cfBC8F78fe16bd1499d1e\",\n  \"PIP_GUNIV3DAIUSDC2\": \"0xcCBa43231aC6eceBd1278B90c3a44711a00F4e93\",\n  \"MCD_JOIN_GUNIV3DAIUSDC2_A\": \"0xA7e4dDde3cBcEf122851A7C8F7A55f23c0Daf335\",\n  \"MCD_CLIP_GUNIV3DAIUSDC2_A\": \"0xB55da3d3100C4eBF9De755b6DdC24BF209f6cc06\",\n  \"MCD_CLIP_CALC_GUNIV3DAIUSDC2_A\": \"0xef051Ca2A2d809ba47ee0FC8caaEd06E3D832225\",\n  \"CRVV1ETHSTETH\": \"0x06325440D014e39736583c165C2963BA99fAf14E\",\n  \"PIP_CRVV1ETHSTETH\": \"0xEa508F82728927454bd3ce853171b0e2705880D4\",\n  \"MCD_JOIN_CRVV1ETHSTETH_A\": \"0x82d8bfdb61404c796385f251654f6d7e92092b5d\",\n  \"MCD_CLIP_CRVV1ETHSTETH_A\": \"0x1926862f899410bfc19fefb8a3c69c7aed22463a\",\n  \"MCD_CLIP_CALC_CRVV1ETHSTETH_A\": \"0x8a4780acabadcae1a297b2eae5deebd7d50deeb8\",\n  \"MIP21_LIQUIDATION_ORACLE\": \"0x88f88Bb9E66241B73B84f3A6E197FbBa487b1E30\",\n  \"RWA001\": \"0x10b2aA5D77Aa6484886d8e244f0686aB319a270d\",\n  \"PIP_RWA001\": \"0x76A9f30B45F4ebFD60Ce8a1c6e963b1605f7cB6d\",\n  \"MCD_JOIN_RWA001_A\": \"0x476b81c12Dc71EDfad1F64B9E07CaA60F4b156E2\",\n  \"RWA001_A_URN\": \"0xa3342059BcDcFA57a13b12a35eD4BBE59B873005\",\n  \"RWA001_A_INPUT_CONDUIT\": \"0x486C85e2bb9801d14f6A8fdb78F5108a0fd932f2\",\n  \"RWA001_A_OUTPUT_CONDUIT\": \"0xb3eFb912e1cbC0B26FC17388Dd433Cecd2206C3d\",\n  \"RWA002\": \"0xAAA760c2027817169D7C8DB0DC61A2fb4c19AC23\",\n  \"PIP_RWA002\": \"0xd2473237E20Bd52F8E7cE0FD79403A6a82fbAEC8\",\n  \"MCD_JOIN_RWA002_A\": \"0xe72C7e90bc26c11d45dBeE736F0acf57fC5B7152\",\n  \"RWA002_A_URN\": \"0x225B3da5BE762Ee52B182157E67BeA0b31968163\",\n  \"RWA002_A_INPUT_CONDUIT\": \"0x2474F297214E5d96Ba4C81986A9F0e5C260f445D\",\n  \"RWA002_A_OUTPUT_CONDUIT\": \"0x2474F297214E5d96Ba4C81986A9F0e5C260f445D\",\n  \"RWA003\": \"0x07F0A80aD7AeB7BfB7f139EA71B3C8f7E17156B9\",\n  \"PIP_RWA003\": \"0xDeF7E88447F7D129420FC881B2a854ABB52B73B8\",\n  \"MCD_JOIN_RWA003_A\": \"0x1Fe789BBac5b141bdD795A3Bc5E12Af29dDB4b86\",\n  \"RWA003_A_URN\": \"0x7bF825718e7C388c3be16CFe9982539A7455540F\",\n  \"RWA003_A_INPUT_CONDUIT\": \"0x2A9798c6F165B6D60Cfb923Fe5BFD6f338695D9B\",\n  \"RWA003_A_OUTPUT_CONDUIT\": \"0x2A9798c6F165B6D60Cfb923Fe5BFD6f338695D9B\",\n  \"RWA004\": \"0x873F2101047A62F84456E3B2B13df2287925D3F9\",\n  \"PIP_RWA004\": \"0x5eEE1F3d14850332A75324514CcbD2DBC8Bbc566\",\n  \"MCD_JOIN_RWA004_A\": \"0xD50a8e9369140539D1c2D113c4dC1e659c6242eB\",\n  \"RWA004_A_URN\": \"0xeF1699548717aa4Cf47aD738316280b56814C821\",\n  \"RWA004_A_INPUT_CONDUIT\": \"0xe1ed3F588A98bF8a3744f4BF74Fd8540e81AdE3f\",\n  \"RWA004_A_OUTPUT_CONDUIT\": \"0xe1ed3F588A98bF8a3744f4BF74Fd8540e81AdE3f\",\n  \"RWA005\": \"0x6DB236515E90fC831D146f5829407746EDdc5296\",\n  \"PIP_RWA005\": \"0x8E6039C558738eb136833aB50271ae065c700d2B\",\n  \"MCD_JOIN_RWA005_A\": \"0xA4fD373b93aD8e054970A3d6cd4Fd4C31D08192e\",\n  \"RWA005_A_URN\": \"0xc40907545C57dB30F01a1c2acB242C7c7ACB2B90\",\n  \"RWA005_A_INPUT_CONDUIT\": \"0x5b702e1fEF3F556cbe219eE697D7f170A236cc66\",\n  \"RWA005_A_OUTPUT_CONDUIT\": \"0x5b702e1fEF3F556cbe219eE697D7f170A236cc66\",\n  \"RWA006\": \"0x4EE03cfBF6E784c462839f5954d60f7C2B60b113\",\n  \"PIP_RWA006\": \"0xB8AeCF04Fdf22Ef6C0c6b6536896e1F2870C41D3\",\n  \"MCD_JOIN_RWA006_A\": \"0x5E11E34b6745FeBa9449Ae53c185413d6EdC66BE\",\n  \"RWA006_A_URN\": \"0x0C185bf5388DdfDB288F4D875265d456D18FD9Cb\",\n  \"RWA006_A_INPUT_CONDUIT\": \"0x8Fe38D1E4293181273E2e323e4c16e0D1d4861e3\",\n  \"RWA006_A_OUTPUT_CONDUIT\": \"0x8Fe38D1E4293181273E2e323e4c16e0D1d4861e3\",\n  \"PROXY_PAUSE_ACTIONS\": \"0x6bda13D43B7EDd6CAfE1f70fB98b5d40f61A1370\",\n  \"PROXY_DEPLOYER\": \"0x1b93556AB8dcCEF01Cd7823C617a6d340f53Fb58\",\n  \"OPTIMISM_DAI_BRIDGE\": \"0x10E6593CDda8c58a1d0f14C5164B376352a55f2F\",\n  \"OPTIMISM_ESCROW\": \"0x467194771dAe2967Aef3ECbEDD3Bf9a310C76C65\",\n  \"OPTIMISM_GOV_RELAY\": \"0x09B354CDA89203BB7B3131CC728dFa06ab09Ae2F\",\n  \"ARBITRUM_DAI_BRIDGE\": \"0xD3B5b60020504bc3489D6949d545893982BA3011\",\n  \"ARBITRUM_ESCROW\": \"0xA10c7CE4b876998858b1a9E12b10092229539400\",\n  \"ARBITRUM_GOV_RELAY\": \"0x9ba25c289e351779E0D481Ba37489317c34A899d\"\n}\n"
  },
  {
    "path": "config/testnet-addresses.json",
    "content": "{\n  \"DEPLOYER\": \"0x00a329c0648769A73afAc7F9381E08FB43dBEA72\",\n  \"MULTICALL\": \"0x492934308E98b590A626666B703A6dDf2120e85e\",\n  \"FAUCET\": \"0x0A64DF94bc0E039474DB42bb52FEca0c1d540402\",\n  \"MCD_DEPLOY\": \"0xd29915F1A3fF9846fE5D8d9d2C954de21932AF7F\",\n  \"MCD_GOV\": \"0x1FD8397e8108ada12eC07976D92F773364ba46e7\",\n  \"GOV_GUARD\": \"0x39a812a6aA4C475b6562B73Bf0584eb3655e8D6C\",\n  \"MCD_IOU\": \"0xDfBc5fbEaa41bD1cd15F9d6b77265DBc3CB2A677\",\n  \"MCD_ADM\": \"0x8c39d4833812A7516BaCD455dA7F97f0a8C11B05\",\n  \"VOTE_PROXY_FACTORY\": \"0x2dC383E93ec3DB735777a3E9ae69E2aD81edaF03\",\n  \"MCD_VAT\": \"0x3D72e5B28FbA05Bd4090A2A587Bb3eCC899f33b2\",\n  \"MCD_JUG\": \"0x77a371Ed06fbA2D93D05C5bDE6d8eC58b3a35fbd\",\n  \"MCD_CAT\": \"0x31865076D1E28ad4eA06D5Db7aAa4AAF225f1Fb5\",\n  \"MCD_DOG\": \"0xd9b3B2429F1b301156Bf3103419ef6B78E888386\",\n  \"MCD_VOW\": \"0xA2F9C8C13118c88f14501cDCB2b52Af3751622ae\",\n  \"MCD_JOIN_DAI\": \"0x7f8241b7250c5C5368788543E4dA2F9A919E9F02\",\n  \"MCD_FLAP\": \"0xB5054202380d093A02916e0137d75b54D6182A23\",\n  \"MCD_FLOP\": \"0xd57D9931b305f1bc1622B97c8Cc6747E4A9254a0\",\n  \"MCD_PAUSE\": \"0x967aE1FB90aA36C7d3B16B5328504F542495D952\",\n  \"MCD_PAUSE_PROXY\": \"0x3689de8F568e4A59254eE7eaB1A37d87044f52Da\",\n  \"MCD_GOV_ACTIONS\": \"0x2287909BB95FA078C73CC2d5a5AF6fE1244b0911\",\n  \"MCD_DAI\": \"0x8A1567046e610Fec30F120BB70Df94B50561C1d3\",\n  \"MCD_SPOT\": \"0x5422Ee4e22603E336905CDC9D59aE3F0012fe4c8\",\n  \"MCD_POT\": \"0xe51e9A4D22b7451c0232508455195E7c0a6e0f19\",\n  \"MCD_END\": \"0x27547dE5f11122283825Adf97FB98eF301f5F73c\",\n  \"MCD_ESM\": \"0x8a606fD18B9f0CA3Cf480e70639c58EAa98d7389\",\n  \"PROXY_ACTIONS\": \"0x84617303947304444Ceb641582c024f277BBF4Ff\",\n  \"PROXY_ACTIONS_END\": \"0x78c362A5690447EA2BBC3E8008502efD13936F79\",\n  \"PROXY_ACTIONS_DSR\": \"0x277aD07109FE52a742B808a3E6765Ee1Ad0e7Ad2\",\n  \"CDP_MANAGER\": \"0x79a8FC3D98Fc84c9BC2B3a737EA992321a1b86A3\",\n  \"DSR_MANAGER\": \"0x0Faf2F31Ab165B55F42E55c8065c0EC7170A0d45\",\n  \"GET_CDPS\": \"0x4f05AfbC371854D027263e756487BDefD099178f\",\n  \"ILK_REGISTRY\": \"0x8e23974b151827f0E8151aC526C4c4c974c06A90\",\n  \"OSM_MOM\": \"0x96724aa934979936aE5c3Afda1599b5ed61252ce\",\n  \"FLIPPER_MOM\": \"0xcbfD09D76140D01E573b452bB984d82589571fC2\",\n  \"CLIPPER_MOM\": \"0x9BB69befBAA567a7EaEE33b671756596517338F4\",\n  \"MCD_IAM_AUTO_LINE\": \"0x01E354A7eF79962DbB690705e46bd54c1C855E80\",\n  \"PROXY_FACTORY\": \"0x3DD0864668C36D27B53a98137764c99F9FD5B7B2\",\n  \"PROXY_REGISTRY\": \"0x26C8d09E5C0B423E2827844c770F61c9af2870E7\",\n  \"ETH\": \"0xEddA486ddB7eaa8f9FEce8c682EFD40f535b3Ad5\",\n  \"VAL_ETH\": \"0xb4c79daB8f259C7Aee6E5b2Aa729821864227e84\",\n  \"MCD_JOIN_ETH_A\": \"0x9119B5d8b735E4cEbaE7386AF6cD2B863c7d35A8\",\n  \"MCD_FLIP_ETH_A\": \"0x0BD7632aF5F7020575e59E80ABbca739035Ac0EC\",\n  \"MCD_JOIN_ETH_B\": \"0xe2dD18a6000030F30ecB1237B15605533f814c59\",\n  \"MCD_CLIP_ETH_B\": \"0x3f2603979a4A185acE9B9c941193704FfBD24F4A\",\n  \"MCD_CLIP_CALC_ETH_B\": \"0x8cabea65F0140962A7D7Fe9f31a265a2B19Dc305\",\n  \"MCD_JOIN_ETH_C\": \"0x9FdC3bBD89ae1fB19054241644EF3dfbdcA85544\",\n  \"MCD_FLIP_ETH_C\": \"0xB0b7Db244994E9B922998f49b7ae61956314CA35\",\n  \"BAT\": \"0x39b4C0A63c4c16DD1816D104F2C18a296Dbd4e70\",\n  \"VAL_BAT\": \"0x62d69f6867A0A084C6d313943dC22023Bc263691\",\n  \"MCD_JOIN_BAT_A\": \"0xA616aD7D4562dCD9208425Af4038defD0a9057B0\",\n  \"MCD_FLIP_BAT_A\": \"0xB36901dB56E2fb44862a7D0eAE9F5Cf9a7E449bD\",\n  \"USDC\": \"0x32Ee2bF1267253f76298D4199095B9C6b5A389c0\",\n  \"VAL_USDC\": \"0xee35211C4D9126D520bBfeaf3cFee5FE7B86F221\",\n  \"MCD_JOIN_USDC_A\": \"0x59ea98A4b40B72140b7dc93c29c098AE607Ce20D\",\n  \"MCD_FLIP_USDC_A\": \"0xE8a124764cCcb7Ee3E8e320aAaA841Ea249197D4\",\n  \"MCD_JOIN_USDC_B\": \"0xaD1Bf7D34Fa48f7Cf7CA1CE3c7408f9151DF2745\",\n  \"MCD_FLIP_USDC_B\": \"0xC8055bC4415Ac354fA6EFbC3bcf57d5cBcc072ed\",\n  \"TUSD\": \"0xB014e899ddb9a55af72fE09E8570E700A5167b6d\",\n  \"VAL_TUSD\": \"0x7C276DcAab99BD16163c1bcce671CaD6A1ec0945\",\n  \"MCD_JOIN_TUSD_A\": \"0xAFB95880bc835B6Eeb041ce57D570D013360beC6\",\n  \"MCD_FLIP_TUSD_A\": \"0x6b0f809E52218192AbAf32C9C74F31229E07B626\",\n  \"WBTC\": \"0x123010c0Fe7D4d7420f309431bb95060393fe3B7\",\n  \"VAL_WBTC\": \"0x3f85D0b6119B38b7E6B119F7550290fec4BE0e3c\",\n  \"MCD_JOIN_WBTC_A\": \"0xf4C33a989bD0C9e9268c5bfCbCE2C8501B9dBa25\",\n  \"MCD_FLIP_WBTC_A\": \"0x53781B6DC81F5b90421371172c1b06e384858e44\",\n  \"GUSD\": \"0x0363Ef677c78bd8C8302DB33be2F6629E33E72Fe\",\n  \"VAL_GUSD\": \"0xd5F051401ca478B34C80D0B5A119e437Dc6D9df5\",\n  \"MCD_JOIN_GUSD_A\": \"0x01C957395029E9aCCbCb25a6Ab72C618252CACf9\",\n  \"MCD_FLIP_GUSD_A\": \"0x334490acE32D96808B104A1f8723cFAB5881DE46\",\n  \"PROXY_PAUSE_ACTIONS\": \"0x23263d4ebB1190A483A84e90B9a6Dd8720979284\",\n  \"PROXY_DEPLOYER\": \"0x30c8860f6a38819B59E1255A499A10bCBF4Ee747\"\n}"
  },
  {
    "path": "docker-compose.yml",
    "content": "version: \"3.2\"\nservices:\n  parity:\n    image: makerdao/testchain-pymaker:unit-testing-2.0.0\n    container_name: parity-pymaker-test\n    ports:\n      - \"8545:8545\"\n      - \"8546:8546\"\n    expose:\n      - \"8545\"\n      - \"8546\"\n    user: root\n    working_dir: /home/parity\n\n  ganache:\n    image: trufflesuite/ganache-cli:v6.9.1\n    container_name: ganache\n    ports:\n      - \"8555:8555\"\n    expose:\n      - \"8555\"\n    command: \"--gasLimit 10000000\n    -p 8555\n    --account=\\\"0x91cf2cc3671a365fcbf38010ff97ee31a5b7e674842663c56769e41600696ead,1000000000000000000000000\\\"\n    --account=\\\"0xc0a550404067ce46a51283e0cc99ec3ba832940064587147a8db9a7ba355ef27,1000000000000000000000000\\\",\n    --account=\\\"0x6ca1cfaba9715aa485504cb8a3d3fe54191e0991b5f47eb982e8fb40d1b8e8d8,1000000000000000000000000\\\",\n    --account=\\\"0x1a9e422172e3d84487f7c833e3895f2f65c35eff7e68783adaa0c5bbe741ca8a,1000000000000000000000000\\\"\"\n\n"
  },
  {
    "path": "docs/conf.py",
    "content": "# -*- coding: utf-8 -*-\n#\n# This file is execfile()d with the current directory set to its\n# containing dir.\n#\n# Note that not all possible configuration values are present in this\n# autogenerated file.\n#\n# All configuration values have a default; values that are commented out\n# serve to show the default.\n\n# If extensions (or modules to document with autodoc) are in another directory,\n# add these directories to sys.path here. If the directory is relative to the\n# documentation root, use os.path.abspath to make it absolute, like shown here.\n\nimport os\nimport sys\nsys.path.insert(0, os.path.abspath('.'))\nsys.path.insert(0, os.path.abspath('..'))\n\n\n# -- General configuration ------------------------------------------------\n\n# If your documentation needs a minimal Sphinx version, state it here.\n#\n# needs_sphinx = '1.0'\n\n# Add any Sphinx extension module names here, as strings. They can be\n# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom\n# ones.\nextensions = ['sphinx.ext.autodoc', 'sphinx.ext.napoleon']\n\n# Add any paths that contain templates here, relative to this directory.\ntemplates_path = ['_templates']\n\n# The suffix(es) of source filenames.\n# You can specify multiple suffix as a list of string:\n#\n# source_suffix = ['.rst', '.md']\nsource_suffix = '.rst'\n\n# The master toctree document.\nmaster_doc = 'index'\n\n# General information about the project.\nproject = 'pymaker'\ncopyright = '2017, MakerDAO'\nauthor = 'MakerDAO'\n\n# The version info for the project you're documenting, acts as replacement for\n# |version| and |release|, also used in various other places throughout the\n# built documents.\n#\n# The short X.Y version.\nversion = ''\n# The full version, including alpha/beta/rc tags.\nrelease = ''\n\n# The language for content autogenerated by Sphinx. Refer to documentation\n# for a list of supported languages.\n#\n# This is also used if you do content translation via gettext catalogs.\n# Usually you set \"language\" from the command line for these cases.\nlanguage = None\n\n# List of patterns, relative to source directory, that match files and\n# directories to ignore when looking for source files.\n# This patterns also effect to html_static_path and html_extra_path\nexclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']\n\n# The name of the Pygments (syntax highlighting) style to use.\npygments_style = 'sphinx'\n\n# If true, `todo` and `todoList` produce output, else they produce nothing.\ntodo_include_todos = False\n\n\n# -- Options for HTML output ----------------------------------------------\n\n# The theme to use for HTML and HTML Help pages.  See the documentation for\n# a list of builtin themes.\n#\nhtml_theme = 'classic'\n\n# Theme options are theme-specific and customize the look and feel of a theme\n# further.  For a list of options available for each theme, see the\n# documentation.\n#\n# html_theme_options = {}\n\n# Add any paths that contain custom static files (such as style sheets) here,\n# relative to this directory. They are copied after the builtin static files,\n# so a file named \"default.css\" will overwrite the builtin \"default.css\".\n# html_static_path = ['_static']\n\n\n# -- Options for HTMLHelp output ------------------------------------------\n\n# Output file base name for HTML help builder.\nhtmlhelp_basename = 'pymaker'\n\n\n# -- Options for LaTeX output ---------------------------------------------\n\nlatex_elements = {\n    # The paper size ('letterpaper' or 'a4paper').\n    #\n    # 'papersize': 'letterpaper',\n\n    # The font size ('10pt', '11pt' or '12pt').\n    #\n    # 'pointsize': '10pt',\n\n    # Additional stuff for the LaTeX preamble.\n    #\n    # 'preamble': '',\n\n    # Latex figure (float) alignment\n    #\n    # 'figure_align': 'htbp',\n}\n\n# Grouping the document tree into LaTeX files. List of tuples\n# (source start file, target name, title,\n#  author, documentclass [howto, manual, or own class]).\nlatex_documents = [\n    (master_doc, 'pymaker.py.tex', 'pymaker Documentation',\n     'MakerDAO', 'manual'),\n]\n\n\n# -- Options for manual page output ---------------------------------------\n\n# One entry per manual page. List of tuples\n# (source start file, name, description, authors, manual section).\nman_pages = [\n    (master_doc, 'pymaker', 'pymaker Documentation',\n     [author], 1)\n]\n\n\n# -- Options for Texinfo output -------------------------------------------\n\n# Grouping the document tree into Texinfo files. List of tuples\n# (source start file, target name, title, author,\n#  dir menu entry, description, category)\ntexinfo_documents = [\n    (master_doc, 'pymaker', 'pymaker Documentation',\n     author, 'pymaker', 'One line description of project.',\n     'Miscellaneous'),\n]\n\n\n\n"
  },
  {
    "path": "docs/index.rst",
    "content": "pymaker API\n===========\n\nThe `pymaker` API exists to provide a simple way of interacting with Maker smart contracts.\n\nIt was designed to simplify and facilitate creation of external profit-seeking agents, usually called keepers,\nthat operate around the stablecoin set of smart contracts. The API can also be used to automate certain tasks for\nother entities involved in the platform, like DAI issuers or traders.\n\n\nGeneral\n-------\n\nAddress\n~~~~~~~\n\n.. autoclass:: pymaker.Address\n    :members:\n\nTransact\n~~~~~~~~\n\n.. autoclass:: pymaker.Transact\n    :members:\n\nCalldata\n~~~~~~~~\n\n.. autoclass:: pymaker.Calldata\n    :members:\n\nInvocation\n~~~~~~~~~~\n\n.. autoclass:: pymaker.Invocation\n    :members:\n\nReceipt\n~~~~~~~\n\n.. autoclass:: pymaker.Receipt\n    :members:\n\nTransfer\n~~~~~~~~\n\n.. autoclass:: pymaker.Transfer\n    :members:\n\n\nNumeric types\n-------------\n\nMost of the numeric data throughout the entire platform is kept as either `Wad` (18-digit precision type)\nor `Ray` (27-digit precision type).\n\nWad\n~~~\n\n.. autoclass:: pymaker.numeric.Wad\n    :members:\n\nRay\n~~~\n\n.. autoclass:: pymaker.numeric.Ray\n    :members:\n\n\nGas price\n---------\n\n.. autoclass:: pymaker.gas.GasPrice\n    :members:\n\nThe following implementations of `GasPrice` are available:\n\nDefaultGasPrice\n~~~~~~~~~~~~~~~\n\n.. autoclass:: pymaker.gas.DefaultGasPrice\n    :members:\n\nFixedGasPrice\n~~~~~~~~~~~~~\n\n.. autoclass:: pymaker.gas.FixedGasPrice\n    :members:\n\nIncreasingGasPrice\n~~~~~~~~~~~~~~~~~~\n\n.. autoclass:: pymaker.gas.IncreasingGasPrice\n    :members:\n\n\nApprovals\n---------\n\n.. automodule:: pymaker.approval\n    :members:\n\n\nContracts\n---------\n\nDAI Stablecoin\n~~~~~~~~~~~~~~\n\nTub\n\"\"\"\n\n.. autoclass:: pymaker.sai.Tub\n    :members:\n\nTap\n\"\"\"\n\n.. autoclass:: pymaker.sai.Tap\n    :members:\n\nTop\n\"\"\"\n\n.. autoclass:: pymaker.sai.Top\n    :members:\n\nVox\n\"\"\"\n\n.. autoclass:: pymaker.sai.Vox\n    :members:\n\nERC20\n~~~~~\n\nERC20Token\n\"\"\"\"\"\"\"\"\"\"\n\n.. autoclass:: pymaker.token.ERC20Token\n    :members:\n\nDSToken\n\"\"\"\"\"\"\"\n\n.. autoclass:: pymaker.token.DSToken\n    :members:\n\nDSEthToken\n\"\"\"\"\"\"\"\"\"\"\n\n.. autoclass:: pymaker.token.DSEthToken\n    :members:\n\n\nExchanges\n~~~~~~~~~\n\n`OaaisDEX`, `EtherDelta` and `0x` are decentralized exchanges which also provide some arbitrage opportunities\nfor profit-seeking agents. Because of that an API has been created around them as well. Also an API for\nthe `Bibox` centralized exchange is present.\n\nOasisDEX\n\"\"\"\"\"\"\"\"\n\n.. automodule:: pymaker.oasis\n    :members:\n\nEtherDelta\n\"\"\"\"\"\"\"\"\"\"\n\n.. automodule:: pymaker.etherdelta\n    :members:\n\n0x\n\"\"\n\n.. automodule:: pymaker.zrx\n    :members:\n\nBibox\n\"\"\"\"\"\n\n.. automodule:: pymaker.bibox\n    :members:\n\n\nAuthentication\n~~~~~~~~~~~~~~\n\nDSGuard\n\"\"\"\"\"\"\"\n\n.. autoclass:: pymaker.auth.DSGuard\n    :members:\n\n\nDSValue\n~~~~~~~\n\n.. autoclass:: pymaker.feed.DSValue\n    :members:\n\nDSVault\n~~~~~~~\n\n.. autoclass:: pymaker.vault.DSVault\n    :members:\n\n\n\nAtomic transactions\n-------------------\n\nTxManager\n~~~~~~~~~\n\n.. autoclass:: pymaker.transactional.TxManager\n    :members:\n"
  },
  {
    "path": "pymaker/__init__.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2017-2018 reverendus\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nimport asyncio\nimport json\nimport logging\nimport re\nimport requests\nimport sys\nimport time\nfrom enum import Enum, auto\nfrom functools import total_ordering, wraps\nfrom threading import Lock\nfrom typing import Optional\nfrom weakref import WeakKeyDictionary\n\nimport eth_utils\nimport pkg_resources\nfrom hexbytes import HexBytes\n\nfrom web3 import HTTPProvider, Web3\nfrom web3._utils.contracts import get_function_info, encode_abi\nfrom web3._utils.events import get_event_data\nfrom web3.exceptions import TransactionNotFound\nfrom web3.middleware import geth_poa_middleware\nfrom web3.exceptions import LogTopicError, TransactionNotFound\n\nfrom eth_abi.codec import ABICodec\nfrom eth_abi.registry import registry as default_registry\n\nfrom pymaker.gas import DefaultGasPrice, GasPrice\nfrom pymaker.numeric import Wad\nfrom pymaker.util import synchronize, bytes_to_hexstring, is_contract_at\n\nfilter_threads = []\nnonce_calc = WeakKeyDictionary()\nnext_nonce = {}\ntransaction_lock = Lock()\nlogger = logging.getLogger()\n\n\ndef web3_via_http(endpoint_uri: str, timeout=60, http_pool_size=20):\n    assert isinstance(endpoint_uri, str)\n    adapter = requests.adapters.HTTPAdapter(pool_connections=http_pool_size, pool_maxsize=http_pool_size)\n    session = requests.Session()\n    if endpoint_uri.startswith(\"http\"):\n        # Mount over both existing adaptors created by default (rather than just the one which applies to our URI)\n        session.mount('http://', adapter)\n        session.mount('https://', adapter)\n    else:\n        raise ValueError(\"Unsupported protocol\")\n\n    web3 = Web3(HTTPProvider(endpoint_uri=endpoint_uri, request_kwargs={\"timeout\": timeout}, session=session))\n    if web3.net.version == \"5\":  # goerli\n        web3.middleware_onion.inject(geth_poa_middleware, layer=0)\n    return web3\n\n\nclass NonceCalculation(Enum):\n    TX_COUNT = auto()\n    PARITY_NEXTNONCE = auto()\n    SERIAL = auto()\n    PARITY_SERIAL = auto()\n\n\ndef _get_nonce_calc(web3: Web3) -> NonceCalculation:\n    assert isinstance(web3, Web3)\n    global nonce_calc\n    if web3 not in nonce_calc:\n        providers_without_nonce_calculation = ['infura', 'quiknode']\n        requires_serial_nonce = any(provider in web3.manager.provider.endpoint_uri for provider in\n                                    providers_without_nonce_calculation)\n        is_parity = \"parity\" in web3.clientVersion.lower() or \"openethereum\" in web3.clientVersion.lower()\n        if is_parity and requires_serial_nonce:\n            nonce_calc[web3] = NonceCalculation.PARITY_SERIAL\n        elif requires_serial_nonce:\n            nonce_calc[web3] = NonceCalculation.SERIAL\n        elif is_parity:\n            nonce_calc[web3] = NonceCalculation.PARITY_NEXTNONCE\n        else:\n            nonce_calc[web3] = NonceCalculation.TX_COUNT\n        logger.debug(f\"node clientVersion={web3.clientVersion}, will use {nonce_calc[web3]}\")\n    return nonce_calc[web3]\n\n\ndef register_filter_thread(filter_thread):\n    filter_threads.append(filter_thread)\n\n\ndef any_filter_thread_present() -> bool:\n    return len(filter_threads) > 0\n\n\ndef all_filter_threads_alive() -> bool:\n    return all(filter_thread_alive(filter_thread) for filter_thread in filter_threads)\n\n\ndef filter_thread_alive(filter_thread) -> bool:\n    # it's a wicked way of detecting whether a web3.py filter is still working\n    # but unfortunately I wasn't able to find any other one\n    return hasattr(filter_thread, '_args') and hasattr(filter_thread, '_kwargs') or not filter_thread.is_alive()\n\n\ndef stop_all_filter_threads():\n    for filter_thread in filter_threads:\n        try:\n            filter_thread.stop_watching(timeout=60)\n        except:\n            pass\n\n\ndef _track_status(f):\n    @wraps(f)\n    async def wrapper(*args, **kwds):\n        # Check for multiple execution\n        if args[0].status != TransactStatus.NEW:\n            raise Exception(\"Each `Transact` can only be executed once\")\n\n        # Set current status to in progress\n        args[0].status = TransactStatus.IN_PROGRESS\n\n        try:\n            return await f(*args, **kwds)\n        finally:\n            args[0].status = TransactStatus.FINISHED\n\n    return wrapper\n\n\n@total_ordering\nclass Address:\n    \"\"\"Represents an Ethereum address.\n\n    Addresses get normalized automatically, so instances of this class can be safely compared to each other.\n\n    Args:\n        address: Can be any address representation allowed by web3.py\n            or another instance of the Address class.\n\n    Attributes:\n        address: Normalized hexadecimal representation of the Ethereum address.\n    \"\"\"\n    def __init__(self, address):\n        if isinstance(address, Address):\n            self.address = address.address\n        else:\n            self.address = eth_utils.to_checksum_address(address)\n\n    @staticmethod\n    def zero():\n        return Address(\"0x0000000000000000000000000000000000000000\")\n\n    def as_bytes(self) -> bytes:\n        \"\"\"Return the address as a 20-byte bytes array.\"\"\"\n        return bytes.fromhex(self.address.replace('0x', ''))\n\n    def __str__(self):\n        return f\"{self.address}\"\n\n    def __repr__(self):\n        return f\"Address('{self.address}')\"\n\n    def __hash__(self):\n        return self.address.__hash__()\n\n    def __eq__(self, other):\n        assert(isinstance(other, Address))\n        return self.address == other.address\n\n    def __lt__(self, other):\n        assert(isinstance(other, Address))\n        return self.address < other.address\n\n\nclass Contract:\n    logger = logging.getLogger()\n\n    @staticmethod\n    def _deploy(web3: Web3, abi: list, bytecode: str, args: list) -> Address:\n        assert(isinstance(web3, Web3))\n        assert(isinstance(abi, list))\n        assert(isinstance(bytecode, str))\n        assert(isinstance(args, list))\n\n        contract = web3.eth.contract(abi=abi, bytecode=bytecode)\n        tx_hash = contract.constructor(*args).transact(\n            transaction={'from': eth_utils.to_checksum_address(web3.eth.defaultAccount)})\n        receipt = web3.eth.getTransactionReceipt(tx_hash)\n        return Address(receipt['contractAddress'])\n\n    @staticmethod\n    def _get_contract(web3: Web3, abi: list, address: Address):\n        assert(isinstance(web3, Web3))\n        assert(isinstance(abi, list))\n        assert(isinstance(address, Address))\n\n        if not is_contract_at(web3, address):\n            raise Exception(f\"No contract found at {address}\")\n\n        return web3.eth.contract(abi=abi)(address=address.address)\n\n    def _past_events(self, contract, event, cls, number_of_past_blocks, event_filter) -> list:\n        block_number = contract.web3.eth.blockNumber\n        return self._past_events_in_block_range(contract, event, cls, max(block_number-number_of_past_blocks, 0),\n                                                block_number, event_filter)\n\n    def _past_events_in_block_range(self, contract, event, cls, from_block, to_block, event_filter) -> list:\n        assert(isinstance(from_block, int))\n        assert(isinstance(to_block, int))\n        assert(isinstance(event_filter, dict) or (event_filter is None))\n\n        def _event_callback(cls, past):\n            def callback(log):\n                if past:\n                    self.logger.debug(f\"Past event {log['event']} discovered, block_number={log['blockNumber']},\"\n                                      f\" tx_hash={bytes_to_hexstring(log['transactionHash'])}\")\n                else:\n                    self.logger.debug(f\"Event {log['event']} discovered, block_number={log['blockNumber']},\"\n                                      f\" tx_hash={bytes_to_hexstring(log['transactionHash'])}\")\n                return cls(log)\n\n            return callback\n\n        result = contract.events[event].createFilter(fromBlock=from_block, toBlock=to_block,\n                                                     argument_filters=event_filter).get_all_entries()\n\n        return list(map(_event_callback(cls, True), result))\n\n    @staticmethod\n    def _load_abi(package, resource) -> list:\n        return json.loads(pkg_resources.resource_string(package, resource))\n\n    @staticmethod\n    def _load_bin(package, resource) -> str:\n        return str(pkg_resources.resource_string(package, resource), \"utf-8\")\n\n\nclass Calldata:\n    \"\"\"Represents Ethereum calldata.\n\n    Attributes:\n        value: Calldata as either a string starting with `0x`, or as bytes.\n    \"\"\"\n    def __init__(self, value):\n        if isinstance(value, str):\n            assert(value.startswith('0x'))\n            self.value = value\n\n        elif isinstance(value, bytes):\n            self.value = bytes_to_hexstring(value)\n\n        else:\n            raise Exception(f\"Unable to create calldata from '{value}'\")\n\n    @classmethod\n    def from_signature(cls, web3: Web3, fn_sign: str, fn_args: list):\n        \"\"\" Allow to create a `Calldata` from a function signature and a list of arguments.\n\n        :param fn_sign: the function signature ie. \"function(uint256,address)\"\n        :param fn_args: arguments to the function ie. [123, \"0x00...00\"]\n        \"\"\"\n        assert isinstance(fn_sign, str)\n        assert isinstance(fn_args, list)\n\n        fn_split = re.split('[(),]', fn_sign)\n        fn_name = fn_split[0]\n        fn_args_type = [{\"type\": type} for type in fn_split[1:] if type]\n\n        fn_abi = {\"type\": \"function\", \"name\": fn_name, \"inputs\": fn_args_type}\n        fn_abi, fn_selector, fn_arguments = get_function_info(\"test\", abi_codec=web3.codec, fn_abi=fn_abi, args=fn_args)\n\n        calldata = encode_abi(web3, fn_abi, fn_arguments, fn_selector)\n\n        return cls(calldata)\n\n    @classmethod\n    def from_contract_abi(cls, web3: Web3, fn_sign: str, fn_args: list, contract_abi):\n        \"\"\" Create a `Calldata` according to the given contract abi \"\"\"\n        assert isinstance(web3, Web3)\n        assert isinstance(fn_sign, str)\n        assert isinstance(fn_args, list)\n\n        fn_split = re.split('[(),]', fn_sign)\n        fn_name = fn_split[0]\n\n        fn_abi, fn_selector, fn_arguments = get_function_info(fn_name, abi_codec=web3.codec, contract_abi=contract_abi, args=fn_args)\n        calldata = encode_abi(web3, fn_abi, fn_arguments, fn_selector)\n\n        return cls(calldata)\n\n    def as_bytes(self) -> bytes:\n        \"\"\"Return the calldata as a byte array.\"\"\"\n        return bytes.fromhex(self.value.replace('0x', ''))\n\n    def __str__(self):\n        return f\"{self.value}\"\n\n    def __repr__(self):\n        return f\"Calldata('{self.value}')\"\n\n    def __hash__(self):\n        return self.value.__hash__()\n\n    def __eq__(self, other):\n        assert(isinstance(other, Calldata))\n        return self.value == other.value\n\n\nclass Invocation(object):\n    \"\"\"Single contract method invocation, to be used together with `TxManager`.\n\n    Attributes:\n        address: Contract address.\n        calldata: The calldata of the invocation.\n    \"\"\"\n    def __init__(self, address: Address, calldata: Calldata):\n        assert(isinstance(address, Address))\n        assert(isinstance(calldata, Calldata))\n        self.address = address\n        self.calldata = calldata\n\n\nclass Receipt:\n    \"\"\"Represents a receipt for an Ethereum transaction.\n\n    Attributes:\n        raw_receipt: Raw receipt received from the Ethereum node.\n        transaction_hash: Hash of the Ethereum transaction.\n        gas_used: Amount of gas used by the Ethereum transaction.\n        transfers: A list of ERC20 token transfers resulting from the execution\n            of this Ethereum transaction. Each transfer is an instance of the\n            :py:class:`pymaker.Transfer` class.\n        result: Transaction-specific return value (i.e. new order id for Oasis\n            order creation transaction).\n        successful: Boolean flag which is `True` if the Ethereum transaction\n            was successful. We consider transaction successful if the contract\n            method has been executed without throwing.\n    \"\"\"\n    def __init__(self, receipt):\n        self.raw_receipt = receipt\n        self.transaction_hash = receipt['transactionHash']\n        self.gas_used = receipt['gasUsed']\n        self.transfers = []\n        self.result = None\n\n        receipt_logs = receipt['logs']\n        if (receipt_logs is not None) and (len(receipt_logs) > 0):\n            self.successful = True\n            for receipt_log in receipt_logs:\n                if len(receipt_log['topics']) > 0:\n                    # $ seth keccak $(seth --from-ascii \"Transfer(address,address,uint256)\")\n                    # 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef\n                    if receipt_log['topics'][0] == HexBytes('0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'):\n                        from pymaker.token import ERC20Token\n                        transfer_abi = [abi for abi in ERC20Token.abi if abi.get('name') == 'Transfer'][0]\n                        codec = ABICodec(default_registry)\n                        try:\n                            event_data = get_event_data(codec, transfer_abi, receipt_log)\n                            self.transfers.append(Transfer(token_address=Address(event_data['address']),\n                                                           from_address=Address(event_data['args']['from']),\n                                                           to_address=Address(event_data['args']['to']),\n                                                           value=Wad(event_data['args']['value'])))\n                        # UniV3 Mint logIndex: 3 has an NFT mint of 1, from null, to a given address, but only 2 types (address, address)\n                        except LogTopicError:\n                            continue\n\n                    # $ seth keccak $(seth --from-ascii \"Mint(address,uint256)\")\n                    # 0x0f6798a560793a54c3bcfe86a93cde1e73087d944c0ea20544137d4121396885\n                    if receipt_log['topics'][0] == HexBytes('0x0f6798a560793a54c3bcfe86a93cde1e73087d944c0ea20544137d4121396885'):\n                        from pymaker.token import DSToken\n                        transfer_abi = [abi for abi in DSToken.abi if abi.get('name') == 'Mint'][0]\n                        codec = ABICodec(default_registry)\n                        event_data = get_event_data(codec, transfer_abi, receipt_log)\n                        self.transfers.append(Transfer(token_address=Address(event_data['address']),\n                                                       from_address=Address('0x0000000000000000000000000000000000000000'),\n                                                       to_address=Address(event_data['args']['guy']),\n                                                       value=Wad(event_data['args']['wad'])))\n\n                    # $ seth keccak $(seth --from-ascii \"Burn(address,uint256)\")\n                    # 0xcc16f5dbb4873280815c1ee09dbd06736cffcc184412cf7a71a0fdb75d397ca5\n                    if receipt_log['topics'][0] == HexBytes('0xcc16f5dbb4873280815c1ee09dbd06736cffcc184412cf7a71a0fdb75d397ca5'):\n                        from pymaker.token import DSToken\n                        transfer_abi = [abi for abi in DSToken.abi if abi.get('name') == 'Burn'][0]\n                        codec = ABICodec(default_registry)\n                        event_data = get_event_data(codec, transfer_abi, receipt_log)\n                        self.transfers.append(Transfer(token_address=Address(event_data['address']),\n                                                       from_address=Address(event_data['args']['guy']),\n                                                       to_address=Address('0x0000000000000000000000000000000000000000'),\n                                                       value=Wad(event_data['args']['wad'])))\n\n        else:\n            self.successful = False\n\n    @property\n    def logs(self):\n        return self.raw_receipt['logs']\n\n\nclass TransactStatus(Enum):\n     NEW = auto()\n     IN_PROGRESS = auto()\n     FINISHED = auto()\n\n\ndef get_pending_transactions(web3: Web3, address: Address = None) -> list:\n    \"\"\"Retrieves a list of pending transactions from the mempool.\"\"\"\n    assert isinstance(web3, Web3)\n    assert isinstance(address, Address) or address is None\n\n    if address is None:\n        address = Address(web3.eth.defaultAccount)\n\n    # Get the list of pending transactions and their details from specified sources\n    if _get_nonce_calc(web3) in (NonceCalculation.PARITY_NEXTNONCE, NonceCalculation.PARITY_SERIAL):\n        items = web3.manager.request_blocking(\"parity_pendingTransactions\", [])\n        items = filter(lambda item: item['from'].lower() == address.address.lower(), items)\n        items = filter(lambda item: item['blockNumber'] is None, items)\n        txes = map(lambda item: RecoveredTransact(web3=web3, address=address, nonce=int(item['nonce'], 16),\n                                                  latest_tx_hash=item['hash'], current_gas=int(item['gasPrice'], 16)),\n                   items)\n    else:\n        items = web3.manager.request_blocking(\"eth_getBlockByNumber\", [\"pending\", True])['transactions']\n        items = filter(lambda item: item['from'].lower() == address.address.lower(), items)\n        list(items)  # Unsure why this is required\n        txes = map(lambda item: RecoveredTransact(web3=web3, address=address, nonce=item['nonce'],\n                                                  latest_tx_hash=item['hash'], current_gas=item['gasPrice']),\n                   items)\n\n    return list(txes)\n\n\nclass Transact:\n    \"\"\"Represents an Ethereum transaction before it gets executed.\"\"\"\n\n    logger = logging.getLogger()\n    gas_estimate_for_bad_txs = None\n\n    def __init__(self,\n                 origin: Optional[object],\n                 web3: Web3,\n                 abi: Optional[list],\n                 address: Address,\n                 contract: Optional[object],\n                 function_name: Optional[str],\n                 parameters: Optional[list],\n                 extra: Optional[dict] = None,\n                 result_function=None):\n        assert(isinstance(origin, object) or (origin is None))\n        assert(isinstance(web3, Web3))\n        assert(isinstance(abi, list) or (abi is None))\n        assert(isinstance(address, Address))\n        assert(isinstance(contract, object) or (contract is None))\n        assert(isinstance(function_name, str) or (function_name is None))\n        assert(isinstance(parameters, list) or (parameters is None))\n        assert(isinstance(extra, dict) or (extra is None))\n        assert(callable(result_function) or (result_function is None))\n\n        self.origin = origin\n        self.web3 = web3\n        self.abi = abi\n        self.address = address\n        self.contract = contract\n        self.function_name = function_name\n        self.parameters = parameters\n        self.extra = extra\n        self.result_function = result_function\n        self.initial_time = None\n        self.status = TransactStatus.NEW\n        self.nonce = None\n        self.replaced = False\n        self.gas_price = None\n        self.gas_price_last = 0\n        self.tx_hashes = []\n\n    def _get_receipt(self, transaction_hash: str) -> Optional[Receipt]:\n        try:\n            raw_receipt = self.web3.eth.getTransactionReceipt(transaction_hash)\n            if raw_receipt is not None and raw_receipt['blockNumber'] is not None:\n                receipt = Receipt(raw_receipt)\n                receipt.result = self.result_function(receipt) if self.result_function is not None else None\n                return receipt\n        except (TransactionNotFound, ValueError):\n            self.logger.debug(f\"Transaction {transaction_hash} not found (may have been dropped/replaced)\")\n        return None\n\n    def _as_dict(self, dict_or_none) -> dict:\n        if dict_or_none is None:\n            return {}\n        else:\n            return dict(**dict_or_none)\n\n    def _gas(self, gas_estimate: int, **kwargs) -> int:\n        if 'gas' in kwargs and 'gas_buffer' in kwargs:\n            raise Exception('\"gas\" and \"gas_buffer\" keyword arguments may not be specified at the same time')\n\n        if 'gas' in kwargs:\n            return kwargs['gas']\n        elif 'gas_buffer' in kwargs:\n            return gas_estimate + kwargs['gas_buffer']\n        else:\n            return gas_estimate + 100000\n\n    def _func(self, from_account: str, gas: int, gas_price: Optional[int], nonce: Optional[int]):\n        gas_price_dict = {'gasPrice': gas_price} if gas_price is not None else {}\n        nonce_dict = {'nonce': nonce} if nonce is not None else {}\n\n        transaction_params = {**{'from': from_account, 'gas': gas},\n                              **gas_price_dict,\n                              **nonce_dict,\n                              **self._as_dict(self.extra)}\n\n        if self.contract is not None:\n            if self.function_name is None:\n\n                return bytes_to_hexstring(self.web3.eth.sendTransaction({**transaction_params,\n                                                                         **{'to': self.address.address,\n                                                                            'data': self.parameters[0]}}))\n            else:\n                return bytes_to_hexstring(self._contract_function().transact(transaction_params))\n        else:\n            return bytes_to_hexstring(self.web3.eth.sendTransaction({**transaction_params,\n                                                                     **{'to': self.address.address}}))\n\n    def _contract_function(self):\n        if '(' in self.function_name:\n            function_factory = self.contract.get_function_by_signature(self.function_name)\n\n        else:\n            function_factory = self.contract.get_function_by_name(self.function_name)\n\n        return function_factory(*self.parameters)\n\n    def name(self) -> str:\n        \"\"\"Returns the nicely formatted name of this pending Ethereum transaction.\n\n        Returns:\n            Nicely formatted name of this pending Ethereum transaction.\n        \"\"\"\n        if self.origin:\n            def format_parameter(parameter):\n                if isinstance(parameter, bytes):\n                    return bytes_to_hexstring(parameter)\n                else:\n                    return parameter\n\n            formatted_parameters = str(list(map(format_parameter, self.parameters))).lstrip(\"[\").rstrip(\"]\")\n            name = f\"{repr(self.origin)}.{self.function_name}({formatted_parameters})\"\n        else:\n            name = f\"Regular transfer to {self.address}\"\n\n        return name if self.extra is None else name + f\" with {self.extra}\"\n\n    def estimated_gas(self, from_address: Address) -> int:\n        \"\"\"Return an estimated amount of gas which will get consumed by this Ethereum transaction.\n\n        May throw an exception if the actual transaction will fail as well.\n\n        Args:\n            from_address: Address to simulate sending the transaction from.\n\n        Returns:\n            Amount of gas as an integer.\n        \"\"\"\n        assert(isinstance(from_address, Address))\n\n        if self.contract is not None:\n            if self.function_name is None:\n                return self.web3.eth.estimateGas({**self._as_dict(self.extra), **{'from': from_address.address,\n                                                                                  'to': self.address.address,\n                                                                                  'data': self.parameters[0]}})\n\n            else:\n                estimate = self._contract_function() \\\n                        .estimateGas({**self._as_dict(self.extra), **{'from': from_address.address}})\n\n        else:\n            estimate = 21000\n\n        return estimate\n\n    def transact(self, **kwargs) -> Optional[Receipt]:\n        \"\"\"Executes the Ethereum transaction synchronously.\n\n        Executes the Ethereum transaction synchronously. The method will block until the\n        transaction gets mined i.e. it will return when either the transaction execution\n        succeeded or failed. In case of the former, a :py:class:`pymaker.Receipt`\n        object will be returned.\n\n        Out-of-gas exceptions are automatically recognized as transaction failures.\n\n        Allowed keyword arguments are: `from_address`, `replace`, `gas`, `gas_buffer`, `gas_price`.\n        `gas_price` needs to be an instance of a class inheriting from :py:class:`pymaker.gas.GasPrice`.\n        `from_address` needs to be an instance of :py:class:`pymaker.Address`.\n\n        The `gas` keyword argument is the gas limit for the transaction, whereas `gas_buffer`\n        specifies how much gas should be added to the estimate. They can not be present\n        at the same time. If none of them are present, a default buffer is added to the estimate.\n\n        Returns:\n            A :py:class:`pymaker.Receipt` object if the transaction invocation was successful.\n            `None` otherwise.\n        \"\"\"\n        return synchronize([self.transact_async(**kwargs)])[0]\n\n    @_track_status\n    async def transact_async(self, **kwargs) -> Optional[Receipt]:\n        \"\"\"Executes the Ethereum transaction asynchronously.\n\n        Executes the Ethereum transaction asynchronously. The method will return immediately.\n        Ultimately, its future value will become either a :py:class:`pymaker.Receipt` or `None`,\n        depending on whether the transaction execution was successful or not.\n\n        Out-of-gas exceptions are automatically recognized as transaction failures.\n\n        Allowed keyword arguments are: `from_address`, `replace`, `gas`, `gas_buffer`, `gas_price`.\n        `gas_price` needs to be an instance of a class inheriting from :py:class:`pymaker.gas.GasPrice`.\n\n        The `gas` keyword argument is the gas limit for the transaction, whereas `gas_buffer`\n        specifies how much gas should be added to the estimate. They can not be present\n        at the same time. If none of them are present, a default buffer is added to the estimate.\n\n        Returns:\n            A future value of either a :py:class:`pymaker.Receipt` object if the transaction\n            invocation was successful, or `None` if it failed.\n        \"\"\"\n\n        global next_nonce\n        self.initial_time = time.time()\n        unknown_kwargs = set(kwargs.keys()) - {'from_address', 'replace', 'gas', 'gas_buffer', 'gas_price'}\n        if len(unknown_kwargs) > 0:\n            raise ValueError(f\"Unknown kwargs: {unknown_kwargs}\")\n\n        # Get the from account; initialize the first nonce for the account.\n        from_account = kwargs['from_address'].address if ('from_address' in kwargs) else self.web3.eth.defaultAccount\n        if not next_nonce or from_account not in next_nonce:\n            next_nonce[from_account] = self.web3.eth.getTransactionCount(from_account, block_identifier='pending')\n\n        # First we try to estimate the gas usage of the transaction. If gas estimation fails\n        # it means there is no point in sending the transaction, thus we fail instantly and\n        # do not increment the nonce. If the estimation is successful, we pass the calculated\n        # gas value (plus some `gas_buffer`) to the subsequent `transact` calls so it does not\n        # try to estimate it again.\n        try:\n            gas_estimate = self.estimated_gas(Address(from_account))\n        except:\n            if Transact.gas_estimate_for_bad_txs:\n                self.logger.warning(f\"Transaction {self.name()} will fail, submitting anyway\")\n                gas_estimate = Transact.gas_estimate_for_bad_txs\n            else:\n                self.logger.warning(f\"Transaction {self.name()} will fail, refusing to send ({sys.exc_info()[1]})\")\n                return None\n\n        # Get or calculate `gas`. Get `gas_price`, which in fact refers to a gas pricing algorithm.\n        gas = self._gas(gas_estimate, **kwargs)\n        self.gas_price = kwargs['gas_price'] if ('gas_price' in kwargs) else DefaultGasPrice()\n        assert(isinstance(self.gas_price, GasPrice))\n\n        # Get the transaction this one is supposed to replace.\n        # If there is one, try to borrow the nonce from it as long as that transaction isn't finished.\n        replaced_tx = kwargs['replace'] if ('replace' in kwargs) else None\n        if replaced_tx is not None:\n            while replaced_tx.nonce is None and replaced_tx.status != TransactStatus.FINISHED:\n                await asyncio.sleep(0.25)\n\n            replaced_tx.replaced = True\n            self.nonce = replaced_tx.nonce\n            # Gas should be calculated from the original time of submission\n            self.initial_time = replaced_tx.initial_time if replaced_tx.initial_time else time.time()\n            # Use gas strategy from the original transaction if one was not provided\n            if 'gas_price' not in kwargs:\n                self.gas_price = replaced_tx.gas_price if replaced_tx.gas_price else DefaultGasPrice()\n            self.gas_price_last = replaced_tx.gas_price_last\n            # Detain replacement until gas strategy produces a price acceptable to the node\n            if replaced_tx.tx_hashes:\n                most_recent_tx = replaced_tx.tx_hashes[-1]\n                self.tx_hashes = [most_recent_tx]\n\n        while True:\n            seconds_elapsed = int(time.time() - self.initial_time)\n\n            # CAUTION: if transact_async is called rapidly, we will hammer the node with these JSON-RPC requests\n            if self.nonce is not None and self.web3.eth.getTransactionCount(from_account) > self.nonce:\n                # Check if any transaction sent so far has been mined (has a receipt).\n                # If it has, we return either the receipt (if if was successful) or `None`.\n                for attempt in range(1, 11):\n                    if self.replaced:\n                        self.logger.info(f\"Transaction with nonce={self.nonce} was replaced with a newer transaction\")\n                        return None\n\n                    for tx_hash in self.tx_hashes:\n                        receipt = self._get_receipt(tx_hash)\n                        if receipt:\n                            if receipt.successful:\n                                self.logger.info(f\"Transaction {self.name()} was successful (tx_hash={tx_hash})\")\n                                return receipt\n                            else:\n                                self.logger.warning(f\"Transaction {self.name()} mined successfully but generated no single\"\n                                                    f\" log entry, assuming it has failed (tx_hash={tx_hash})\")\n                                return None\n\n                    self.logger.debug(f\"No receipt found in attempt #{attempt}/10 (nonce={self.nonce},\"\n                                      f\" getTransactionCount={self.web3.eth.getTransactionCount(from_account)})\")\n\n                    await asyncio.sleep(0.5)\n\n                # If we can not find a mined receipt but at the same time we know last used nonce\n                # has increased, then it means that the transaction we tried to send failed.\n                self.logger.warning(f\"Transaction {self.name()} has been overridden by another transaction\"\n                                    f\" with the same nonce, which means it has failed\")\n                return None\n\n            # Trap replacement after the tx has entered the mempool and before it has been mined\n            if self.replaced:\n                self.logger.info(f\"Transaction {self.name()} with nonce={self.nonce} is being replaced\")\n                return None\n\n            # Send a transaction if:\n            # - no transaction has been sent yet, or\n            # - the requested gas price has changed enough since the last transaction has been sent\n            # - the gas price on a replacement has sufficiently exceeded that of the original transaction\n            gas_price_value = self.gas_price.get_gas_price(seconds_elapsed)\n            transaction_was_sent = len(self.tx_hashes) > 0 or (replaced_tx is not None and len(replaced_tx.tx_hashes) > 0)\n            # Uncomment this to debug state during transaction submission\n            # self.logger.debug(f\"Transaction {self.name()} is churning: was_sent={transaction_was_sent}, gas_price_value={gas_price_value} gas_price_last={self.gas_price_last}\")\n            if not transaction_was_sent or (gas_price_value is not None and gas_price_value > self.gas_price_last * 1.125):\n                self.gas_price_last = gas_price_value\n\n                try:\n                    # We need the lock in order to not try to send two transactions with the same nonce.\n                    with transaction_lock:\n                        if self.nonce is None:\n                            nonce_calculation = _get_nonce_calc(self.web3)\n                            if nonce_calculation == NonceCalculation.PARITY_NEXTNONCE:\n                                self.nonce = int(self.web3.manager.request_blocking(\"parity_nextNonce\", [from_account]), 16)\n                            elif nonce_calculation == NonceCalculation.TX_COUNT:\n                                self.nonce = self.web3.eth.getTransactionCount(from_account, block_identifier='pending')\n                            elif nonce_calculation == NonceCalculation.SERIAL:\n                                tx_count = self.web3.eth.getTransactionCount(from_account, block_identifier='pending')\n                                next_serial = next_nonce[from_account]\n                                self.nonce = max(tx_count, next_serial)\n                            elif nonce_calculation == NonceCalculation.PARITY_SERIAL:\n                                tx_count = int(self.web3.manager.request_blocking(\"parity_nextNonce\", [from_account]), 16)\n                                next_serial = next_nonce[from_account]\n                                self.nonce = max(tx_count, next_serial)\n                            next_nonce[from_account] = self.nonce + 1\n\n                        # Trap replacement while original is holding the lock awaiting nonce assignment\n                        if self.replaced:\n                            self.logger.info(f\"Transaction {self.name()} with nonce={self.nonce} was replaced\")\n                            return None\n\n                        tx_hash = self._func(from_account, gas, gas_price_value, self.nonce)\n                        self.tx_hashes.append(tx_hash)\n\n                    self.logger.info(f\"Sent transaction {self.name()} with nonce={self.nonce}, gas={gas},\"\n                                     f\" gas_price={gas_price_value if gas_price_value is not None else 'default'}\"\n                                     f\" (tx_hash={tx_hash})\")\n                except Exception as e:\n                    self.logger.warning(f\"Failed to send transaction {self.name()} with nonce={self.nonce}, gas={gas},\"\n                                        f\" gas_price={gas_price_value if gas_price_value is not None else 'default'}\"\n                                        f\" ({e})\")\n\n                    if len(self.tx_hashes) == 0:\n                        raise\n\n            await asyncio.sleep(0.25)\n\n    def invocation(self) -> Invocation:\n        \"\"\"Returns the `Invocation` object for this pending Ethereum transaction.\n\n        The :py:class:`pymaker.Invocation` object may be used with :py:class:`pymaker.transactional.TxManager`\n        to invoke multiple contract calls in one Ethereum transaction.\n\n        Please see :py:class:`pymaker.transactional.TxManager` documentation for more details.\n\n        Returns:\n            :py:class:`pymaker.Invocation` object for this pending Ethereum transaction.\n        \"\"\"\n        return Invocation(self.address, Calldata(self._contract_function()._encode_transaction_data()))\n\n\nclass RecoveredTransact(Transact):\n    \"\"\" Models a pending transaction retrieved from the mempool.\n\n    These can be created by a call to `get_pending_transactions`, enabling the consumer to implement logic which\n    cancels pending transactions upon keeper/bot startup.\n    \"\"\"\n    def __init__(self, web3: Web3,\n                 address: Address,\n                 nonce: int,\n                 latest_tx_hash: str,\n                 current_gas: int):\n        assert isinstance(current_gas, int)\n        super().__init__(origin=None,\n                         web3=web3,\n                         abi=None,\n                         address=address,\n                         contract=None,\n                         function_name=None,\n                         parameters=None)\n        self.nonce = nonce\n        self.tx_hashes.append(latest_tx_hash)\n        self.current_gas = current_gas\n\n    def name(self):\n        return f\"Recovered tx with nonce {self.nonce}\"\n\n    @_track_status\n    async def transact_async(self, **kwargs) -> Optional[Receipt]:\n        # TODO: Read transaction data from chain, create a new state machine to manage gas for the transaction.\n        raise NotImplementedError()\n\n    def cancel(self, gas_price: GasPrice):\n        return synchronize([self.cancel_async(gas_price)])[0]\n\n    async def cancel_async(self, gas_price: GasPrice):\n        assert isinstance(gas_price, GasPrice)\n        initial_time = time.time()\n        self.gas_price_last = self.current_gas\n        self.tx_hashes.clear()\n\n        if gas_price.get_gas_price(0) <= self.current_gas * 1.125:\n            self.logger.warning(f\"Recovery gas price is less than current gas price {self.current_gas}; \"\n                                \"cancellation will be deferred until the strategy produces an acceptable price.\")\n\n        while True:\n            seconds_elapsed = int(time.time() - initial_time)\n            gas_price_value = gas_price.get_gas_price(seconds_elapsed)\n            if gas_price_value > self.gas_price_last * 1.125:\n                self.gas_price_last = gas_price_value\n                # Transaction lock isn't needed here, as we are replacing an existing nonce\n                tx_hash = bytes_to_hexstring(self.web3.eth.sendTransaction({'from': self.address.address,\n                                                                            'to': self.address.address,\n                                                                            'gasPrice': gas_price_value,\n                                                                            'nonce': self.nonce,\n                                                                            'value': 0}))\n                self.tx_hashes.append(tx_hash)\n                self.logger.info(f\"Attempting to cancel recovered tx with nonce={self.nonce}, \"\n                                 f\"gas_price={gas_price_value} (tx_hash={tx_hash})\")\n\n            for tx_hash in self.tx_hashes:\n                receipt = self._get_receipt(tx_hash)\n                if receipt:\n                    self.logger.info(f\"{self.name()} was cancelled (tx_hash={tx_hash})\")\n                    return\n\n            await asyncio.sleep(0.75)\n\n\nclass Transfer:\n    \"\"\"Represents an ERC20 token transfer.\n\n    Represents an ERC20 token transfer resulting from contract method execution.\n    A list of transfers can be found in the :py:class:`pymaker.Receipt` class.\n\n    Attributes:\n        token_address: Address of the ERC20 token that has been transferred.\n        from_address: Source address of the transfer.\n        to_address: Destination address of the transfer.\n        value: Value transferred.\n    \"\"\"\n    def __init__(self, token_address: Address, from_address: Address, to_address: Address, value: Wad):\n        assert(isinstance(token_address, Address))\n        assert(isinstance(from_address, Address))\n        assert(isinstance(to_address, Address))\n        assert(isinstance(value, Wad))\n        self.token_address = token_address\n        self.from_address = from_address\n        self.to_address = to_address\n        self.value = value\n\n    def __eq__(self, other):\n        assert(isinstance(other, Transfer))\n        return self.token_address == other.token_address and \\\n               self.from_address == other.from_address and \\\n               self.to_address == other.to_address and \\\n               self.value == other.value\n\n    def __hash__(self):\n        return hash((self.token_address, self.from_address, self.token_address, self.value))\n\n\ndef eth_transfer(web3: Web3, to: Address, amount: Wad) -> Transact:\n    return Transact(None, web3, None, to, None, None, None, {'value': amount.value})\n"
  },
  {
    "path": "pymaker/abi/Cat.abi",
    "content": "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"vat_\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"ilk\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"urn\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"ink\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"art\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"tab\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"flip\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"Bite\",\"type\":\"event\"},{\"anonymous\":true,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes4\",\"name\":\"sig\",\"type\":\"bytes4\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"arg1\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"arg2\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"LogNote\",\"type\":\"event\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"ilk\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"urn\",\"type\":\"address\"}],\"name\":\"bite\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"box\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"cage\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"rad\",\"type\":\"uint256\"}],\"name\":\"claw\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"}],\"name\":\"deny\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"ilk\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"what\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"data\",\"type\":\"uint256\"}],\"name\":\"file\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"what\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"data\",\"type\":\"uint256\"}],\"name\":\"file\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"what\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"data\",\"type\":\"address\"}],\"name\":\"file\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"ilk\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"what\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"flip\",\"type\":\"address\"}],\"name\":\"file\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"ilks\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"flip\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"chop\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"dunk\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"litter\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"live\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"}],\"name\":\"rely\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"vat\",\"outputs\":[{\"internalType\":\"contract VatLike\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"vow\",\"outputs\":[{\"internalType\":\"contract VowLike\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"wards\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"}]\n"
  },
  {
    "path": "pymaker/abi/Clipper.abi",
    "content": "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"vat_\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"spotter_\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"dog_\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"ilk_\",\"type\":\"bytes32\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"}],\"name\":\"Deny\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"what\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"data\",\"type\":\"uint256\"}],\"name\":\"File\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"what\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"data\",\"type\":\"address\"}],\"name\":\"File\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"top\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"tab\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"lot\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"kpr\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"coin\",\"type\":\"uint256\"}],\"name\":\"Kick\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"top\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"tab\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"lot\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"kpr\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"coin\",\"type\":\"uint256\"}],\"name\":\"Redo\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"}],\"name\":\"Rely\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"max\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"price\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"owe\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"tab\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"lot\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"}],\"name\":\"Take\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"Yank\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"active\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"buf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"calc\",\"outputs\":[{\"internalType\":\"contract AbacusLike\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"chip\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"chost\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"count\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"cusp\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"}],\"name\":\"deny\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dog\",\"outputs\":[{\"internalType\":\"contract DogLike\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"what\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"data\",\"type\":\"uint256\"}],\"name\":\"file\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"what\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"data\",\"type\":\"address\"}],\"name\":\"file\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"getStatus\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"needsRedo\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"price\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"lot\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"tab\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"ilk\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"tab\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"lot\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"kpr\",\"type\":\"address\"}],\"name\":\"kick\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"kicks\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"list\",\"outputs\":[{\"internalType\":\"uint256[]\",\"name\":\"\",\"type\":\"uint256[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"kpr\",\"type\":\"address\"}],\"name\":\"redo\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"}],\"name\":\"rely\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"sales\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"pos\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"tab\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"lot\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"tic\",\"type\":\"uint96\"},{\"internalType\":\"uint256\",\"name\":\"top\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"spotter\",\"outputs\":[{\"internalType\":\"contract SpotterLike\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"stopped\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"tail\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"amt\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"max\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"who\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"take\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"tip\",\"outputs\":[{\"internalType\":\"uint192\",\"name\":\"\",\"type\":\"uint192\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"upchost\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"vat\",\"outputs\":[{\"internalType\":\"contract VatLike\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"vow\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"wards\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"yank\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]\n"
  },
  {
    "path": "pymaker/abi/ClipperCallee.abi",
    "content": "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"name\":\"clipperCall\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]\n"
  },
  {
    "path": "pymaker/abi/DSAuth.abi",
    "content": "[{\"constant\":false,\"inputs\":[{\"name\":\"owner_\",\"type\":\"address\"}],\"name\":\"setOwner\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"authority_\",\"type\":\"address\"}],\"name\":\"setAuthority\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"authority\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"authority\",\"type\":\"address\"}],\"name\":\"LogSetAuthority\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"LogSetOwner\",\"type\":\"event\"}]"
  },
  {
    "path": "pymaker/abi/DSChief.abi",
    "content": "[{\"inputs\":[{\"internalType\":\"contract DSToken\",\"name\":\"GOV\",\"type\":\"address\"},{\"internalType\":\"contract DSToken\",\"name\":\"IOU\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"MAX_YAYS\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"slate\",\"type\":\"bytes32\"}],\"name\":\"Etch\",\"type\":\"event\"},{\"anonymous\":true,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes4\",\"name\":\"sig\",\"type\":\"bytes4\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"guy\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"foo\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"bar\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"wad\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"fax\",\"type\":\"bytes\"}],\"name\":\"LogNote\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"authority\",\"type\":\"address\"}],\"name\":\"LogSetAuthority\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"LogSetOwner\",\"type\":\"event\"},{\"constant\":true,\"inputs\":[],\"name\":\"GOV\",\"outputs\":[{\"internalType\":\"contract DSToken\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"IOU\",\"outputs\":[{\"internalType\":\"contract DSToken\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"MAX_YAYS\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"approvals\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"authority\",\"outputs\":[{\"internalType\":\"contract DSAuthority\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"caller\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"code\",\"type\":\"address\"},{\"internalType\":\"bytes4\",\"name\":\"sig\",\"type\":\"bytes4\"}],\"name\":\"canCall\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"deposits\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"yays\",\"type\":\"address[]\"}],\"name\":\"etch\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"slate\",\"type\":\"bytes32\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"free\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"code\",\"type\":\"address\"},{\"internalType\":\"bytes4\",\"name\":\"sig\",\"type\":\"bytes4\"}],\"name\":\"getCapabilityRoles\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"who\",\"type\":\"address\"}],\"name\":\"getUserRoles\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"who\",\"type\":\"address\"},{\"internalType\":\"uint8\",\"name\":\"role\",\"type\":\"uint8\"}],\"name\":\"hasUserRole\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"hat\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"code\",\"type\":\"address\"},{\"internalType\":\"bytes4\",\"name\":\"sig\",\"type\":\"bytes4\"}],\"name\":\"isCapabilityPublic\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"who\",\"type\":\"address\"}],\"name\":\"isUserRoot\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"last\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"launch\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"whom\",\"type\":\"address\"}],\"name\":\"lift\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"live\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"lock\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"contract DSAuthority\",\"name\":\"authority_\",\"type\":\"address\"}],\"name\":\"setAuthority\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner_\",\"type\":\"address\"}],\"name\":\"setOwner\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"code\",\"type\":\"address\"},{\"internalType\":\"bytes4\",\"name\":\"sig\",\"type\":\"bytes4\"},{\"internalType\":\"bool\",\"name\":\"enabled\",\"type\":\"bool\"}],\"name\":\"setPublicCapability\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"role\",\"type\":\"uint8\"},{\"internalType\":\"address\",\"name\":\"code\",\"type\":\"address\"},{\"internalType\":\"bytes4\",\"name\":\"sig\",\"type\":\"bytes4\"},{\"internalType\":\"bool\",\"name\":\"enabled\",\"type\":\"bool\"}],\"name\":\"setRoleCapability\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"who\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"enabled\",\"type\":\"bool\"}],\"name\":\"setRootUser\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"who\",\"type\":\"address\"},{\"internalType\":\"uint8\",\"name\":\"role\",\"type\":\"uint8\"},{\"internalType\":\"bool\",\"name\":\"enabled\",\"type\":\"bool\"}],\"name\":\"setUserRole\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"slates\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"slate\",\"type\":\"bytes32\"}],\"name\":\"vote\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"yays\",\"type\":\"address[]\"}],\"name\":\"vote\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"votes\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"}]"
  },
  {
    "path": "pymaker/abi/DSEthToken.abi",
    "content": "[{\"constant\":true,\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"guy\",\"type\":\"address\"},{\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"totalSupply\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"src\",\"type\":\"address\"},{\"name\":\"dst\",\"type\":\"address\"},{\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"withdraw\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"src\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"symbol\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"dst\",\"type\":\"address\"},{\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"transfer\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"deposit\",\"outputs\":[],\"payable\":true,\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"wrap\",\"outputs\":[],\"payable\":true,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"src\",\"type\":\"address\"},{\"name\":\"guy\",\"type\":\"address\"}],\"name\":\"allowance\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"unwrap\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"tryWithdraw\",\"outputs\":[{\"name\":\"ok\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"payable\":true,\"type\":\"fallback\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"who\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Deposit\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"who\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Withdrawal\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Transfer\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"spender\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Approval\",\"type\":\"event\"}]"
  },
  {
    "path": "pymaker/abi/DSGuard.abi",
    "content": "[{\"constant\":false,\"inputs\":[{\"name\":\"owner_\",\"type\":\"address\"}],\"name\":\"setOwner\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"src\",\"type\":\"address\"},{\"name\":\"dst\",\"type\":\"address\"},{\"name\":\"sig\",\"type\":\"bytes32\"}],\"name\":\"forbid\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"src\",\"type\":\"bytes32\"},{\"name\":\"dst\",\"type\":\"bytes32\"},{\"name\":\"sig\",\"type\":\"bytes32\"}],\"name\":\"forbid\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"authority_\",\"type\":\"address\"}],\"name\":\"setAuthority\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"ANY\",\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"src_\",\"type\":\"address\"},{\"name\":\"dst_\",\"type\":\"address\"},{\"name\":\"sig\",\"type\":\"bytes4\"}],\"name\":\"canCall\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"authority\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"src\",\"type\":\"address\"},{\"name\":\"dst\",\"type\":\"address\"},{\"name\":\"sig\",\"type\":\"bytes32\"}],\"name\":\"permit\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"src\",\"type\":\"bytes32\"},{\"name\":\"dst\",\"type\":\"bytes32\"},{\"name\":\"sig\",\"type\":\"bytes32\"}],\"name\":\"permit\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"src\",\"type\":\"bytes32\"},{\"indexed\":true,\"name\":\"dst\",\"type\":\"bytes32\"},{\"indexed\":true,\"name\":\"sig\",\"type\":\"bytes32\"}],\"name\":\"LogPermit\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"src\",\"type\":\"bytes32\"},{\"indexed\":true,\"name\":\"dst\",\"type\":\"bytes32\"},{\"indexed\":true,\"name\":\"sig\",\"type\":\"bytes32\"}],\"name\":\"LogForbid\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"authority\",\"type\":\"address\"}],\"name\":\"LogSetAuthority\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"LogSetOwner\",\"type\":\"event\"}]"
  },
  {
    "path": "pymaker/abi/DSPause.abi",
    "content": "[{\"constant\":false,\"inputs\":[{\"name\":\"owner_\",\"type\":\"address\"}],\"name\":\"setOwner\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"usr\",\"type\":\"address\"},{\"name\":\"fax\",\"type\":\"bytes\"},{\"name\":\"era\",\"type\":\"uint256\"}],\"name\":\"drop\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"delay\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"authority_\",\"type\":\"address\"}],\"name\":\"setAuthority\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"usr\",\"type\":\"address\"},{\"name\":\"fax\",\"type\":\"bytes\"},{\"name\":\"era\",\"type\":\"uint256\"}],\"name\":\"plan\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"plans\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"usr\",\"type\":\"address\"},{\"name\":\"fax\",\"type\":\"bytes\"},{\"name\":\"era\",\"type\":\"uint256\"}],\"name\":\"exec\",\"outputs\":[{\"name\":\"response\",\"type\":\"bytes\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"authority\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"name\":\"delay_\",\"type\":\"uint256\"},{\"name\":\"owner_\",\"type\":\"address\"},{\"name\":\"authority_\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"name\":\"usr\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"fax\",\"type\":\"bytes\"},{\"indexed\":false,\"name\":\"era\",\"type\":\"uint256\"}],\"name\":\"Plan\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"name\":\"usr\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"fax\",\"type\":\"bytes\"},{\"indexed\":false,\"name\":\"era\",\"type\":\"uint256\"}],\"name\":\"Drop\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"name\":\"usr\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"fax\",\"type\":\"bytes\"},{\"indexed\":false,\"name\":\"era\",\"type\":\"uint256\"}],\"name\":\"Exec\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"authority\",\"type\":\"address\"}],\"name\":\"LogSetAuthority\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"LogSetOwner\",\"type\":\"event\"}]"
  },
  {
    "path": "pymaker/abi/DSProxy.abi",
    "content": "[{\"constant\":false,\"inputs\":[{\"name\":\"owner_\",\"type\":\"address\"}],\"name\":\"setOwner\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_target\",\"type\":\"address\"},{\"name\":\"_data\",\"type\":\"bytes\"}],\"name\":\"execute\",\"outputs\":[{\"name\":\"response\",\"type\":\"bytes32\"}],\"payable\":true,\"stateMutability\":\"payable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_code\",\"type\":\"bytes\"},{\"name\":\"_data\",\"type\":\"bytes\"}],\"name\":\"execute\",\"outputs\":[{\"name\":\"target\",\"type\":\"address\"},{\"name\":\"response\",\"type\":\"bytes32\"}],\"payable\":true,\"stateMutability\":\"payable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"cache\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"authority_\",\"type\":\"address\"}],\"name\":\"setAuthority\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_cacheAddr\",\"type\":\"address\"}],\"name\":\"setCache\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"authority\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"name\":\"_cacheAddr\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"payable\":true,\"stateMutability\":\"payable\",\"type\":\"fallback\"},{\"anonymous\":true,\"inputs\":[{\"indexed\":true,\"name\":\"sig\",\"type\":\"bytes4\"},{\"indexed\":true,\"name\":\"guy\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"foo\",\"type\":\"bytes32\"},{\"indexed\":true,\"name\":\"bar\",\"type\":\"bytes32\"},{\"indexed\":false,\"name\":\"wad\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"fax\",\"type\":\"bytes\"}],\"name\":\"LogNote\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"authority\",\"type\":\"address\"}],\"name\":\"LogSetAuthority\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"LogSetOwner\",\"type\":\"event\"}]"
  },
  {
    "path": "pymaker/abi/DSProxyCache.abi",
    "content": "[{\"constant\":false,\"inputs\":[{\"name\":\"_code\",\"type\":\"bytes\"}],\"name\":\"write\",\"outputs\":[{\"name\":\"target\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"_code\",\"type\":\"bytes\"}],\"name\":\"read\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"}]"
  },
  {
    "path": "pymaker/abi/DSProxyFactory.abi",
    "content": "[{\"constant\":true,\"inputs\":[{\"name\":\"\",\"type\":\"address\"}],\"name\":\"isProxy\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"cache\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"build\",\"outputs\":[{\"name\":\"proxy\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"build\",\"outputs\":[{\"name\":\"proxy\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"proxy\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"cache\",\"type\":\"address\"}],\"name\":\"Created\",\"type\":\"event\"}]"
  },
  {
    "path": "pymaker/abi/DSRoles.abi",
    "content": "[{\"constant\":true,\"inputs\":[{\"name\":\"who\",\"type\":\"address\"}],\"name\":\"getUserRoles\",\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"owner_\",\"type\":\"address\"}],\"name\":\"setOwner\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"code\",\"type\":\"address\"},{\"name\":\"sig\",\"type\":\"bytes4\"}],\"name\":\"getCapabilityRoles\",\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"code\",\"type\":\"address\"},{\"name\":\"sig\",\"type\":\"bytes4\"}],\"name\":\"isCapabilityPublic\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"who\",\"type\":\"address\"},{\"name\":\"role\",\"type\":\"uint8\"},{\"name\":\"enabled\",\"type\":\"bool\"}],\"name\":\"setUserRole\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"authority_\",\"type\":\"address\"}],\"name\":\"setAuthority\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"role\",\"type\":\"uint8\"},{\"name\":\"code\",\"type\":\"address\"},{\"name\":\"sig\",\"type\":\"bytes4\"},{\"name\":\"enabled\",\"type\":\"bool\"}],\"name\":\"setRoleCapability\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"who\",\"type\":\"address\"},{\"name\":\"role\",\"type\":\"uint8\"}],\"name\":\"hasUserRole\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"caller\",\"type\":\"address\"},{\"name\":\"code\",\"type\":\"address\"},{\"name\":\"sig\",\"type\":\"bytes4\"}],\"name\":\"canCall\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"authority\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"code\",\"type\":\"address\"},{\"name\":\"sig\",\"type\":\"bytes4\"},{\"name\":\"enabled\",\"type\":\"bool\"}],\"name\":\"setPublicCapability\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"who\",\"type\":\"address\"},{\"name\":\"enabled\",\"type\":\"bool\"}],\"name\":\"setRootUser\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"who\",\"type\":\"address\"}],\"name\":\"isUserRoot\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"authority\",\"type\":\"address\"}],\"name\":\"LogSetAuthority\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"LogSetOwner\",\"type\":\"event\"}]"
  },
  {
    "path": "pymaker/abi/DSToken.abi",
    "content": "[{\"constant\":true,\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"stop\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"guy\",\"type\":\"address\"},{\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"owner_\",\"type\":\"address\"}],\"name\":\"setOwner\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"totalSupply\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"src\",\"type\":\"address\"},{\"name\":\"dst\",\"type\":\"address\"},{\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"guy\",\"type\":\"address\"},{\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"mint\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"burn\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"name_\",\"type\":\"bytes32\"}],\"name\":\"setName\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"src\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"stopped\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"authority_\",\"type\":\"address\"}],\"name\":\"setAuthority\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"symbol\",\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"guy\",\"type\":\"address\"},{\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"burn\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"mint\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"dst\",\"type\":\"address\"},{\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"transfer\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"dst\",\"type\":\"address\"},{\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"push\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"src\",\"type\":\"address\"},{\"name\":\"dst\",\"type\":\"address\"},{\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"move\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"start\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"authority\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"guy\",\"type\":\"address\"}],\"name\":\"approve\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"src\",\"type\":\"address\"},{\"name\":\"guy\",\"type\":\"address\"}],\"name\":\"allowance\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"src\",\"type\":\"address\"},{\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"pull\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"name\":\"symbol_\",\"type\":\"bytes32\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"guy\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"Mint\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"guy\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"Burn\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"authority\",\"type\":\"address\"}],\"name\":\"LogSetAuthority\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"LogSetOwner\",\"type\":\"event\"},{\"anonymous\":true,\"inputs\":[{\"indexed\":true,\"name\":\"sig\",\"type\":\"bytes4\"},{\"indexed\":true,\"name\":\"guy\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"foo\",\"type\":\"bytes32\"},{\"indexed\":true,\"name\":\"bar\",\"type\":\"bytes32\"},{\"indexed\":false,\"name\":\"wad\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"fax\",\"type\":\"bytes\"}],\"name\":\"LogNote\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"src\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"guy\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"Approval\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"src\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"dst\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"Transfer\",\"type\":\"event\"}]"
  },
  {
    "path": "pymaker/abi/DSValue.abi",
    "content": "[{\"constant\":false,\"inputs\":[{\"name\":\"owner_\",\"type\":\"address\"}],\"name\":\"setOwner\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"wut\",\"type\":\"bytes32\"}],\"name\":\"poke\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"read\",\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"peek\",\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\"},{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"authority_\",\"type\":\"address\"}],\"name\":\"setAuthority\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"void\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"authority\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"type\":\"function\"},{\"anonymous\":true,\"inputs\":[{\"indexed\":true,\"name\":\"sig\",\"type\":\"bytes4\"},{\"indexed\":true,\"name\":\"guy\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"foo\",\"type\":\"bytes32\"},{\"indexed\":true,\"name\":\"bar\",\"type\":\"bytes32\"},{\"indexed\":false,\"name\":\"wad\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"fax\",\"type\":\"bytes\"}],\"name\":\"LogNote\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"authority\",\"type\":\"address\"}],\"name\":\"LogSetAuthority\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"LogSetOwner\",\"type\":\"event\"}]"
  },
  {
    "path": "pymaker/abi/DSVault.abi",
    "content": "[{\"constant\":false,\"inputs\":[{\"name\":\"token_\",\"type\":\"address\"}],\"name\":\"swap\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"owner_\",\"type\":\"address\"}],\"name\":\"setOwner\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"dst\",\"type\":\"address\"},{\"name\":\"wad\",\"type\":\"uint128\"}],\"name\":\"push\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"token\",\"type\":\"address\"},{\"name\":\"dst\",\"type\":\"address\"},{\"name\":\"wad\",\"type\":\"uint128\"}],\"name\":\"push\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"burn\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"src\",\"type\":\"address\"}],\"name\":\"pull\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"token\",\"type\":\"address\"},{\"name\":\"src\",\"type\":\"address\"}],\"name\":\"pull\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"wad\",\"type\":\"uint128\"}],\"name\":\"mint\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"token\",\"type\":\"address\"},{\"name\":\"src\",\"type\":\"address\"},{\"name\":\"wad\",\"type\":\"uint128\"}],\"name\":\"pull\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"token\",\"type\":\"address\"},{\"name\":\"wad\",\"type\":\"uint128\"}],\"name\":\"burn\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"authority_\",\"type\":\"address\"}],\"name\":\"setAuthority\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"src\",\"type\":\"address\"},{\"name\":\"wad\",\"type\":\"uint128\"}],\"name\":\"pull\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"token\",\"type\":\"address\"}],\"name\":\"burn\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"dst\",\"type\":\"address\"}],\"name\":\"push\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"wad\",\"type\":\"uint128\"}],\"name\":\"burn\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"token\",\"type\":\"address\"},{\"name\":\"dst\",\"type\":\"address\"}],\"name\":\"push\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"token\",\"type\":\"address\"},{\"name\":\"wad\",\"type\":\"uint128\"}],\"name\":\"mint\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"authority\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"token\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"type\":\"function\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"authority\",\"type\":\"address\"}],\"name\":\"LogSetAuthority\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"LogSetOwner\",\"type\":\"event\"}]"
  },
  {
    "path": "pymaker/abi/DaiJoin.abi",
    "content": "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"vat_\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"dai_\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":true,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes4\",\"name\":\"sig\",\"type\":\"bytes4\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"arg1\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"arg2\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"LogNote\",\"type\":\"event\"},{\"constant\":false,\"inputs\":[],\"name\":\"cage\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"dai\",\"outputs\":[{\"internalType\":\"contract DSTokenLike\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"}],\"name\":\"deny\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"exit\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"join\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"live\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"}],\"name\":\"rely\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"vat\",\"outputs\":[{\"internalType\":\"contract VatLike\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"wards\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"}]\n"
  },
  {
    "path": "pymaker/abi/Dog.abi",
    "content": "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"vat_\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"ilk\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"urn\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"ink\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"art\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"due\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"clip\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"Bark\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[],\"name\":\"Cage\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"}],\"name\":\"Deny\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"ilk\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"rad\",\"type\":\"uint256\"}],\"name\":\"Digs\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"what\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"data\",\"type\":\"uint256\"}],\"name\":\"File\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"what\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"data\",\"type\":\"address\"}],\"name\":\"File\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"ilk\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"what\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"data\",\"type\":\"uint256\"}],\"name\":\"File\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"ilk\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"what\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"clip\",\"type\":\"address\"}],\"name\":\"File\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"}],\"name\":\"Rely\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"Dirt\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"Hole\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"ilk\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"urn\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"kpr\",\"type\":\"address\"}],\"name\":\"bark\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"cage\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"ilk\",\"type\":\"bytes32\"}],\"name\":\"chop\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"}],\"name\":\"deny\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"ilk\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"rad\",\"type\":\"uint256\"}],\"name\":\"digs\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"ilk\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"what\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"data\",\"type\":\"uint256\"}],\"name\":\"file\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"what\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"data\",\"type\":\"uint256\"}],\"name\":\"file\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"what\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"data\",\"type\":\"address\"}],\"name\":\"file\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"ilk\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"what\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"clip\",\"type\":\"address\"}],\"name\":\"file\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"ilks\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"clip\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"chop\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"hole\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"dirt\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"live\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"}],\"name\":\"rely\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"vat\",\"outputs\":[{\"internalType\":\"contract VatLike\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"vow\",\"outputs\":[{\"internalType\":\"contract VowLike\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"wards\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]\n"
  },
  {
    "path": "pymaker/abi/DsrManager.abi",
    "content": "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"pot_\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"daiJoin_\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"dst\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"Exit\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"dst\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"Join\",\"type\":\"event\"},{\"constant\":true,\"inputs\":[],\"name\":\"dai\",\"outputs\":[{\"internalType\":\"contract GemLike\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"}],\"name\":\"daiBalance\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"wad\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"daiJoin\",\"outputs\":[{\"internalType\":\"contract JoinLike\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"dst\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"exit\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"dst\",\"type\":\"address\"}],\"name\":\"exitAll\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"dst\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"join\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"pieOf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"pot\",\"outputs\":[{\"internalType\":\"contract PotLike\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"supply\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"}]\n"
  },
  {
    "path": "pymaker/abi/DssCdpManager.abi",
    "content": "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"vat_\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":true,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes4\",\"name\":\"sig\",\"type\":\"bytes4\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"arg1\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"arg2\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"LogNote\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"own\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"cdp\",\"type\":\"uint256\"}],\"name\":\"NewCdp\",\"type\":\"event\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"cdp\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"ok\",\"type\":\"uint256\"}],\"name\":\"cdpAllow\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"cdpCan\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"cdpi\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"count\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"src\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"cdp\",\"type\":\"uint256\"}],\"name\":\"enter\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"first\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"ilk\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"cdp\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"dst\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"flux\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"cdp\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"dst\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"flux\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"cdp\",\"type\":\"uint256\"},{\"internalType\":\"int256\",\"name\":\"dink\",\"type\":\"int256\"},{\"internalType\":\"int256\",\"name\":\"dart\",\"type\":\"int256\"}],\"name\":\"frob\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"cdp\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"dst\",\"type\":\"address\"}],\"name\":\"give\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"ilks\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"last\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"list\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"prev\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"next\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"cdp\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"dst\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"rad\",\"type\":\"uint256\"}],\"name\":\"move\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"ilk\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"}],\"name\":\"open\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"owns\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"cdp\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"dst\",\"type\":\"address\"}],\"name\":\"quit\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"cdpSrc\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"cdpDst\",\"type\":\"uint256\"}],\"name\":\"shift\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"ok\",\"type\":\"uint256\"}],\"name\":\"urnAllow\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"urnCan\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"urns\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"vat\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"}]\n"
  },
  {
    "path": "pymaker/abi/DssProxyActionsDsr.abi",
    "content": "[{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"apt\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"urn\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"daiJoin_join\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"daiJoin\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"pot\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"exit\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"daiJoin\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"pot\",\"type\":\"address\"}],\"name\":\"exitAll\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"daiJoin\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"pot\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"join\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]\n"
  },
  {
    "path": "pymaker/abi/ERC20Token.abi",
    "content": "[{\"constant\":false,\"inputs\":[{\"name\":\"guy\",\"type\":\"address\"},{\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"totalSupply\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"src\",\"type\":\"address\"},{\"name\":\"dst\",\"type\":\"address\"},{\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"src\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"dst\",\"type\":\"address\"},{\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"transfer\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"src\",\"type\":\"address\"},{\"name\":\"guy\",\"type\":\"address\"}],\"name\":\"allowance\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"inputs\":[{\"name\":\"supply\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Transfer\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"spender\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Approval\",\"type\":\"event\"}]"
  },
  {
    "path": "pymaker/abi/ESM.abi",
    "content": "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"gem_\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"end_\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"proxy_\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"min_\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[],\"name\":\"Fire\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"Join\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"Sum\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"burn\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"}],\"name\":\"deny\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"end\",\"outputs\":[{\"internalType\":\"contract EndLike\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"fire\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"gem\",\"outputs\":[{\"internalType\":\"contract GemLike\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"join\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"min\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"proxy\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"revokesGovernanceAccess\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"ret\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"sum\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]\n"
  },
  {
    "path": "pymaker/abi/End.abi",
    "content": "[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[],\"name\":\"Cage\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"ilk\",\"type\":\"bytes32\"}],\"name\":\"Cage\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"ilk\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"Cash\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"}],\"name\":\"Deny\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"what\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"data\",\"type\":\"uint256\"}],\"name\":\"File\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"what\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"data\",\"type\":\"address\"}],\"name\":\"File\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"ilk\",\"type\":\"bytes32\"}],\"name\":\"Flow\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"ilk\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"ink\",\"type\":\"uint256\"}],\"name\":\"Free\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"Pack\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"}],\"name\":\"Rely\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"ilk\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"urn\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"wad\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"art\",\"type\":\"uint256\"}],\"name\":\"Skim\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"ilk\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"tab\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"lot\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"art\",\"type\":\"uint256\"}],\"name\":\"Skip\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"ilk\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"tab\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"lot\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"art\",\"type\":\"uint256\"}],\"name\":\"Snip\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[],\"name\":\"Thaw\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"Art\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"bag\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"cage\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"ilk\",\"type\":\"bytes32\"}],\"name\":\"cage\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"ilk\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"cash\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"cat\",\"outputs\":[{\"internalType\":\"contract CatLike\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"debt\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"}],\"name\":\"deny\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dog\",\"outputs\":[{\"internalType\":\"contract DogLike\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"what\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"data\",\"type\":\"uint256\"}],\"name\":\"file\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"what\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"data\",\"type\":\"address\"}],\"name\":\"file\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"fix\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"ilk\",\"type\":\"bytes32\"}],\"name\":\"flow\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"ilk\",\"type\":\"bytes32\"}],\"name\":\"free\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"gap\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"live\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"out\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"pack\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pot\",\"outputs\":[{\"internalType\":\"contract PotLike\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"}],\"name\":\"rely\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"ilk\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"urn\",\"type\":\"address\"}],\"name\":\"skim\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"ilk\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"skip\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"ilk\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"snip\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"spot\",\"outputs\":[{\"internalType\":\"contract SpotLike\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"tag\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"thaw\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"vat\",\"outputs\":[{\"internalType\":\"contract VatLike\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"vow\",\"outputs\":[{\"internalType\":\"contract VowLike\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"wait\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"wards\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"when\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]\n"
  },
  {
    "path": "pymaker/abi/EtherDelta.abi",
    "content": "[{\"constant\":false,\"inputs\":[{\"name\":\"tokenGet\",\"type\":\"address\"},{\"name\":\"amountGet\",\"type\":\"uint256\"},{\"name\":\"tokenGive\",\"type\":\"address\"},{\"name\":\"amountGive\",\"type\":\"uint256\"},{\"name\":\"expires\",\"type\":\"uint256\"},{\"name\":\"nonce\",\"type\":\"uint256\"},{\"name\":\"user\",\"type\":\"address\"},{\"name\":\"v\",\"type\":\"uint8\"},{\"name\":\"r\",\"type\":\"bytes32\"},{\"name\":\"s\",\"type\":\"bytes32\"},{\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"trade\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"tokenGet\",\"type\":\"address\"},{\"name\":\"amountGet\",\"type\":\"uint256\"},{\"name\":\"tokenGive\",\"type\":\"address\"},{\"name\":\"amountGive\",\"type\":\"uint256\"},{\"name\":\"expires\",\"type\":\"uint256\"},{\"name\":\"nonce\",\"type\":\"uint256\"}],\"name\":\"order\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"\",\"type\":\"address\"},{\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"orderFills\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"tokenGet\",\"type\":\"address\"},{\"name\":\"amountGet\",\"type\":\"uint256\"},{\"name\":\"tokenGive\",\"type\":\"address\"},{\"name\":\"amountGive\",\"type\":\"uint256\"},{\"name\":\"expires\",\"type\":\"uint256\"},{\"name\":\"nonce\",\"type\":\"uint256\"},{\"name\":\"v\",\"type\":\"uint8\"},{\"name\":\"r\",\"type\":\"bytes32\"},{\"name\":\"s\",\"type\":\"bytes32\"}],\"name\":\"cancelOrder\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"withdraw\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"token\",\"type\":\"address\"},{\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"depositToken\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"tokenGet\",\"type\":\"address\"},{\"name\":\"amountGet\",\"type\":\"uint256\"},{\"name\":\"tokenGive\",\"type\":\"address\"},{\"name\":\"amountGive\",\"type\":\"uint256\"},{\"name\":\"expires\",\"type\":\"uint256\"},{\"name\":\"nonce\",\"type\":\"uint256\"},{\"name\":\"user\",\"type\":\"address\"},{\"name\":\"v\",\"type\":\"uint8\"},{\"name\":\"r\",\"type\":\"bytes32\"},{\"name\":\"s\",\"type\":\"bytes32\"}],\"name\":\"amountFilled\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"\",\"type\":\"address\"},{\"name\":\"\",\"type\":\"address\"}],\"name\":\"tokens\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"feeMake_\",\"type\":\"uint256\"}],\"name\":\"changeFeeMake\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"feeMake\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"feeRebate_\",\"type\":\"uint256\"}],\"name\":\"changeFeeRebate\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"feeAccount\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"tokenGet\",\"type\":\"address\"},{\"name\":\"amountGet\",\"type\":\"uint256\"},{\"name\":\"tokenGive\",\"type\":\"address\"},{\"name\":\"amountGive\",\"type\":\"uint256\"},{\"name\":\"expires\",\"type\":\"uint256\"},{\"name\":\"nonce\",\"type\":\"uint256\"},{\"name\":\"user\",\"type\":\"address\"},{\"name\":\"v\",\"type\":\"uint8\"},{\"name\":\"r\",\"type\":\"bytes32\"},{\"name\":\"s\",\"type\":\"bytes32\"},{\"name\":\"amount\",\"type\":\"uint256\"},{\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"testTrade\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"feeAccount_\",\"type\":\"address\"}],\"name\":\"changeFeeAccount\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"feeRebate\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"feeTake_\",\"type\":\"uint256\"}],\"name\":\"changeFeeTake\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"admin_\",\"type\":\"address\"}],\"name\":\"changeAdmin\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"token\",\"type\":\"address\"},{\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"withdrawToken\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"\",\"type\":\"address\"},{\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"orders\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"feeTake\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"deposit\",\"outputs\":[],\"payable\":true,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"accountLevelsAddr_\",\"type\":\"address\"}],\"name\":\"changeAccountLevelsAddr\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"accountLevelsAddr\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"token\",\"type\":\"address\"},{\"name\":\"user\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"admin\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"tokenGet\",\"type\":\"address\"},{\"name\":\"amountGet\",\"type\":\"uint256\"},{\"name\":\"tokenGive\",\"type\":\"address\"},{\"name\":\"amountGive\",\"type\":\"uint256\"},{\"name\":\"expires\",\"type\":\"uint256\"},{\"name\":\"nonce\",\"type\":\"uint256\"},{\"name\":\"user\",\"type\":\"address\"},{\"name\":\"v\",\"type\":\"uint8\"},{\"name\":\"r\",\"type\":\"bytes32\"},{\"name\":\"s\",\"type\":\"bytes32\"}],\"name\":\"availableVolume\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"inputs\":[{\"name\":\"admin_\",\"type\":\"address\"},{\"name\":\"feeAccount_\",\"type\":\"address\"},{\"name\":\"accountLevelsAddr_\",\"type\":\"address\"},{\"name\":\"feeMake_\",\"type\":\"uint256\"},{\"name\":\"feeTake_\",\"type\":\"uint256\"},{\"name\":\"feeRebate_\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"constructor\"},{\"payable\":false,\"type\":\"fallback\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"name\":\"tokenGet\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"amountGet\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"tokenGive\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"amountGive\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"expires\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"nonce\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"user\",\"type\":\"address\"}],\"name\":\"Order\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"name\":\"tokenGet\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"amountGet\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"tokenGive\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"amountGive\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"expires\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"nonce\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"user\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"v\",\"type\":\"uint8\"},{\"indexed\":false,\"name\":\"r\",\"type\":\"bytes32\"},{\"indexed\":false,\"name\":\"s\",\"type\":\"bytes32\"}],\"name\":\"Cancel\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"name\":\"tokenGet\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"amountGet\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"tokenGive\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"amountGive\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"get\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"give\",\"type\":\"address\"}],\"name\":\"Trade\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"name\":\"token\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"user\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"balance\",\"type\":\"uint256\"}],\"name\":\"Deposit\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"name\":\"token\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"user\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"balance\",\"type\":\"uint256\"}],\"name\":\"Withdraw\",\"type\":\"event\"}]"
  },
  {
    "path": "pymaker/abi/EtherToken.abi",
    "content": "[{\"constant\":true,\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_spender\",\"type\":\"address\"},{\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"totalSupply\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_from\",\"type\":\"address\"},{\"name\":\"_to\",\"type\":\"address\"},{\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"withdraw\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"name\":\"\",\"type\":\"uint8\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"_owner\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"symbol\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_to\",\"type\":\"address\"},{\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"transfer\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"deposit\",\"outputs\":[],\"payable\":true,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"_owner\",\"type\":\"address\"},{\"name\":\"_spender\",\"type\":\"address\"}],\"name\":\"allowance\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"payable\":true,\"type\":\"fallback\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"_from\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"_to\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"Transfer\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"_owner\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"_spender\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"Approval\",\"type\":\"event\"}]"
  },
  {
    "path": "pymaker/abi/Exchange.abi",
    "content": "[{\"constant\":true,\"inputs\":[{\"name\":\"numerator\",\"type\":\"uint256\"},{\"name\":\"denominator\",\"type\":\"uint256\"},{\"name\":\"target\",\"type\":\"uint256\"}],\"name\":\"isRoundingError\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"filled\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"cancelled\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"orderAddresses\",\"type\":\"address[5][]\"},{\"name\":\"orderValues\",\"type\":\"uint256[6][]\"},{\"name\":\"fillTakerTokenAmount\",\"type\":\"uint256\"},{\"name\":\"shouldThrowOnInsufficientBalanceOrAllowance\",\"type\":\"bool\"},{\"name\":\"v\",\"type\":\"uint8[]\"},{\"name\":\"r\",\"type\":\"bytes32[]\"},{\"name\":\"s\",\"type\":\"bytes32[]\"}],\"name\":\"fillOrdersUpTo\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"orderAddresses\",\"type\":\"address[5]\"},{\"name\":\"orderValues\",\"type\":\"uint256[6]\"},{\"name\":\"cancelTakerTokenAmount\",\"type\":\"uint256\"}],\"name\":\"cancelOrder\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"ZRX_TOKEN_CONTRACT\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"orderAddresses\",\"type\":\"address[5][]\"},{\"name\":\"orderValues\",\"type\":\"uint256[6][]\"},{\"name\":\"fillTakerTokenAmounts\",\"type\":\"uint256[]\"},{\"name\":\"v\",\"type\":\"uint8[]\"},{\"name\":\"r\",\"type\":\"bytes32[]\"},{\"name\":\"s\",\"type\":\"bytes32[]\"}],\"name\":\"batchFillOrKillOrders\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"orderAddresses\",\"type\":\"address[5]\"},{\"name\":\"orderValues\",\"type\":\"uint256[6]\"},{\"name\":\"fillTakerTokenAmount\",\"type\":\"uint256\"},{\"name\":\"v\",\"type\":\"uint8\"},{\"name\":\"r\",\"type\":\"bytes32\"},{\"name\":\"s\",\"type\":\"bytes32\"}],\"name\":\"fillOrKillOrder\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"orderHash\",\"type\":\"bytes32\"}],\"name\":\"getUnavailableTakerTokenAmount\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"signer\",\"type\":\"address\"},{\"name\":\"hash\",\"type\":\"bytes32\"},{\"name\":\"v\",\"type\":\"uint8\"},{\"name\":\"r\",\"type\":\"bytes32\"},{\"name\":\"s\",\"type\":\"bytes32\"}],\"name\":\"isValidSignature\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"numerator\",\"type\":\"uint256\"},{\"name\":\"denominator\",\"type\":\"uint256\"},{\"name\":\"target\",\"type\":\"uint256\"}],\"name\":\"getPartialAmount\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"TOKEN_TRANSFER_PROXY_CONTRACT\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"orderAddresses\",\"type\":\"address[5][]\"},{\"name\":\"orderValues\",\"type\":\"uint256[6][]\"},{\"name\":\"fillTakerTokenAmounts\",\"type\":\"uint256[]\"},{\"name\":\"shouldThrowOnInsufficientBalanceOrAllowance\",\"type\":\"bool\"},{\"name\":\"v\",\"type\":\"uint8[]\"},{\"name\":\"r\",\"type\":\"bytes32[]\"},{\"name\":\"s\",\"type\":\"bytes32[]\"}],\"name\":\"batchFillOrders\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"orderAddresses\",\"type\":\"address[5][]\"},{\"name\":\"orderValues\",\"type\":\"uint256[6][]\"},{\"name\":\"cancelTakerTokenAmounts\",\"type\":\"uint256[]\"}],\"name\":\"batchCancelOrders\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"orderAddresses\",\"type\":\"address[5]\"},{\"name\":\"orderValues\",\"type\":\"uint256[6]\"},{\"name\":\"fillTakerTokenAmount\",\"type\":\"uint256\"},{\"name\":\"shouldThrowOnInsufficientBalanceOrAllowance\",\"type\":\"bool\"},{\"name\":\"v\",\"type\":\"uint8\"},{\"name\":\"r\",\"type\":\"bytes32\"},{\"name\":\"s\",\"type\":\"bytes32\"}],\"name\":\"fillOrder\",\"outputs\":[{\"name\":\"filledTakerTokenAmount\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"orderAddresses\",\"type\":\"address[5]\"},{\"name\":\"orderValues\",\"type\":\"uint256[6]\"}],\"name\":\"getOrderHash\",\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"EXTERNAL_QUERY_GAS_LIMIT\",\"outputs\":[{\"name\":\"\",\"type\":\"uint16\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"VERSION\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"type\":\"function\"},{\"inputs\":[{\"name\":\"_zrxToken\",\"type\":\"address\"},{\"name\":\"_tokenTransferProxy\",\"type\":\"address\"}],\"payable\":false,\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"maker\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"taker\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"feeRecipient\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"makerToken\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"takerToken\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"filledMakerTokenAmount\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"filledTakerTokenAmount\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"paidMakerFee\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"paidTakerFee\",\"type\":\"uint256\"},{\"indexed\":true,\"name\":\"tokens\",\"type\":\"bytes32\"},{\"indexed\":false,\"name\":\"orderHash\",\"type\":\"bytes32\"}],\"name\":\"LogFill\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"maker\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"feeRecipient\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"makerToken\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"takerToken\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"cancelledMakerTokenAmount\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"cancelledTakerTokenAmount\",\"type\":\"uint256\"},{\"indexed\":true,\"name\":\"tokens\",\"type\":\"bytes32\"},{\"indexed\":false,\"name\":\"orderHash\",\"type\":\"bytes32\"}],\"name\":\"LogCancel\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"errorId\",\"type\":\"uint8\"},{\"indexed\":true,\"name\":\"orderHash\",\"type\":\"bytes32\"}],\"name\":\"LogError\",\"type\":\"event\"}]"
  },
  {
    "path": "pymaker/abi/ExchangeV2-ERC20Proxy.abi",
    "content": "[\n\t\t\t{\n\t\t\t\t\"constant\": false,\n\t\t\t\t\"inputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"target\",\n\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"name\": \"addAuthorizedAddress\",\n\t\t\t\t\"outputs\": [],\n\t\t\t\t\"payable\": false,\n\t\t\t\t\"stateMutability\": \"nonpayable\",\n\t\t\t\t\"type\": \"function\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"constant\": true,\n\t\t\t\t\"inputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"\",\n\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"name\": \"authorities\",\n\t\t\t\t\"outputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"\",\n\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"payable\": false,\n\t\t\t\t\"stateMutability\": \"view\",\n\t\t\t\t\"type\": \"function\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"constant\": false,\n\t\t\t\t\"inputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"target\",\n\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"name\": \"removeAuthorizedAddress\",\n\t\t\t\t\"outputs\": [],\n\t\t\t\t\"payable\": false,\n\t\t\t\t\"stateMutability\": \"nonpayable\",\n\t\t\t\t\"type\": \"function\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"constant\": true,\n\t\t\t\t\"inputs\": [],\n\t\t\t\t\"name\": \"owner\",\n\t\t\t\t\"outputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"\",\n\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"payable\": false,\n\t\t\t\t\"stateMutability\": \"view\",\n\t\t\t\t\"type\": \"function\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"constant\": false,\n\t\t\t\t\"inputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"target\",\n\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"index\",\n\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"name\": \"removeAuthorizedAddressAtIndex\",\n\t\t\t\t\"outputs\": [],\n\t\t\t\t\"payable\": false,\n\t\t\t\t\"stateMutability\": \"nonpayable\",\n\t\t\t\t\"type\": \"function\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"constant\": true,\n\t\t\t\t\"inputs\": [],\n\t\t\t\t\"name\": \"getProxyId\",\n\t\t\t\t\"outputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"\",\n\t\t\t\t\t\t\"type\": \"bytes4\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"payable\": false,\n\t\t\t\t\"stateMutability\": \"pure\",\n\t\t\t\t\"type\": \"function\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"constant\": true,\n\t\t\t\t\"inputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"\",\n\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"name\": \"authorized\",\n\t\t\t\t\"outputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"\",\n\t\t\t\t\t\t\"type\": \"bool\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"payable\": false,\n\t\t\t\t\"stateMutability\": \"view\",\n\t\t\t\t\"type\": \"function\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"constant\": true,\n\t\t\t\t\"inputs\": [],\n\t\t\t\t\"name\": \"getAuthorizedAddresses\",\n\t\t\t\t\"outputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"\",\n\t\t\t\t\t\t\"type\": \"address[]\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"payable\": false,\n\t\t\t\t\"stateMutability\": \"view\",\n\t\t\t\t\"type\": \"function\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"constant\": false,\n\t\t\t\t\"inputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"newOwner\",\n\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"name\": \"transferOwnership\",\n\t\t\t\t\"outputs\": [],\n\t\t\t\t\"payable\": false,\n\t\t\t\t\"stateMutability\": \"nonpayable\",\n\t\t\t\t\"type\": \"function\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"payable\": false,\n\t\t\t\t\"stateMutability\": \"nonpayable\",\n\t\t\t\t\"type\": \"fallback\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"anonymous\": false,\n\t\t\t\t\"inputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"indexed\": true,\n\t\t\t\t\t\t\"name\": \"target\",\n\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"indexed\": true,\n\t\t\t\t\t\t\"name\": \"caller\",\n\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"name\": \"AuthorizedAddressAdded\",\n\t\t\t\t\"type\": \"event\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"anonymous\": false,\n\t\t\t\t\"inputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"indexed\": true,\n\t\t\t\t\t\t\"name\": \"target\",\n\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"indexed\": true,\n\t\t\t\t\t\t\"name\": \"caller\",\n\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"name\": \"AuthorizedAddressRemoved\",\n\t\t\t\t\"type\": \"event\"\n\t\t\t}\n\t\t]"
  },
  {
    "path": "pymaker/abi/ExchangeV2.abi",
    "content": "[\n\t\t\t{\n\t\t\t\t\"constant\": true,\n\t\t\t\t\"inputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"\",\n\t\t\t\t\t\t\"type\": \"bytes32\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"name\": \"filled\",\n\t\t\t\t\"outputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"\",\n\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"payable\": false,\n\t\t\t\t\"stateMutability\": \"view\",\n\t\t\t\t\"type\": \"function\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"constant\": false,\n\t\t\t\t\"inputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"components\": [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerAddress\",\n\t\t\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerAddress\",\n\t\t\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"feeRecipientAddress\",\n\t\t\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"senderAddress\",\n\t\t\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerAssetAmount\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerAssetAmount\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerFee\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerFee\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"expirationTimeSeconds\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"salt\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerAssetData\",\n\t\t\t\t\t\t\t\t\"type\": \"bytes\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerAssetData\",\n\t\t\t\t\t\t\t\t\"type\": \"bytes\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t],\n\t\t\t\t\t\t\"name\": \"orders\",\n\t\t\t\t\t\t\"type\": \"tuple[]\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"takerAssetFillAmounts\",\n\t\t\t\t\t\t\"type\": \"uint256[]\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"signatures\",\n\t\t\t\t\t\t\"type\": \"bytes[]\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"name\": \"batchFillOrders\",\n\t\t\t\t\"outputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"components\": [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerAssetFilledAmount\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerAssetFilledAmount\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerFeePaid\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerFeePaid\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t],\n\t\t\t\t\t\t\"name\": \"totalFillResults\",\n\t\t\t\t\t\t\"type\": \"tuple\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"payable\": false,\n\t\t\t\t\"stateMutability\": \"nonpayable\",\n\t\t\t\t\"type\": \"function\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"constant\": true,\n\t\t\t\t\"inputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"\",\n\t\t\t\t\t\t\"type\": \"bytes32\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"name\": \"cancelled\",\n\t\t\t\t\"outputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"\",\n\t\t\t\t\t\t\"type\": \"bool\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"payable\": false,\n\t\t\t\t\"stateMutability\": \"view\",\n\t\t\t\t\"type\": \"function\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"constant\": false,\n\t\t\t\t\"inputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"hash\",\n\t\t\t\t\t\t\"type\": \"bytes32\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"signerAddress\",\n\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"signature\",\n\t\t\t\t\t\t\"type\": \"bytes\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"name\": \"preSign\",\n\t\t\t\t\"outputs\": [],\n\t\t\t\t\"payable\": false,\n\t\t\t\t\"stateMutability\": \"nonpayable\",\n\t\t\t\t\"type\": \"function\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"constant\": false,\n\t\t\t\t\"inputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"components\": [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerAddress\",\n\t\t\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerAddress\",\n\t\t\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"feeRecipientAddress\",\n\t\t\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"senderAddress\",\n\t\t\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerAssetAmount\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerAssetAmount\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerFee\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerFee\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"expirationTimeSeconds\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"salt\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerAssetData\",\n\t\t\t\t\t\t\t\t\"type\": \"bytes\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerAssetData\",\n\t\t\t\t\t\t\t\t\"type\": \"bytes\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t],\n\t\t\t\t\t\t\"name\": \"leftOrder\",\n\t\t\t\t\t\t\"type\": \"tuple\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"components\": [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerAddress\",\n\t\t\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerAddress\",\n\t\t\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"feeRecipientAddress\",\n\t\t\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"senderAddress\",\n\t\t\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerAssetAmount\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerAssetAmount\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerFee\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerFee\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"expirationTimeSeconds\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"salt\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerAssetData\",\n\t\t\t\t\t\t\t\t\"type\": \"bytes\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerAssetData\",\n\t\t\t\t\t\t\t\t\"type\": \"bytes\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t],\n\t\t\t\t\t\t\"name\": \"rightOrder\",\n\t\t\t\t\t\t\"type\": \"tuple\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"leftSignature\",\n\t\t\t\t\t\t\"type\": \"bytes\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"rightSignature\",\n\t\t\t\t\t\t\"type\": \"bytes\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"name\": \"matchOrders\",\n\t\t\t\t\"outputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"components\": [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"components\": [\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\"name\": \"makerAssetFilledAmount\",\n\t\t\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\"name\": \"takerAssetFilledAmount\",\n\t\t\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\"name\": \"makerFeePaid\",\n\t\t\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\"name\": \"takerFeePaid\",\n\t\t\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t\"name\": \"left\",\n\t\t\t\t\t\t\t\t\"type\": \"tuple\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"components\": [\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\"name\": \"makerAssetFilledAmount\",\n\t\t\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\"name\": \"takerAssetFilledAmount\",\n\t\t\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\"name\": \"makerFeePaid\",\n\t\t\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\"name\": \"takerFeePaid\",\n\t\t\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t\"name\": \"right\",\n\t\t\t\t\t\t\t\t\"type\": \"tuple\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"leftMakerAssetSpreadAmount\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t],\n\t\t\t\t\t\t\"name\": \"matchedFillResults\",\n\t\t\t\t\t\t\"type\": \"tuple\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"payable\": false,\n\t\t\t\t\"stateMutability\": \"nonpayable\",\n\t\t\t\t\"type\": \"function\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"constant\": false,\n\t\t\t\t\"inputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"components\": [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerAddress\",\n\t\t\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerAddress\",\n\t\t\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"feeRecipientAddress\",\n\t\t\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"senderAddress\",\n\t\t\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerAssetAmount\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerAssetAmount\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerFee\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerFee\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"expirationTimeSeconds\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"salt\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerAssetData\",\n\t\t\t\t\t\t\t\t\"type\": \"bytes\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerAssetData\",\n\t\t\t\t\t\t\t\t\"type\": \"bytes\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t],\n\t\t\t\t\t\t\"name\": \"order\",\n\t\t\t\t\t\t\"type\": \"tuple\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"takerAssetFillAmount\",\n\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"signature\",\n\t\t\t\t\t\t\"type\": \"bytes\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"name\": \"fillOrderNoThrow\",\n\t\t\t\t\"outputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"components\": [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerAssetFilledAmount\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerAssetFilledAmount\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerFeePaid\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerFeePaid\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t],\n\t\t\t\t\t\t\"name\": \"fillResults\",\n\t\t\t\t\t\t\"type\": \"tuple\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"payable\": false,\n\t\t\t\t\"stateMutability\": \"nonpayable\",\n\t\t\t\t\"type\": \"function\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"constant\": true,\n\t\t\t\t\"inputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"\",\n\t\t\t\t\t\t\"type\": \"bytes4\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"name\": \"assetProxies\",\n\t\t\t\t\"outputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"\",\n\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"payable\": false,\n\t\t\t\t\"stateMutability\": \"view\",\n\t\t\t\t\"type\": \"function\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"constant\": false,\n\t\t\t\t\"inputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"components\": [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerAddress\",\n\t\t\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerAddress\",\n\t\t\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"feeRecipientAddress\",\n\t\t\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"senderAddress\",\n\t\t\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerAssetAmount\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerAssetAmount\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerFee\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerFee\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"expirationTimeSeconds\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"salt\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerAssetData\",\n\t\t\t\t\t\t\t\t\"type\": \"bytes\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerAssetData\",\n\t\t\t\t\t\t\t\t\"type\": \"bytes\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t],\n\t\t\t\t\t\t\"name\": \"orders\",\n\t\t\t\t\t\t\"type\": \"tuple[]\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"name\": \"batchCancelOrders\",\n\t\t\t\t\"outputs\": [],\n\t\t\t\t\"payable\": false,\n\t\t\t\t\"stateMutability\": \"nonpayable\",\n\t\t\t\t\"type\": \"function\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"constant\": false,\n\t\t\t\t\"inputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"components\": [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerAddress\",\n\t\t\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerAddress\",\n\t\t\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"feeRecipientAddress\",\n\t\t\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"senderAddress\",\n\t\t\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerAssetAmount\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerAssetAmount\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerFee\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerFee\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"expirationTimeSeconds\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"salt\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerAssetData\",\n\t\t\t\t\t\t\t\t\"type\": \"bytes\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerAssetData\",\n\t\t\t\t\t\t\t\t\"type\": \"bytes\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t],\n\t\t\t\t\t\t\"name\": \"orders\",\n\t\t\t\t\t\t\"type\": \"tuple[]\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"takerAssetFillAmounts\",\n\t\t\t\t\t\t\"type\": \"uint256[]\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"signatures\",\n\t\t\t\t\t\t\"type\": \"bytes[]\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"name\": \"batchFillOrKillOrders\",\n\t\t\t\t\"outputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"components\": [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerAssetFilledAmount\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerAssetFilledAmount\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerFeePaid\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerFeePaid\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t],\n\t\t\t\t\t\t\"name\": \"totalFillResults\",\n\t\t\t\t\t\t\"type\": \"tuple\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"payable\": false,\n\t\t\t\t\"stateMutability\": \"nonpayable\",\n\t\t\t\t\"type\": \"function\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"constant\": false,\n\t\t\t\t\"inputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"targetOrderEpoch\",\n\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"name\": \"cancelOrdersUpTo\",\n\t\t\t\t\"outputs\": [],\n\t\t\t\t\"payable\": false,\n\t\t\t\t\"stateMutability\": \"nonpayable\",\n\t\t\t\t\"type\": \"function\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"constant\": false,\n\t\t\t\t\"inputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"components\": [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerAddress\",\n\t\t\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerAddress\",\n\t\t\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"feeRecipientAddress\",\n\t\t\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"senderAddress\",\n\t\t\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerAssetAmount\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerAssetAmount\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerFee\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerFee\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"expirationTimeSeconds\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"salt\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerAssetData\",\n\t\t\t\t\t\t\t\t\"type\": \"bytes\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerAssetData\",\n\t\t\t\t\t\t\t\t\"type\": \"bytes\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t],\n\t\t\t\t\t\t\"name\": \"orders\",\n\t\t\t\t\t\t\"type\": \"tuple[]\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"takerAssetFillAmounts\",\n\t\t\t\t\t\t\"type\": \"uint256[]\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"signatures\",\n\t\t\t\t\t\t\"type\": \"bytes[]\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"name\": \"batchFillOrdersNoThrow\",\n\t\t\t\t\"outputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"components\": [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerAssetFilledAmount\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerAssetFilledAmount\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerFeePaid\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerFeePaid\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t],\n\t\t\t\t\t\t\"name\": \"totalFillResults\",\n\t\t\t\t\t\t\"type\": \"tuple\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"payable\": false,\n\t\t\t\t\"stateMutability\": \"nonpayable\",\n\t\t\t\t\"type\": \"function\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"constant\": true,\n\t\t\t\t\"inputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"assetProxyId\",\n\t\t\t\t\t\t\"type\": \"bytes4\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"name\": \"getAssetProxy\",\n\t\t\t\t\"outputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"\",\n\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"payable\": false,\n\t\t\t\t\"stateMutability\": \"view\",\n\t\t\t\t\"type\": \"function\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"constant\": true,\n\t\t\t\t\"inputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"\",\n\t\t\t\t\t\t\"type\": \"bytes32\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"name\": \"transactions\",\n\t\t\t\t\"outputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"\",\n\t\t\t\t\t\t\"type\": \"bool\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"payable\": false,\n\t\t\t\t\"stateMutability\": \"view\",\n\t\t\t\t\"type\": \"function\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"constant\": false,\n\t\t\t\t\"inputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"components\": [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerAddress\",\n\t\t\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerAddress\",\n\t\t\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"feeRecipientAddress\",\n\t\t\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"senderAddress\",\n\t\t\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerAssetAmount\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerAssetAmount\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerFee\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerFee\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"expirationTimeSeconds\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"salt\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerAssetData\",\n\t\t\t\t\t\t\t\t\"type\": \"bytes\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerAssetData\",\n\t\t\t\t\t\t\t\t\"type\": \"bytes\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t],\n\t\t\t\t\t\t\"name\": \"order\",\n\t\t\t\t\t\t\"type\": \"tuple\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"takerAssetFillAmount\",\n\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"signature\",\n\t\t\t\t\t\t\"type\": \"bytes\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"name\": \"fillOrKillOrder\",\n\t\t\t\t\"outputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"components\": [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerAssetFilledAmount\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerAssetFilledAmount\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerFeePaid\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerFeePaid\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t],\n\t\t\t\t\t\t\"name\": \"fillResults\",\n\t\t\t\t\t\t\"type\": \"tuple\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"payable\": false,\n\t\t\t\t\"stateMutability\": \"nonpayable\",\n\t\t\t\t\"type\": \"function\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"constant\": false,\n\t\t\t\t\"inputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"validatorAddress\",\n\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"approval\",\n\t\t\t\t\t\t\"type\": \"bool\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"name\": \"setSignatureValidatorApproval\",\n\t\t\t\t\"outputs\": [],\n\t\t\t\t\"payable\": false,\n\t\t\t\t\"stateMutability\": \"nonpayable\",\n\t\t\t\t\"type\": \"function\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"constant\": true,\n\t\t\t\t\"inputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"\",\n\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"\",\n\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"name\": \"allowedValidators\",\n\t\t\t\t\"outputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"\",\n\t\t\t\t\t\t\"type\": \"bool\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"payable\": false,\n\t\t\t\t\"stateMutability\": \"view\",\n\t\t\t\t\"type\": \"function\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"constant\": false,\n\t\t\t\t\"inputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"components\": [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerAddress\",\n\t\t\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerAddress\",\n\t\t\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"feeRecipientAddress\",\n\t\t\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"senderAddress\",\n\t\t\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerAssetAmount\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerAssetAmount\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerFee\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerFee\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"expirationTimeSeconds\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"salt\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerAssetData\",\n\t\t\t\t\t\t\t\t\"type\": \"bytes\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerAssetData\",\n\t\t\t\t\t\t\t\t\"type\": \"bytes\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t],\n\t\t\t\t\t\t\"name\": \"orders\",\n\t\t\t\t\t\t\"type\": \"tuple[]\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"takerAssetFillAmount\",\n\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"signatures\",\n\t\t\t\t\t\t\"type\": \"bytes[]\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"name\": \"marketSellOrders\",\n\t\t\t\t\"outputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"components\": [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerAssetFilledAmount\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerAssetFilledAmount\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerFeePaid\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerFeePaid\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t],\n\t\t\t\t\t\t\"name\": \"totalFillResults\",\n\t\t\t\t\t\t\"type\": \"tuple\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"payable\": false,\n\t\t\t\t\"stateMutability\": \"nonpayable\",\n\t\t\t\t\"type\": \"function\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"constant\": true,\n\t\t\t\t\"inputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"components\": [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerAddress\",\n\t\t\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerAddress\",\n\t\t\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"feeRecipientAddress\",\n\t\t\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"senderAddress\",\n\t\t\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerAssetAmount\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerAssetAmount\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerFee\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerFee\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"expirationTimeSeconds\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"salt\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerAssetData\",\n\t\t\t\t\t\t\t\t\"type\": \"bytes\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerAssetData\",\n\t\t\t\t\t\t\t\t\"type\": \"bytes\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t],\n\t\t\t\t\t\t\"name\": \"orders\",\n\t\t\t\t\t\t\"type\": \"tuple[]\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"name\": \"getOrdersInfo\",\n\t\t\t\t\"outputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"components\": [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"orderStatus\",\n\t\t\t\t\t\t\t\t\"type\": \"uint8\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"orderHash\",\n\t\t\t\t\t\t\t\t\"type\": \"bytes32\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"orderTakerAssetFilledAmount\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t],\n\t\t\t\t\t\t\"name\": \"\",\n\t\t\t\t\t\t\"type\": \"tuple[]\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"payable\": false,\n\t\t\t\t\"stateMutability\": \"view\",\n\t\t\t\t\"type\": \"function\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"constant\": true,\n\t\t\t\t\"inputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"\",\n\t\t\t\t\t\t\"type\": \"bytes32\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"\",\n\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"name\": \"preSigned\",\n\t\t\t\t\"outputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"\",\n\t\t\t\t\t\t\"type\": \"bool\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"payable\": false,\n\t\t\t\t\"stateMutability\": \"view\",\n\t\t\t\t\"type\": \"function\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"constant\": true,\n\t\t\t\t\"inputs\": [],\n\t\t\t\t\"name\": \"owner\",\n\t\t\t\t\"outputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"\",\n\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"payable\": false,\n\t\t\t\t\"stateMutability\": \"view\",\n\t\t\t\t\"type\": \"function\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"constant\": true,\n\t\t\t\t\"inputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"hash\",\n\t\t\t\t\t\t\"type\": \"bytes32\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"signerAddress\",\n\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"signature\",\n\t\t\t\t\t\t\"type\": \"bytes\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"name\": \"isValidSignature\",\n\t\t\t\t\"outputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"isValid\",\n\t\t\t\t\t\t\"type\": \"bool\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"payable\": false,\n\t\t\t\t\"stateMutability\": \"view\",\n\t\t\t\t\"type\": \"function\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"constant\": false,\n\t\t\t\t\"inputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"components\": [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerAddress\",\n\t\t\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerAddress\",\n\t\t\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"feeRecipientAddress\",\n\t\t\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"senderAddress\",\n\t\t\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerAssetAmount\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerAssetAmount\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerFee\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerFee\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"expirationTimeSeconds\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"salt\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerAssetData\",\n\t\t\t\t\t\t\t\t\"type\": \"bytes\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerAssetData\",\n\t\t\t\t\t\t\t\t\"type\": \"bytes\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t],\n\t\t\t\t\t\t\"name\": \"orders\",\n\t\t\t\t\t\t\"type\": \"tuple[]\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"makerAssetFillAmount\",\n\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"signatures\",\n\t\t\t\t\t\t\"type\": \"bytes[]\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"name\": \"marketBuyOrdersNoThrow\",\n\t\t\t\t\"outputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"components\": [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerAssetFilledAmount\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerAssetFilledAmount\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerFeePaid\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerFeePaid\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t],\n\t\t\t\t\t\t\"name\": \"totalFillResults\",\n\t\t\t\t\t\t\"type\": \"tuple\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"payable\": false,\n\t\t\t\t\"stateMutability\": \"nonpayable\",\n\t\t\t\t\"type\": \"function\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"constant\": false,\n\t\t\t\t\"inputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"components\": [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerAddress\",\n\t\t\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerAddress\",\n\t\t\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"feeRecipientAddress\",\n\t\t\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"senderAddress\",\n\t\t\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerAssetAmount\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerAssetAmount\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerFee\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerFee\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"expirationTimeSeconds\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"salt\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerAssetData\",\n\t\t\t\t\t\t\t\t\"type\": \"bytes\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerAssetData\",\n\t\t\t\t\t\t\t\t\"type\": \"bytes\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t],\n\t\t\t\t\t\t\"name\": \"order\",\n\t\t\t\t\t\t\"type\": \"tuple\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"takerAssetFillAmount\",\n\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"signature\",\n\t\t\t\t\t\t\"type\": \"bytes\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"name\": \"fillOrder\",\n\t\t\t\t\"outputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"components\": [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerAssetFilledAmount\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerAssetFilledAmount\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerFeePaid\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerFeePaid\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t],\n\t\t\t\t\t\t\"name\": \"fillResults\",\n\t\t\t\t\t\t\"type\": \"tuple\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"payable\": false,\n\t\t\t\t\"stateMutability\": \"nonpayable\",\n\t\t\t\t\"type\": \"function\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"constant\": false,\n\t\t\t\t\"inputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"salt\",\n\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"signerAddress\",\n\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"data\",\n\t\t\t\t\t\t\"type\": \"bytes\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"signature\",\n\t\t\t\t\t\t\"type\": \"bytes\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"name\": \"executeTransaction\",\n\t\t\t\t\"outputs\": [],\n\t\t\t\t\"payable\": false,\n\t\t\t\t\"stateMutability\": \"nonpayable\",\n\t\t\t\t\"type\": \"function\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"constant\": false,\n\t\t\t\t\"inputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"assetProxy\",\n\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"name\": \"registerAssetProxy\",\n\t\t\t\t\"outputs\": [],\n\t\t\t\t\"payable\": false,\n\t\t\t\t\"stateMutability\": \"nonpayable\",\n\t\t\t\t\"type\": \"function\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"constant\": true,\n\t\t\t\t\"inputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"components\": [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerAddress\",\n\t\t\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerAddress\",\n\t\t\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"feeRecipientAddress\",\n\t\t\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"senderAddress\",\n\t\t\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerAssetAmount\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerAssetAmount\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerFee\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerFee\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"expirationTimeSeconds\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"salt\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerAssetData\",\n\t\t\t\t\t\t\t\t\"type\": \"bytes\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerAssetData\",\n\t\t\t\t\t\t\t\t\"type\": \"bytes\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t],\n\t\t\t\t\t\t\"name\": \"order\",\n\t\t\t\t\t\t\"type\": \"tuple\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"name\": \"getOrderInfo\",\n\t\t\t\t\"outputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"components\": [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"orderStatus\",\n\t\t\t\t\t\t\t\t\"type\": \"uint8\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"orderHash\",\n\t\t\t\t\t\t\t\t\"type\": \"bytes32\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"orderTakerAssetFilledAmount\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t],\n\t\t\t\t\t\t\"name\": \"orderInfo\",\n\t\t\t\t\t\t\"type\": \"tuple\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"payable\": false,\n\t\t\t\t\"stateMutability\": \"view\",\n\t\t\t\t\"type\": \"function\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"constant\": false,\n\t\t\t\t\"inputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"components\": [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerAddress\",\n\t\t\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerAddress\",\n\t\t\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"feeRecipientAddress\",\n\t\t\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"senderAddress\",\n\t\t\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerAssetAmount\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerAssetAmount\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerFee\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerFee\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"expirationTimeSeconds\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"salt\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerAssetData\",\n\t\t\t\t\t\t\t\t\"type\": \"bytes\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerAssetData\",\n\t\t\t\t\t\t\t\t\"type\": \"bytes\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t],\n\t\t\t\t\t\t\"name\": \"order\",\n\t\t\t\t\t\t\"type\": \"tuple\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"name\": \"cancelOrder\",\n\t\t\t\t\"outputs\": [],\n\t\t\t\t\"payable\": false,\n\t\t\t\t\"stateMutability\": \"nonpayable\",\n\t\t\t\t\"type\": \"function\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"constant\": true,\n\t\t\t\t\"inputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"\",\n\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"\",\n\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"name\": \"orderEpoch\",\n\t\t\t\t\"outputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"\",\n\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"payable\": false,\n\t\t\t\t\"stateMutability\": \"view\",\n\t\t\t\t\"type\": \"function\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"constant\": true,\n\t\t\t\t\"inputs\": [],\n\t\t\t\t\"name\": \"ZRX_ASSET_DATA\",\n\t\t\t\t\"outputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"\",\n\t\t\t\t\t\t\"type\": \"bytes\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"payable\": false,\n\t\t\t\t\"stateMutability\": \"view\",\n\t\t\t\t\"type\": \"function\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"constant\": false,\n\t\t\t\t\"inputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"components\": [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerAddress\",\n\t\t\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerAddress\",\n\t\t\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"feeRecipientAddress\",\n\t\t\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"senderAddress\",\n\t\t\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerAssetAmount\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerAssetAmount\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerFee\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerFee\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"expirationTimeSeconds\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"salt\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerAssetData\",\n\t\t\t\t\t\t\t\t\"type\": \"bytes\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerAssetData\",\n\t\t\t\t\t\t\t\t\"type\": \"bytes\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t],\n\t\t\t\t\t\t\"name\": \"orders\",\n\t\t\t\t\t\t\"type\": \"tuple[]\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"takerAssetFillAmount\",\n\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"signatures\",\n\t\t\t\t\t\t\"type\": \"bytes[]\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"name\": \"marketSellOrdersNoThrow\",\n\t\t\t\t\"outputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"components\": [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerAssetFilledAmount\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerAssetFilledAmount\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerFeePaid\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerFeePaid\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t],\n\t\t\t\t\t\t\"name\": \"totalFillResults\",\n\t\t\t\t\t\t\"type\": \"tuple\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"payable\": false,\n\t\t\t\t\"stateMutability\": \"nonpayable\",\n\t\t\t\t\"type\": \"function\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"constant\": true,\n\t\t\t\t\"inputs\": [],\n\t\t\t\t\"name\": \"EIP712_DOMAIN_HASH\",\n\t\t\t\t\"outputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"\",\n\t\t\t\t\t\t\"type\": \"bytes32\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"payable\": false,\n\t\t\t\t\"stateMutability\": \"view\",\n\t\t\t\t\"type\": \"function\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"constant\": false,\n\t\t\t\t\"inputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"components\": [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerAddress\",\n\t\t\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerAddress\",\n\t\t\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"feeRecipientAddress\",\n\t\t\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"senderAddress\",\n\t\t\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerAssetAmount\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerAssetAmount\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerFee\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerFee\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"expirationTimeSeconds\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"salt\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerAssetData\",\n\t\t\t\t\t\t\t\t\"type\": \"bytes\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerAssetData\",\n\t\t\t\t\t\t\t\t\"type\": \"bytes\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t],\n\t\t\t\t\t\t\"name\": \"orders\",\n\t\t\t\t\t\t\"type\": \"tuple[]\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"makerAssetFillAmount\",\n\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"signatures\",\n\t\t\t\t\t\t\"type\": \"bytes[]\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"name\": \"marketBuyOrders\",\n\t\t\t\t\"outputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"components\": [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerAssetFilledAmount\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerAssetFilledAmount\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"makerFeePaid\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"name\": \"takerFeePaid\",\n\t\t\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t],\n\t\t\t\t\t\t\"name\": \"totalFillResults\",\n\t\t\t\t\t\t\"type\": \"tuple\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"payable\": false,\n\t\t\t\t\"stateMutability\": \"nonpayable\",\n\t\t\t\t\"type\": \"function\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"constant\": true,\n\t\t\t\t\"inputs\": [],\n\t\t\t\t\"name\": \"currentContextAddress\",\n\t\t\t\t\"outputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"\",\n\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"payable\": false,\n\t\t\t\t\"stateMutability\": \"view\",\n\t\t\t\t\"type\": \"function\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"constant\": false,\n\t\t\t\t\"inputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"newOwner\",\n\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"name\": \"transferOwnership\",\n\t\t\t\t\"outputs\": [],\n\t\t\t\t\"payable\": false,\n\t\t\t\t\"stateMutability\": \"nonpayable\",\n\t\t\t\t\"type\": \"function\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"constant\": true,\n\t\t\t\t\"inputs\": [],\n\t\t\t\t\"name\": \"VERSION\",\n\t\t\t\t\"outputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"\",\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"payable\": false,\n\t\t\t\t\"stateMutability\": \"view\",\n\t\t\t\t\"type\": \"function\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"inputs\": [],\n\t\t\t\t\"payable\": false,\n\t\t\t\t\"stateMutability\": \"nonpayable\",\n\t\t\t\t\"type\": \"constructor\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"anonymous\": false,\n\t\t\t\t\"inputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"indexed\": true,\n\t\t\t\t\t\t\"name\": \"signerAddress\",\n\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"indexed\": true,\n\t\t\t\t\t\t\"name\": \"validatorAddress\",\n\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"indexed\": false,\n\t\t\t\t\t\t\"name\": \"approved\",\n\t\t\t\t\t\t\"type\": \"bool\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"name\": \"SignatureValidatorApproval\",\n\t\t\t\t\"type\": \"event\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"anonymous\": false,\n\t\t\t\t\"inputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"indexed\": true,\n\t\t\t\t\t\t\"name\": \"makerAddress\",\n\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"indexed\": true,\n\t\t\t\t\t\t\"name\": \"feeRecipientAddress\",\n\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"indexed\": false,\n\t\t\t\t\t\t\"name\": \"takerAddress\",\n\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"indexed\": false,\n\t\t\t\t\t\t\"name\": \"senderAddress\",\n\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"indexed\": false,\n\t\t\t\t\t\t\"name\": \"makerAssetFilledAmount\",\n\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"indexed\": false,\n\t\t\t\t\t\t\"name\": \"takerAssetFilledAmount\",\n\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"indexed\": false,\n\t\t\t\t\t\t\"name\": \"makerFeePaid\",\n\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"indexed\": false,\n\t\t\t\t\t\t\"name\": \"takerFeePaid\",\n\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"indexed\": true,\n\t\t\t\t\t\t\"name\": \"orderHash\",\n\t\t\t\t\t\t\"type\": \"bytes32\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"indexed\": false,\n\t\t\t\t\t\t\"name\": \"makerAssetData\",\n\t\t\t\t\t\t\"type\": \"bytes\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"indexed\": false,\n\t\t\t\t\t\t\"name\": \"takerAssetData\",\n\t\t\t\t\t\t\"type\": \"bytes\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"name\": \"Fill\",\n\t\t\t\t\"type\": \"event\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"anonymous\": false,\n\t\t\t\t\"inputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"indexed\": true,\n\t\t\t\t\t\t\"name\": \"makerAddress\",\n\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"indexed\": true,\n\t\t\t\t\t\t\"name\": \"feeRecipientAddress\",\n\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"indexed\": false,\n\t\t\t\t\t\t\"name\": \"senderAddress\",\n\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"indexed\": true,\n\t\t\t\t\t\t\"name\": \"orderHash\",\n\t\t\t\t\t\t\"type\": \"bytes32\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"indexed\": false,\n\t\t\t\t\t\t\"name\": \"makerAssetData\",\n\t\t\t\t\t\t\"type\": \"bytes\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"indexed\": false,\n\t\t\t\t\t\t\"name\": \"takerAssetData\",\n\t\t\t\t\t\t\"type\": \"bytes\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"name\": \"Cancel\",\n\t\t\t\t\"type\": \"event\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"anonymous\": false,\n\t\t\t\t\"inputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"indexed\": true,\n\t\t\t\t\t\t\"name\": \"makerAddress\",\n\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"indexed\": true,\n\t\t\t\t\t\t\"name\": \"senderAddress\",\n\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"indexed\": false,\n\t\t\t\t\t\t\"name\": \"orderEpoch\",\n\t\t\t\t\t\t\"type\": \"uint256\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"name\": \"CancelUpTo\",\n\t\t\t\t\"type\": \"event\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"anonymous\": false,\n\t\t\t\t\"inputs\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"indexed\": false,\n\t\t\t\t\t\t\"name\": \"id\",\n\t\t\t\t\t\t\"type\": \"bytes4\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"indexed\": false,\n\t\t\t\t\t\t\"name\": \"assetProxy\",\n\t\t\t\t\t\t\"type\": \"address\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"name\": \"AssetProxyRegistered\",\n\t\t\t\t\"type\": \"event\"\n\t\t\t}\n\t\t]"
  },
  {
    "path": "pymaker/abi/Flapper.abi",
    "content": "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"vat_\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"gem_\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"lot\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"bid\",\"type\":\"uint256\"}],\"name\":\"Kick\",\"type\":\"event\"},{\"anonymous\":true,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes4\",\"name\":\"sig\",\"type\":\"bytes4\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"arg1\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"arg2\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"LogNote\",\"type\":\"event\"},{\"constant\":true,\"inputs\":[],\"name\":\"beg\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"bids\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"bid\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"lot\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"guy\",\"type\":\"address\"},{\"internalType\":\"uint48\",\"name\":\"tic\",\"type\":\"uint48\"},{\"internalType\":\"uint48\",\"name\":\"end\",\"type\":\"uint48\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"rad\",\"type\":\"uint256\"}],\"name\":\"cage\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"deal\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"}],\"name\":\"deny\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"what\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"data\",\"type\":\"uint256\"}],\"name\":\"file\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"gem\",\"outputs\":[{\"internalType\":\"contract GemLike\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"lot\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"bid\",\"type\":\"uint256\"}],\"name\":\"kick\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"kicks\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"live\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"}],\"name\":\"rely\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"tau\",\"outputs\":[{\"internalType\":\"uint48\",\"name\":\"\",\"type\":\"uint48\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"lot\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"bid\",\"type\":\"uint256\"}],\"name\":\"tend\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"tick\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"ttl\",\"outputs\":[{\"internalType\":\"uint48\",\"name\":\"\",\"type\":\"uint48\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"vat\",\"outputs\":[{\"internalType\":\"contract VatLike\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"wards\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"yank\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]\n"
  },
  {
    "path": "pymaker/abi/Flipper.abi",
    "content": "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"vat_\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"cat_\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"ilk_\",\"type\":\"bytes32\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"lot\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"bid\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"tab\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"gal\",\"type\":\"address\"}],\"name\":\"Kick\",\"type\":\"event\"},{\"anonymous\":true,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes4\",\"name\":\"sig\",\"type\":\"bytes4\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"arg1\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"arg2\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"LogNote\",\"type\":\"event\"},{\"constant\":true,\"inputs\":[],\"name\":\"beg\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"bids\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"bid\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"lot\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"guy\",\"type\":\"address\"},{\"internalType\":\"uint48\",\"name\":\"tic\",\"type\":\"uint48\"},{\"internalType\":\"uint48\",\"name\":\"end\",\"type\":\"uint48\"},{\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"gal\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"tab\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"cat\",\"outputs\":[{\"internalType\":\"contract CatLike\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"deal\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"lot\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"bid\",\"type\":\"uint256\"}],\"name\":\"dent\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"}],\"name\":\"deny\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"what\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"data\",\"type\":\"uint256\"}],\"name\":\"file\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"what\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"data\",\"type\":\"address\"}],\"name\":\"file\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"ilk\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"gal\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"tab\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"lot\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"bid\",\"type\":\"uint256\"}],\"name\":\"kick\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"kicks\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"}],\"name\":\"rely\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"tau\",\"outputs\":[{\"internalType\":\"uint48\",\"name\":\"\",\"type\":\"uint48\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"lot\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"bid\",\"type\":\"uint256\"}],\"name\":\"tend\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"tick\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"ttl\",\"outputs\":[{\"internalType\":\"uint48\",\"name\":\"\",\"type\":\"uint48\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"vat\",\"outputs\":[{\"internalType\":\"contract VatLike\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"wards\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"yank\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]\n"
  },
  {
    "path": "pymaker/abi/Flopper.abi",
    "content": "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"vat_\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"gem_\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"lot\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"bid\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"gal\",\"type\":\"address\"}],\"name\":\"Kick\",\"type\":\"event\"},{\"anonymous\":true,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes4\",\"name\":\"sig\",\"type\":\"bytes4\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"arg1\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"arg2\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"LogNote\",\"type\":\"event\"},{\"constant\":true,\"inputs\":[],\"name\":\"beg\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"bids\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"bid\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"lot\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"guy\",\"type\":\"address\"},{\"internalType\":\"uint48\",\"name\":\"tic\",\"type\":\"uint48\"},{\"internalType\":\"uint48\",\"name\":\"end\",\"type\":\"uint48\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"cage\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"deal\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"lot\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"bid\",\"type\":\"uint256\"}],\"name\":\"dent\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"}],\"name\":\"deny\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"what\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"data\",\"type\":\"uint256\"}],\"name\":\"file\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"gem\",\"outputs\":[{\"internalType\":\"contract GemLike\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"gal\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"lot\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"bid\",\"type\":\"uint256\"}],\"name\":\"kick\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"kicks\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"live\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"pad\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"}],\"name\":\"rely\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"tau\",\"outputs\":[{\"internalType\":\"uint48\",\"name\":\"\",\"type\":\"uint48\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"tick\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"ttl\",\"outputs\":[{\"internalType\":\"uint48\",\"name\":\"\",\"type\":\"uint48\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"vat\",\"outputs\":[{\"internalType\":\"contract VatLike\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"vow\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"wards\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"yank\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]\n"
  },
  {
    "path": "pymaker/abi/GemJoin.abi",
    "content": "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"vat_\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"ilk_\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"gem_\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":true,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes4\",\"name\":\"sig\",\"type\":\"bytes4\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"arg1\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"arg2\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"LogNote\",\"type\":\"event\"},{\"constant\":false,\"inputs\":[],\"name\":\"cage\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"dec\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"}],\"name\":\"deny\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"exit\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"gem\",\"outputs\":[{\"internalType\":\"contract GemLike\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"ilk\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"join\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"live\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"}],\"name\":\"rely\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"vat\",\"outputs\":[{\"internalType\":\"contract VatLike\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"wards\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"}]\n"
  },
  {
    "path": "pymaker/abi/GemJoin5.abi",
    "content": "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"vat_\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"ilk_\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"gem_\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":true,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes4\",\"name\":\"sig\",\"type\":\"bytes4\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"arg1\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"arg2\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"LogNote\",\"type\":\"event\"},{\"constant\":false,\"inputs\":[],\"name\":\"cage\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"dec\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"}],\"name\":\"deny\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"guy\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amt\",\"type\":\"uint256\"}],\"name\":\"exit\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"gem\",\"outputs\":[{\"internalType\":\"contract GemLike\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"ilk\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"urn\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amt\",\"type\":\"uint256\"}],\"name\":\"join\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"live\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"}],\"name\":\"rely\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"vat\",\"outputs\":[{\"internalType\":\"contract VatLike\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"wards\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"}]\n"
  },
  {
    "path": "pymaker/abi/Jug.abi",
    "content": "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"vat_\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":true,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes4\",\"name\":\"sig\",\"type\":\"bytes4\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"arg1\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"arg2\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"LogNote\",\"type\":\"event\"},{\"constant\":true,\"inputs\":[],\"name\":\"base\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"}],\"name\":\"deny\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"ilk\",\"type\":\"bytes32\"}],\"name\":\"drip\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"rate\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"ilk\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"what\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"data\",\"type\":\"uint256\"}],\"name\":\"file\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"what\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"data\",\"type\":\"uint256\"}],\"name\":\"file\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"what\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"data\",\"type\":\"address\"}],\"name\":\"file\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"ilks\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"duty\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"rho\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"ilk\",\"type\":\"bytes32\"}],\"name\":\"init\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"}],\"name\":\"rely\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"vat\",\"outputs\":[{\"internalType\":\"contract VatLike\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"vow\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"wards\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"}]\n"
  },
  {
    "path": "pymaker/abi/MakerOtcSupportMethods.abi",
    "content": "[{\"constant\":true,\"inputs\":[{\"name\":\"otc\",\"type\":\"address\"},{\"name\":\"payToken\",\"type\":\"address\"},{\"name\":\"buyToken\",\"type\":\"address\"}],\"name\":\"getOffers\",\"outputs\":[{\"name\":\"ids\",\"type\":\"uint256[100]\"},{\"name\":\"payAmts\",\"type\":\"uint256[100]\"},{\"name\":\"buyAmts\",\"type\":\"uint256[100]\"},{\"name\":\"owners\",\"type\":\"address[100]\"},{\"name\":\"timestamps\",\"type\":\"uint256[100]\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"otc\",\"type\":\"address\"},{\"name\":\"buyToken\",\"type\":\"address\"},{\"name\":\"buyAmt\",\"type\":\"uint256\"},{\"name\":\"payToken\",\"type\":\"address\"}],\"name\":\"getOffersAmountToBuyAll\",\"outputs\":[{\"name\":\"ordersToTake\",\"type\":\"uint256\"},{\"name\":\"takesPartialOrder\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"otc\",\"type\":\"address\"},{\"name\":\"offerId\",\"type\":\"uint256\"}],\"name\":\"getOffers\",\"outputs\":[{\"name\":\"ids\",\"type\":\"uint256[100]\"},{\"name\":\"payAmts\",\"type\":\"uint256[100]\"},{\"name\":\"buyAmts\",\"type\":\"uint256[100]\"},{\"name\":\"owners\",\"type\":\"address[100]\"},{\"name\":\"timestamps\",\"type\":\"uint256[100]\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"otc\",\"type\":\"address\"},{\"name\":\"payToken\",\"type\":\"address\"},{\"name\":\"payAmt\",\"type\":\"uint256\"},{\"name\":\"buyToken\",\"type\":\"address\"}],\"name\":\"getOffersAmountToSellAll\",\"outputs\":[{\"name\":\"ordersToTake\",\"type\":\"uint256\"},{\"name\":\"takesPartialOrder\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"}]"
  },
  {
    "path": "pymaker/abi/MatchingMarket.abi",
    "content": "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_dustToken\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_dustLimit\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"_priceOracle\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"id\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"pair\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"maker\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"contract ERC20\",\"name\":\"pay_gem\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"contract ERC20\",\"name\":\"buy_gem\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint128\",\"name\":\"pay_amt\",\"type\":\"uint128\"},{\"indexed\":false,\"internalType\":\"uint128\",\"name\":\"buy_amt\",\"type\":\"uint128\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"timestamp\",\"type\":\"uint64\"}],\"name\":\"LogBump\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"keeper\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"LogDelete\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"keeper\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"LogInsert\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"LogItemUpdate\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"id\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"pair\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"maker\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"contract ERC20\",\"name\":\"pay_gem\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"contract ERC20\",\"name\":\"buy_gem\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint128\",\"name\":\"pay_amt\",\"type\":\"uint128\"},{\"indexed\":false,\"internalType\":\"uint128\",\"name\":\"buy_amt\",\"type\":\"uint128\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"timestamp\",\"type\":\"uint64\"}],\"name\":\"LogKill\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"id\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"pair\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"maker\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"contract ERC20\",\"name\":\"pay_gem\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"contract ERC20\",\"name\":\"buy_gem\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint128\",\"name\":\"pay_amt\",\"type\":\"uint128\"},{\"indexed\":false,\"internalType\":\"uint128\",\"name\":\"buy_amt\",\"type\":\"uint128\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"timestamp\",\"type\":\"uint64\"}],\"name\":\"LogMake\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"pay_gem\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"min_amount\",\"type\":\"uint256\"}],\"name\":\"LogMinSell\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"LogSortedOffer\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"id\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"pair\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"maker\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"contract ERC20\",\"name\":\"pay_gem\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"contract ERC20\",\"name\":\"buy_gem\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"taker\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint128\",\"name\":\"take_amt\",\"type\":\"uint128\"},{\"indexed\":false,\"internalType\":\"uint128\",\"name\":\"give_amt\",\"type\":\"uint128\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"timestamp\",\"type\":\"uint64\"}],\"name\":\"LogTake\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"pay_amt\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"pay_gem\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"buy_amt\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"buy_gem\",\"type\":\"address\"}],\"name\":\"LogTrade\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"LogUnsortedOffer\",\"type\":\"event\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"_best\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"_dust\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"_near\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"_rank\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"next\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"prev\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"delb\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"_span\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"id_\",\"type\":\"bytes32\"}],\"name\":\"bump\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"buy\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"contract ERC20\",\"name\":\"buy_gem\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"buy_amt\",\"type\":\"uint256\"},{\"internalType\":\"contract ERC20\",\"name\":\"pay_gem\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"max_fill_amount\",\"type\":\"uint256\"}],\"name\":\"buyAllAmount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"fill_amt\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"cancel\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"del_rank\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"dustLimit\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"dustToken\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"contract ERC20\",\"name\":\"sell_gem\",\"type\":\"address\"},{\"internalType\":\"contract ERC20\",\"name\":\"buy_gem\",\"type\":\"address\"}],\"name\":\"getBestOffer\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"getBetterOffer\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"contract ERC20\",\"name\":\"buy_gem\",\"type\":\"address\"},{\"internalType\":\"contract ERC20\",\"name\":\"pay_gem\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"pay_amt\",\"type\":\"uint256\"}],\"name\":\"getBuyAmount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"fill_amt\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"getFirstUnsortedOffer\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"contract ERC20\",\"name\":\"pay_gem\",\"type\":\"address\"}],\"name\":\"getMinSell\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"getNextUnsortedOffer\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"getOffer\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"contract ERC20\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"contract ERC20\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"contract ERC20\",\"name\":\"sell_gem\",\"type\":\"address\"},{\"internalType\":\"contract ERC20\",\"name\":\"buy_gem\",\"type\":\"address\"}],\"name\":\"getOfferCount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"getOwner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"contract ERC20\",\"name\":\"pay_gem\",\"type\":\"address\"},{\"internalType\":\"contract ERC20\",\"name\":\"buy_gem\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"buy_amt\",\"type\":\"uint256\"}],\"name\":\"getPayAmount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"fill_amt\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"getWorseOffer\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"pos\",\"type\":\"uint256\"}],\"name\":\"insert\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"isActive\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"active\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"isOfferSorted\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"id\",\"type\":\"bytes32\"}],\"name\":\"kill\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"last_offer_id\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"contract ERC20\",\"name\":\"pay_gem\",\"type\":\"address\"},{\"internalType\":\"contract ERC20\",\"name\":\"buy_gem\",\"type\":\"address\"},{\"internalType\":\"uint128\",\"name\":\"pay_amt\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"buy_amt\",\"type\":\"uint128\"}],\"name\":\"make\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"pay_amt\",\"type\":\"uint256\"},{\"internalType\":\"contract ERC20\",\"name\":\"pay_gem\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"buy_amt\",\"type\":\"uint256\"},{\"internalType\":\"contract ERC20\",\"name\":\"buy_gem\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"pos\",\"type\":\"uint256\"}],\"name\":\"offer\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"pay_amt\",\"type\":\"uint256\"},{\"internalType\":\"contract ERC20\",\"name\":\"pay_gem\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"buy_amt\",\"type\":\"uint256\"},{\"internalType\":\"contract ERC20\",\"name\":\"buy_gem\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"pos\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"rounding\",\"type\":\"bool\"}],\"name\":\"offer\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"pay_amt\",\"type\":\"uint256\"},{\"internalType\":\"contract ERC20\",\"name\":\"pay_gem\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"buy_amt\",\"type\":\"uint256\"},{\"internalType\":\"contract ERC20\",\"name\":\"buy_gem\",\"type\":\"address\"}],\"name\":\"offer\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"offers\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"pay_amt\",\"type\":\"uint256\"},{\"internalType\":\"contract ERC20\",\"name\":\"pay_gem\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"buy_amt\",\"type\":\"uint256\"},{\"internalType\":\"contract ERC20\",\"name\":\"buy_gem\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"timestamp\",\"type\":\"uint64\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"priceOracle\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"contract ERC20\",\"name\":\"pay_gem\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"pay_amt\",\"type\":\"uint256\"},{\"internalType\":\"contract ERC20\",\"name\":\"buy_gem\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"min_fill_amount\",\"type\":\"uint256\"}],\"name\":\"sellAllAmount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"fill_amt\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"contract ERC20\",\"name\":\"pay_gem\",\"type\":\"address\"}],\"name\":\"setMinSell\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"id\",\"type\":\"bytes32\"},{\"internalType\":\"uint128\",\"name\":\"maxTakeAmount\",\"type\":\"uint128\"}],\"name\":\"take\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]\n"
  },
  {
    "path": "pymaker/abi/OSM.abi",
    "content": "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"src_\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":true,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes4\",\"name\":\"sig\",\"type\":\"bytes4\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"arg1\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"arg2\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"LogNote\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"val\",\"type\":\"bytes32\"}],\"name\":\"LogValue\",\"type\":\"event\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"bud\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"src_\",\"type\":\"address\"}],\"name\":\"change\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"}],\"name\":\"deny\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"a\",\"type\":\"address[]\"}],\"name\":\"diss\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"a\",\"type\":\"address\"}],\"name\":\"diss\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"hop\",\"outputs\":[{\"internalType\":\"uint16\",\"name\":\"\",\"type\":\"uint16\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"a\",\"type\":\"address[]\"}],\"name\":\"kiss\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"a\",\"type\":\"address\"}],\"name\":\"kiss\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"pass\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"ok\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"peek\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"},{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"peep\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"},{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"poke\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"read\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"}],\"name\":\"rely\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"src\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"start\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint16\",\"name\":\"ts\",\"type\":\"uint16\"}],\"name\":\"step\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"stop\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"stopped\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"void\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"wards\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"zzz\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"}]\n"
  },
  {
    "path": "pymaker/abi/Pit.abi",
    "content": "[{\"constant\":false,\"inputs\":[{\"name\":\"ilk\",\"type\":\"bytes32\"},{\"name\":\"what\",\"type\":\"bytes32\"},{\"name\":\"data\",\"type\":\"uint256\"}],\"name\":\"file\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"what\",\"type\":\"bytes32\"},{\"name\":\"data\",\"type\":\"uint256\"}],\"name\":\"file\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"vat\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"ilk\",\"type\":\"bytes32\"},{\"name\":\"dink\",\"type\":\"int256\"},{\"name\":\"dart\",\"type\":\"int256\"}],\"name\":\"frob\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"guy\",\"type\":\"address\"}],\"name\":\"rely\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"live\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"guy\",\"type\":\"address\"}],\"name\":\"deny\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"Line\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"\",\"type\":\"address\"}],\"name\":\"wards\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"ilks\",\"outputs\":[{\"name\":\"spot\",\"type\":\"uint256\"},{\"name\":\"line\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"name\":\"vat_\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"ilk\",\"type\":\"bytes32\"},{\"indexed\":true,\"name\":\"urn\",\"type\":\"bytes32\"},{\"indexed\":false,\"name\":\"ink\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"art\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"dink\",\"type\":\"int256\"},{\"indexed\":false,\"name\":\"dart\",\"type\":\"int256\"},{\"indexed\":false,\"name\":\"iInk\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"iArt\",\"type\":\"uint256\"}],\"name\":\"Frob\",\"type\":\"event\"},{\"anonymous\":true,\"inputs\":[{\"indexed\":true,\"name\":\"sig\",\"type\":\"bytes4\"},{\"indexed\":true,\"name\":\"guy\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"foo\",\"type\":\"bytes32\"},{\"indexed\":true,\"name\":\"bar\",\"type\":\"bytes32\"},{\"indexed\":false,\"name\":\"wad\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"fax\",\"type\":\"bytes\"}],\"name\":\"LogNote\",\"type\":\"event\"}]"
  },
  {
    "path": "pymaker/abi/Pot.abi",
    "content": "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"vat_\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":true,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes4\",\"name\":\"sig\",\"type\":\"bytes4\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"arg1\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"arg2\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"LogNote\",\"type\":\"event\"},{\"constant\":true,\"inputs\":[],\"name\":\"Pie\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"cage\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"chi\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"guy\",\"type\":\"address\"}],\"name\":\"deny\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"drip\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"tmp\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"dsr\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"exit\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"what\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"data\",\"type\":\"uint256\"}],\"name\":\"file\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"what\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"}],\"name\":\"file\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"join\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"live\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"pie\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"guy\",\"type\":\"address\"}],\"name\":\"rely\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"rho\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"vat\",\"outputs\":[{\"internalType\":\"contract VatLike\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"vow\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"wards\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"}]\n"
  },
  {
    "path": "pymaker/abi/ProxyRegistry.abi",
    "content": "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"factory_\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"constant\":false,\"inputs\":[],\"name\":\"build\",\"outputs\":[{\"internalType\":\"address payable\",\"name\":\"proxy\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"build\",\"outputs\":[{\"internalType\":\"address payable\",\"name\":\"proxy\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"proxies\",\"outputs\":[{\"internalType\":\"contract DSProxy\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"}]\n"
  },
  {
    "path": "pymaker/abi/SaiTap.abi",
    "content": "[{\"constant\":false,\"inputs\":[],\"name\":\"heal\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"sin\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"skr\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"owner_\",\"type\":\"address\"}],\"name\":\"setOwner\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"vent\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"cash\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"woe\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"tub\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"mock\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"bid\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"joy\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"s2s\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"off\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"vox\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"gap\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"fog\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"authority_\",\"type\":\"address\"}],\"name\":\"setAuthority\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"sai\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"param\",\"type\":\"bytes32\"},{\"name\":\"val\",\"type\":\"uint256\"}],\"name\":\"mold\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"fix_\",\"type\":\"uint256\"}],\"name\":\"cage\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"fix\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"bust\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"boom\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"authority\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"ask\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"name\":\"tub_\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":true,\"inputs\":[{\"indexed\":true,\"name\":\"sig\",\"type\":\"bytes4\"},{\"indexed\":true,\"name\":\"guy\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"foo\",\"type\":\"bytes32\"},{\"indexed\":true,\"name\":\"bar\",\"type\":\"bytes32\"},{\"indexed\":false,\"name\":\"wad\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"fax\",\"type\":\"bytes\"}],\"name\":\"LogNote\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"authority\",\"type\":\"address\"}],\"name\":\"LogSetAuthority\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"LogSetOwner\",\"type\":\"event\"}]"
  },
  {
    "path": "pymaker/abi/SaiTop.abi",
    "content": "[{\"constant\":true,\"inputs\":[],\"name\":\"sin\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"skr\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"owner_\",\"type\":\"address\"}],\"name\":\"setOwner\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"era\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"flow\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"tub\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"cooldown_\",\"type\":\"uint256\"}],\"name\":\"setCooldown\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"vox\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"cage\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"cooldown\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"authority_\",\"type\":\"address\"}],\"name\":\"setAuthority\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"gem\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"sai\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"fix\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"authority\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"fit\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"caged\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"tap\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"name\":\"tub_\",\"type\":\"address\"},{\"name\":\"tap_\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":true,\"inputs\":[{\"indexed\":true,\"name\":\"sig\",\"type\":\"bytes4\"},{\"indexed\":true,\"name\":\"guy\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"foo\",\"type\":\"bytes32\"},{\"indexed\":true,\"name\":\"bar\",\"type\":\"bytes32\"},{\"indexed\":false,\"name\":\"wad\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"fax\",\"type\":\"bytes\"}],\"name\":\"LogNote\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"authority\",\"type\":\"address\"}],\"name\":\"LogSetAuthority\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"LogSetOwner\",\"type\":\"event\"}]"
  },
  {
    "path": "pymaker/abi/SaiTub.abi",
    "content": "[{\"constant\":false,\"inputs\":[{\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"join\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"sin\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"skr\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"gov\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"owner_\",\"type\":\"address\"}],\"name\":\"setOwner\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"era\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"cup\",\"type\":\"bytes32\"}],\"name\":\"ink\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"rho\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"air\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"rhi\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"flow\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"cap\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"cup\",\"type\":\"bytes32\"}],\"name\":\"bite\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"cup\",\"type\":\"bytes32\"},{\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"draw\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"bid\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"cupi\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"axe\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"tag\",\"outputs\":[{\"name\":\"wad\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"off\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"vox\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"gap\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"cup\",\"type\":\"bytes32\"}],\"name\":\"rap\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"cup\",\"type\":\"bytes32\"},{\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"wipe\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"authority_\",\"type\":\"address\"}],\"name\":\"setAuthority\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"gem\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"tap_\",\"type\":\"address\"}],\"name\":\"turn\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"per\",\"outputs\":[{\"name\":\"ray\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"exit\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"pip_\",\"type\":\"address\"}],\"name\":\"setPip\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"pie\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"fit_\",\"type\":\"uint256\"},{\"name\":\"jam\",\"type\":\"uint256\"}],\"name\":\"cage\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"rum\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"sai\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"param\",\"type\":\"bytes32\"},{\"name\":\"val\",\"type\":\"uint256\"}],\"name\":\"mold\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"tax\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"drip\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"cup\",\"type\":\"bytes32\"},{\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"free\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"mat\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"pep\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"out\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"cup\",\"type\":\"bytes32\"},{\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"lock\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"cup\",\"type\":\"bytes32\"}],\"name\":\"shut\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"cup\",\"type\":\"bytes32\"},{\"name\":\"guy\",\"type\":\"address\"}],\"name\":\"give\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"authority\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"fit\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"chi\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"vox_\",\"type\":\"address\"}],\"name\":\"setVox\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"pip\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"pep_\",\"type\":\"address\"}],\"name\":\"setPep\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"fee\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"cup\",\"type\":\"bytes32\"}],\"name\":\"lad\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"din\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"ask\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"cup\",\"type\":\"bytes32\"}],\"name\":\"safe\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"pit\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"cup\",\"type\":\"bytes32\"}],\"name\":\"tab\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"open\",\"outputs\":[{\"name\":\"cup\",\"type\":\"bytes32\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"tap\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"cups\",\"outputs\":[{\"name\":\"lad\",\"type\":\"address\"},{\"name\":\"ink\",\"type\":\"uint256\"},{\"name\":\"art\",\"type\":\"uint256\"},{\"name\":\"ire\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"name\":\"sai_\",\"type\":\"address\"},{\"name\":\"sin_\",\"type\":\"address\"},{\"name\":\"skr_\",\"type\":\"address\"},{\"name\":\"gem_\",\"type\":\"address\"},{\"name\":\"gov_\",\"type\":\"address\"},{\"name\":\"pip_\",\"type\":\"address\"},{\"name\":\"pep_\",\"type\":\"address\"},{\"name\":\"vox_\",\"type\":\"address\"},{\"name\":\"pit_\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"lad\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"cup\",\"type\":\"bytes32\"}],\"name\":\"LogNewCup\",\"type\":\"event\"},{\"anonymous\":true,\"inputs\":[{\"indexed\":true,\"name\":\"sig\",\"type\":\"bytes4\"},{\"indexed\":true,\"name\":\"guy\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"foo\",\"type\":\"bytes32\"},{\"indexed\":true,\"name\":\"bar\",\"type\":\"bytes32\"},{\"indexed\":false,\"name\":\"wad\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"fax\",\"type\":\"bytes\"}],\"name\":\"LogNote\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"authority\",\"type\":\"address\"}],\"name\":\"LogSetAuthority\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"LogSetOwner\",\"type\":\"event\"}]"
  },
  {
    "path": "pymaker/abi/SaiVox.abi",
    "content": "[{\"constant\":false,\"inputs\":[],\"name\":\"prod\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"owner_\",\"type\":\"address\"}],\"name\":\"setOwner\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"era\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"how\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"par\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"ray\",\"type\":\"uint256\"}],\"name\":\"tell\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"way\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"authority_\",\"type\":\"address\"}],\"name\":\"setAuthority\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"param\",\"type\":\"bytes32\"},{\"name\":\"val\",\"type\":\"uint256\"}],\"name\":\"mold\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"fix\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"ray\",\"type\":\"uint256\"}],\"name\":\"tune\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"authority\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"tau\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"name\":\"par_\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":true,\"inputs\":[{\"indexed\":true,\"name\":\"sig\",\"type\":\"bytes4\"},{\"indexed\":true,\"name\":\"guy\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"foo\",\"type\":\"bytes32\"},{\"indexed\":true,\"name\":\"bar\",\"type\":\"bytes32\"},{\"indexed\":false,\"name\":\"wad\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"fax\",\"type\":\"bytes\"}],\"name\":\"LogNote\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"authority\",\"type\":\"address\"}],\"name\":\"LogSetAuthority\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"LogSetOwner\",\"type\":\"event\"}]"
  },
  {
    "path": "pymaker/abi/SimpleMarket.abi",
    "content": "[{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"id\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"pair\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"maker\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"contract ERC20\",\"name\":\"pay_gem\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"contract ERC20\",\"name\":\"buy_gem\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint128\",\"name\":\"pay_amt\",\"type\":\"uint128\"},{\"indexed\":false,\"internalType\":\"uint128\",\"name\":\"buy_amt\",\"type\":\"uint128\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"timestamp\",\"type\":\"uint64\"}],\"name\":\"LogBump\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"LogItemUpdate\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"id\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"pair\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"maker\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"contract ERC20\",\"name\":\"pay_gem\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"contract ERC20\",\"name\":\"buy_gem\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint128\",\"name\":\"pay_amt\",\"type\":\"uint128\"},{\"indexed\":false,\"internalType\":\"uint128\",\"name\":\"buy_amt\",\"type\":\"uint128\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"timestamp\",\"type\":\"uint64\"}],\"name\":\"LogKill\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"id\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"pair\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"maker\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"contract ERC20\",\"name\":\"pay_gem\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"contract ERC20\",\"name\":\"buy_gem\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint128\",\"name\":\"pay_amt\",\"type\":\"uint128\"},{\"indexed\":false,\"internalType\":\"uint128\",\"name\":\"buy_amt\",\"type\":\"uint128\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"timestamp\",\"type\":\"uint64\"}],\"name\":\"LogMake\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"id\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"pair\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"maker\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"contract ERC20\",\"name\":\"pay_gem\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"contract ERC20\",\"name\":\"buy_gem\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"taker\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint128\",\"name\":\"take_amt\",\"type\":\"uint128\"},{\"indexed\":false,\"internalType\":\"uint128\",\"name\":\"give_amt\",\"type\":\"uint128\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"timestamp\",\"type\":\"uint64\"}],\"name\":\"LogTake\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"pay_amt\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"pay_gem\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"buy_amt\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"buy_gem\",\"type\":\"address\"}],\"name\":\"LogTrade\",\"type\":\"event\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"id_\",\"type\":\"bytes32\"}],\"name\":\"bump\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"quantity\",\"type\":\"uint256\"}],\"name\":\"buy\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"cancel\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"getOffer\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"contract ERC20\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"contract ERC20\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"getOwner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"isActive\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"active\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"id\",\"type\":\"bytes32\"}],\"name\":\"kill\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"last_offer_id\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"contract ERC20\",\"name\":\"pay_gem\",\"type\":\"address\"},{\"internalType\":\"contract ERC20\",\"name\":\"buy_gem\",\"type\":\"address\"},{\"internalType\":\"uint128\",\"name\":\"pay_amt\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"buy_amt\",\"type\":\"uint128\"}],\"name\":\"make\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"id\",\"type\":\"bytes32\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"pay_amt\",\"type\":\"uint256\"},{\"internalType\":\"contract ERC20\",\"name\":\"pay_gem\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"buy_amt\",\"type\":\"uint256\"},{\"internalType\":\"contract ERC20\",\"name\":\"buy_gem\",\"type\":\"address\"}],\"name\":\"offer\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"offers\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"pay_amt\",\"type\":\"uint256\"},{\"internalType\":\"contract ERC20\",\"name\":\"pay_gem\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"buy_amt\",\"type\":\"uint256\"},{\"internalType\":\"contract ERC20\",\"name\":\"buy_gem\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"timestamp\",\"type\":\"uint64\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"id\",\"type\":\"bytes32\"},{\"internalType\":\"uint128\",\"name\":\"maxTakeAmount\",\"type\":\"uint128\"}],\"name\":\"take\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]\n"
  },
  {
    "path": "pymaker/abi/Spotter.abi",
    "content": "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"vat_\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":true,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes4\",\"name\":\"sig\",\"type\":\"bytes4\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"arg1\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"arg2\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"LogNote\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"ilk\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"val\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"spot\",\"type\":\"uint256\"}],\"name\":\"Poke\",\"type\":\"event\"},{\"constant\":false,\"inputs\":[],\"name\":\"cage\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"guy\",\"type\":\"address\"}],\"name\":\"deny\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"ilk\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"what\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"data\",\"type\":\"uint256\"}],\"name\":\"file\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"what\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"data\",\"type\":\"uint256\"}],\"name\":\"file\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"ilk\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"what\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"pip_\",\"type\":\"address\"}],\"name\":\"file\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"ilks\",\"outputs\":[{\"internalType\":\"contract PipLike\",\"name\":\"pip\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"mat\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"live\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"par\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"ilk\",\"type\":\"bytes32\"}],\"name\":\"poke\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"guy\",\"type\":\"address\"}],\"name\":\"rely\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"vat\",\"outputs\":[{\"internalType\":\"contract VatLike\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"wards\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"}]\n"
  },
  {
    "path": "pymaker/abi/TokenFaucet.abi",
    "content": "[{\"inputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":true,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes4\",\"name\":\"sig\",\"type\":\"bytes4\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"arg1\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"arg2\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"LogNote\",\"type\":\"event\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"amt\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"guy\",\"type\":\"address\"}],\"name\":\"deny\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"done\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"gem\",\"type\":\"address\"},{\"internalType\":\"address[]\",\"name\":\"addrs\",\"type\":\"address[]\"}],\"name\":\"gulp\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"gem\",\"type\":\"address\"}],\"name\":\"gulp\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"guy\",\"type\":\"address\"}],\"name\":\"rely\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"gem\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amt_\",\"type\":\"uint256\"}],\"name\":\"setAmt\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"contract ERC20Like\",\"name\":\"gem\",\"type\":\"address\"}],\"name\":\"shut\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"wards\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"}]\n"
  },
  {
    "path": "pymaker/abi/TokenTransferProxy.abi",
    "content": "[{\"constant\":false,\"inputs\":[{\"name\":\"token\",\"type\":\"address\"},{\"name\":\"from\",\"type\":\"address\"},{\"name\":\"to\",\"type\":\"address\"},{\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"target\",\"type\":\"address\"}],\"name\":\"addAuthorizedAddress\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"authorities\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"target\",\"type\":\"address\"}],\"name\":\"removeAuthorizedAddress\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"\",\"type\":\"address\"}],\"name\":\"authorized\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"getAuthorizedAddresses\",\"outputs\":[{\"name\":\"\",\"type\":\"address[]\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"target\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"caller\",\"type\":\"address\"}],\"name\":\"LogAuthorizedAddressAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"target\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"caller\",\"type\":\"address\"}],\"name\":\"LogAuthorizedAddressRemoved\",\"type\":\"event\"}]"
  },
  {
    "path": "pymaker/abi/TxManager.abi",
    "content": "[{\"constant\":false,\"inputs\":[{\"name\":\"owner_\",\"type\":\"address\"}],\"name\":\"setOwner\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"tokens\",\"type\":\"address[]\"},{\"name\":\"script\",\"type\":\"bytes\"}],\"name\":\"execute\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"authority_\",\"type\":\"address\"}],\"name\":\"setAuthority\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"authority\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"anonymous\":true,\"inputs\":[{\"indexed\":true,\"name\":\"sig\",\"type\":\"bytes4\"},{\"indexed\":true,\"name\":\"guy\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"foo\",\"type\":\"bytes32\"},{\"indexed\":true,\"name\":\"bar\",\"type\":\"bytes32\"},{\"indexed\":false,\"name\":\"wad\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"fax\",\"type\":\"bytes\"}],\"name\":\"LogNote\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"authority\",\"type\":\"address\"}],\"name\":\"LogSetAuthority\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"LogSetOwner\",\"type\":\"event\"}]"
  },
  {
    "path": "pymaker/abi/Vat.abi",
    "content": "[{\"inputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":true,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes4\",\"name\":\"sig\",\"type\":\"bytes4\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"arg1\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"arg2\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"arg3\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"LogNote\",\"type\":\"event\"},{\"constant\":true,\"inputs\":[],\"name\":\"Line\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"cage\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"can\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"dai\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"debt\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"}],\"name\":\"deny\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"ilk\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"what\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"data\",\"type\":\"uint256\"}],\"name\":\"file\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"what\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"data\",\"type\":\"uint256\"}],\"name\":\"file\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"ilk\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"src\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"dst\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"flux\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"i\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"u\",\"type\":\"address\"},{\"internalType\":\"int256\",\"name\":\"rate\",\"type\":\"int256\"}],\"name\":\"fold\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"ilk\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"src\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"dst\",\"type\":\"address\"},{\"internalType\":\"int256\",\"name\":\"dink\",\"type\":\"int256\"},{\"internalType\":\"int256\",\"name\":\"dart\",\"type\":\"int256\"}],\"name\":\"fork\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"i\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"u\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"v\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"w\",\"type\":\"address\"},{\"internalType\":\"int256\",\"name\":\"dink\",\"type\":\"int256\"},{\"internalType\":\"int256\",\"name\":\"dart\",\"type\":\"int256\"}],\"name\":\"frob\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"gem\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"i\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"u\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"v\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"w\",\"type\":\"address\"},{\"internalType\":\"int256\",\"name\":\"dink\",\"type\":\"int256\"},{\"internalType\":\"int256\",\"name\":\"dart\",\"type\":\"int256\"}],\"name\":\"grab\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"rad\",\"type\":\"uint256\"}],\"name\":\"heal\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"}],\"name\":\"hope\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"ilks\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"Art\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"rate\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"spot\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"line\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"dust\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"ilk\",\"type\":\"bytes32\"}],\"name\":\"init\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"live\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"src\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"dst\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"rad\",\"type\":\"uint256\"}],\"name\":\"move\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"}],\"name\":\"nope\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"}],\"name\":\"rely\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"sin\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"ilk\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"},{\"internalType\":\"int256\",\"name\":\"wad\",\"type\":\"int256\"}],\"name\":\"slip\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"u\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"v\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"rad\",\"type\":\"uint256\"}],\"name\":\"suck\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"urns\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"ink\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"art\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"vice\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"wards\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"}]\n"
  },
  {
    "path": "pymaker/abi/Vow.abi",
    "content": "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"vat_\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"flapper_\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"flopper_\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":true,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes4\",\"name\":\"sig\",\"type\":\"bytes4\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"arg1\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"arg2\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"LogNote\",\"type\":\"event\"},{\"constant\":true,\"inputs\":[],\"name\":\"Ash\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"Sin\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"bump\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"cage\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"}],\"name\":\"deny\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"dump\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"tab\",\"type\":\"uint256\"}],\"name\":\"fess\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"what\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"data\",\"type\":\"uint256\"}],\"name\":\"file\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"what\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"data\",\"type\":\"address\"}],\"name\":\"file\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"flap\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"flapper\",\"outputs\":[{\"internalType\":\"contract FlapLike\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"era\",\"type\":\"uint256\"}],\"name\":\"flog\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"flop\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"flopper\",\"outputs\":[{\"internalType\":\"contract FlopLike\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"rad\",\"type\":\"uint256\"}],\"name\":\"heal\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"hump\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"rad\",\"type\":\"uint256\"}],\"name\":\"kiss\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"live\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"usr\",\"type\":\"address\"}],\"name\":\"rely\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"sin\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"sump\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"vat\",\"outputs\":[{\"internalType\":\"contract VatLike\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"wait\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"wards\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"}]\n"
  },
  {
    "path": "pymaker/abi/ZRXToken.abi",
    "content": "[{\"constant\":true,\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_spender\",\"type\":\"address\"},{\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"totalSupply\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_from\",\"type\":\"address\"},{\"name\":\"_to\",\"type\":\"address\"},{\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"name\":\"\",\"type\":\"uint8\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"_owner\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"symbol\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_to\",\"type\":\"address\"},{\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"transfer\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"_owner\",\"type\":\"address\"},{\"name\":\"_spender\",\"type\":\"address\"}],\"name\":\"allowance\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"inputs\":[],\"payable\":false,\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"_from\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"_to\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"Transfer\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"_owner\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"_spender\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"Approval\",\"type\":\"event\"}]"
  },
  {
    "path": "pymaker/abi/diff-abi.sh",
    "content": "#!/bin/bash\n\n# This script is useful when updating ABIs as newer contracts are released.\nvimdiff <(git show HEAD:pymaker/abi/$@ | jq '.') <(jq '.' < $@)\n"
  },
  {
    "path": "pymaker/approval.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2017-2018 reverendus\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nimport logging\n\nfrom pymaker import Address, Contract\nfrom pymaker import Transact\nfrom pymaker.numeric import Wad\nfrom pymaker.token import ERC20Token\nfrom pymaker.transactional import TxManager\n\n\ndef directly(**kwargs):\n    \"\"\"Approval function: Approves the caller to access tokens directly.\n\n    This function is meant to be passed as a parameter to the `approve(...)` method\n    of `Tub`, `SimpleMarket`, 'EtherDelta', 'TxManager', `ZrxExchange` and possibly\n    others in the future.\n    \"\"\"\n\n    def approval_function(token: ERC20Token, spender_address: Address, spender_name: str):\n        address_to_check = kwargs['from_address'] if 'from_address' in kwargs else Address(token.web3.eth.defaultAccount)\n\n        if token.allowance_of(address_to_check, spender_address) < Wad(2 ** 128 - 1):\n            logger = logging.getLogger()\n            logger.info(f\"Approving {spender_name} ({spender_address}) to access our {token.address} directly\")\n            if not token.approve(spender_address).transact(**kwargs):\n                raise RuntimeError(\"Approval failed!\")\n\n    return approval_function\n\n\ndef via_tx_manager(tx_manager: TxManager, **kwargs):\n    \"\"\"Approval function: Approves the caller to access tokens via the `TxManager`.\n\n    This function is meant to be passed as a parameter to the `approve(...)` method\n    of `Tub`, `SimpleMarket`, 'EtherDelta', 'TxManager', `ZrxExchange` and possibly\n    others in the future.\n    \"\"\"\n    assert(isinstance(tx_manager, TxManager))\n\n    def approval_function(token: ERC20Token, spender_address: Address, spender_name: str):\n        if token.allowance_of(tx_manager.address, spender_address) < Wad(2 ** 128 - 1):\n            logger = logging.getLogger()\n            logger.info(f\"Approving {spender_name} ({spender_address}) to access our {token.address}\"\n                        f\" via TxManager {tx_manager.address}\")\n            if not tx_manager.execute([], [(token.approve(spender_address).invocation())]).transact(**kwargs):\n                raise RuntimeError(\"Approval failed!\")\n\n    return approval_function\n\n\ndef hope_directly(**kwargs):\n    \"\"\"Approval function: Approves the caller to access tokens directly.\n\n    This function is meant to be passed as a parameter to the `approve(...)` method\n    of `Flipper` and `Flopper` and possibly others in the future.\n    \"\"\"\n\n    move_abi = [{'constant': False, 'inputs': [{'name': 'guy', 'type': 'address'}], 'name': 'hope', 'outputs': [],\n                 'payable': False, 'stateMutability': 'nonpayable', 'type': 'function'},\n                {'constant': True, 'inputs': [{'name': '', 'type': 'address'}, {'name': '', 'type': 'address'}],\n                 'name': 'can', 'outputs': [{'name': '', 'type': 'bool'}], 'payable': False, 'stateMutability': 'view',\n                 'type': 'function'}]\n\n    def approval_function(token: ERC20Token, spender_address: Address, spender_name: str):\n        address_to_check = kwargs['from_address'] if 'from_address' in kwargs else Address(\n            token.web3.eth.defaultAccount)\n\n        move_contract = Contract._get_contract(web3=token.web3, abi=move_abi, address=token.address)\n        if move_contract.functions.can(address_to_check.address, spender_address.address).call() is False:\n            logger = logging.getLogger()\n            logger.info(f\"Approving {spender_name} ({spender_address}) to move our {token.address} directly\")\n\n            hope = Transact(move_contract, move_contract.web3, move_contract.abi, Address(move_contract.address),\n                            move_contract, 'hope', [spender_address.address])\n\n            if not hope.transact(**kwargs):\n                raise RuntimeError(\"Approval failed!\")\n\n    return approval_function\n"
  },
  {
    "path": "pymaker/auctions.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2018-2019 reverendus, bargst, EdNoepel\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nfrom datetime import datetime\nimport logging\nfrom pprint import pformat\nfrom typing import List\nfrom web3 import Web3\n\nfrom web3._utils.events import get_event_data\n\nfrom eth_abi.codec import ABICodec\nfrom eth_abi.registry import registry as default_registry\n\nfrom pymaker import Contract, Address, Transact\nfrom pymaker.dss import Dog, Vat\nfrom pymaker.logging import LogNote\nfrom pymaker.numeric import Wad, Rad, Ray\nfrom pymaker.token import ERC20Token\n\n\ndef toBytes(string: str):\n    assert(isinstance(string, str))\n    return string.encode('utf-8').ljust(32, bytes(1))\n\n\nlogger = logging.getLogger()\n\n\nclass AuctionContract(Contract):\n    \"\"\"Abstract baseclass shared across all auction contracts.\"\"\"\n    def __init__(self, web3: Web3, address: Address, abi: list):\n        if self.__class__ == AuctionContract:\n            raise NotImplemented('Abstract class; please call Clipper, Flapper, Flipper, or Flopper ctor')\n        assert isinstance(web3, Web3)\n        assert isinstance(address, Address)\n        assert isinstance(abi, list)\n\n        self.web3 = web3\n        self.address = address\n        self.abi = abi\n        self._contract = self._get_contract(web3, abi, address)\n\n        self.log_note_abi = None\n        self.kick_abi = None\n        for member in abi:\n            if not self.log_note_abi and member.get('name') == 'LogNote':\n                self.log_note_abi = member\n            elif not self.kick_abi and member.get('name') == 'Kick':\n                self.kick_abi = member\n\n    def approve(self, source: Address, approval_function):\n        \"\"\"Approve the auction to access our collateral, Dai, or MKR so we can participate in auctions.\n\n        For available approval functions (i.e. approval modes) see `directly` and `hope_directly`\n        in `pymaker.approval`.\n\n        Args:\n            source: Address of the contract or token relevant to the auction (for Flipper and Flopper pass Vat address,\n            for Flapper pass MKR token address)\n            approval_function: Approval function (i.e. approval mode)\n        \"\"\"\n        assert isinstance(source, Address)\n        assert(callable(approval_function))\n\n        approval_function(token=ERC20Token(web3=self.web3, address=source),\n                          spender_address=self.address, spender_name=self.__class__.__name__)\n\n    def wards(self, address: Address) -> bool:\n        assert isinstance(address, Address)\n\n        return bool(self._contract.functions.wards(address.address).call())\n\n    def vat(self) -> Address:\n        \"\"\"Returns the `vat` address.\n         Returns:\n            The address of the `vat` contract.\n        \"\"\"\n        return Address(self._contract.functions.vat().call())\n\n    def get_past_lognotes(self, abi: list, from_block: int, to_block: int = None, chunk_size=20000) -> List[LogNote]:\n        current_block = self._contract.web3.eth.blockNumber\n        assert isinstance(from_block, int)\n        assert from_block < current_block\n        if to_block is None:\n            to_block = current_block\n        else:\n            assert isinstance(to_block, int)\n            assert to_block >= from_block\n            assert to_block <= current_block\n        assert chunk_size > 0\n        assert isinstance(abi, list)\n\n        logger.debug(f\"Consumer requested auction data from block {from_block} to {to_block}\")\n        start = from_block\n        end = None\n        chunks_queried = 0\n        events = []\n        while end is None or start <= to_block:\n            chunks_queried += 1\n            end = min(to_block, start + chunk_size)\n\n            filter_params = {\n                'address': self.address.address,\n                'fromBlock': start,\n                'toBlock': end\n            }\n            logger.debug(f\"Querying logs from block {start} to {end} ({end-start} blocks); \"\n                         f\"accumulated {len(events)} events in {chunks_queried-1} requests\")\n\n            logs = self.web3.eth.getLogs(filter_params)\n            events.extend(list(map(lambda l: self.parse_event(l), logs)))\n            start += chunk_size\n\n        return list(filter(lambda l: l is not None, events))\n\n    def parse_event(self, event):\n        raise NotImplemented()\n\n\nclass DealableAuctionContract(AuctionContract):\n    \"\"\"Abstract baseclass shared across original auction contracts.\"\"\"\n\n    class DealLog:\n        def __init__(self, lognote: LogNote):\n            # This is whoever called `deal`, which could differ from the `guy` who won the auction\n            self.usr = Address(lognote.usr)\n            self.id = Web3.toInt(lognote.arg1)\n            self.block = lognote.block\n            self.tx_hash = lognote.tx_hash\n\n        def __repr__(self):\n            return f\"AuctionContract.DealLog({pformat(vars(self))})\"\n\n    def __init__(self, web3: Web3, address: Address, abi: list, bids: callable):\n        if self.__class__ == DealableAuctionContract:\n            raise NotImplemented('Abstract class; please call Flipper, Flapper, or Flopper ctor')\n        super(DealableAuctionContract, self).__init__(web3, address, abi)\n\n        self._bids = bids\n\n    def active_auctions(self) -> list:\n        active_auctions = []\n        auction_count = self.kicks()+1\n        for index in range(1, auction_count):\n            bid = self._bids(index)\n            if bid.guy != Address(\"0x0000000000000000000000000000000000000000\"):\n                now = datetime.now().timestamp()\n                if (bid.tic == 0 or now < bid.tic) and now < bid.end:\n                    active_auctions.append(bid)\n            index += 1\n        return active_auctions\n\n    def beg(self) -> Wad:\n        \"\"\"Returns the percentage minimum bid increase.\n\n        Returns:\n            The percentage minimum bid increase.\n        \"\"\"\n        return Wad(self._contract.functions.beg().call())\n\n    def ttl(self) -> int:\n        \"\"\"Returns the bid lifetime.\n\n        Returns:\n            The bid lifetime (in seconds).\n        \"\"\"\n        return int(self._contract.functions.ttl().call())\n\n    def tau(self) -> int:\n        \"\"\"Returns the total auction length.\n\n        Returns:\n            The total auction length (in seconds).\n        \"\"\"\n        return int(self._contract.functions.tau().call())\n\n    def kicks(self) -> int:\n        \"\"\"Returns the number of auctions started so far.\n\n        Returns:\n            The number of auctions started so far.\n        \"\"\"\n        return int(self._contract.functions.kicks().call())\n\n    def deal(self, id: int) -> Transact:\n        assert(isinstance(id, int))\n\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'deal', [id])\n\n    def tick(self, id: int) -> Transact:\n        \"\"\"Resurrect an auction which expired without any bids.\"\"\"\n        assert(isinstance(id, int))\n\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'tick', [id])\n\n\nclass Flipper(DealableAuctionContract):\n    \"\"\"A client for the `Flipper` contract, used to interact with collateral auctions.\n\n    You can find the source code of the `Flipper` contract here:\n    <https://github.com/makerdao/dss/blob/master/src/flip.sol>.\n\n    Attributes:\n        web3: An instance of `Web` from `web3.py`.\n        address: Ethereum address of the `Flipper` contract.\n\n    Event signatures:\n        0x65fae35e: (deployment-related)\n        0x9c52a7f1: (deployment-related)\n        0x29ae8114: file\n        0xc84ce3a1172f0dec3173f04caaa6005151a4bfe40d4c9f3ea28dba5f719b2a7a: kick\n        0x4b43ed12: tend\n        0x5ff3a382: dent\n        0xc959c42b: deal\n    \"\"\"\n\n    abi = Contract._load_abi(__name__, 'abi/Flipper.abi')\n    bin = Contract._load_bin(__name__, 'abi/Flipper.bin')\n\n    class Bid:\n        def __init__(self, id: int, bid: Rad, lot: Wad, guy: Address, tic: int, end: int,\n                     usr: Address, gal: Address, tab: Rad):\n            assert(isinstance(id, int))\n            assert(isinstance(bid, Rad))\n            assert(isinstance(lot, Wad))\n            assert(isinstance(guy, Address))\n            assert(isinstance(tic, int))\n            assert(isinstance(end, int))\n            assert(isinstance(usr, Address))\n            assert(isinstance(gal, Address))\n            assert(isinstance(tab, Rad))\n\n            self.id = id\n            self.bid = bid\n            self.lot = lot\n            self.guy = guy\n            self.tic = tic\n            self.end = end\n            self.usr = usr\n            self.gal = gal\n            self.tab = tab\n\n        def __repr__(self):\n            return f\"Flipper.Bid({pformat(vars(self))})\"\n\n    class KickLog:\n        def __init__(self, log):\n            args = log['args']\n            self.id = args['id']\n            self.lot = Wad(args['lot'])\n            self.bid = Rad(args['bid'])\n            self.tab = Rad(args['tab'])\n            self.usr = Address(args['usr'])\n            self.gal = Address(args['gal'])\n            self.block = log['blockNumber']\n            self.tx_hash = log['transactionHash'].hex()\n\n        def __repr__(self):\n            return f\"Flipper.KickLog({pformat(vars(self))})\"\n\n    class TendLog:\n        def __init__(self, lognote: LogNote):\n            self.guy = Address(lognote.usr)\n            self.id = Web3.toInt(lognote.arg1)\n            self.lot = Wad(Web3.toInt(lognote.arg2))\n            self.bid = Rad(Web3.toInt(lognote.get_bytes_at_index(2)))\n            self.block = lognote.block\n            self.tx_hash = lognote.tx_hash\n\n        def __repr__(self):\n            return f\"Flipper.TendLog({pformat(vars(self))})\"\n\n    class DentLog:\n        def __init__(self, lognote: LogNote):\n            self.guy = Address(lognote.usr)\n            self.id = Web3.toInt(lognote.arg1)\n            self.lot = Wad(Web3.toInt(lognote.arg2))\n            self.bid = Rad(Web3.toInt(lognote.get_bytes_at_index(2)))\n            self.block = lognote.block\n            self.tx_hash = lognote.tx_hash\n\n        def __repr__(self):\n            return f\"Flipper.DentLog({pformat(vars(self))})\"\n\n    def __init__(self, web3: Web3, address: Address):\n        super(Flipper, self).__init__(web3, address, Flipper.abi, self.bids)\n\n    def bids(self, id: int) -> Bid:\n        \"\"\"Returns the auction details.\n\n        Args:\n            id: Auction identifier.\n\n        Returns:\n            The auction details.\n        \"\"\"\n        assert(isinstance(id, int))\n\n        array = self._contract.functions.bids(id).call()\n\n        return Flipper.Bid(id=id,\n                           bid=Rad(array[0]),\n                           lot=Wad(array[1]),\n                           guy=Address(array[2]),\n                           tic=int(array[3]),\n                           end=int(array[4]),\n                           usr=Address(array[5]),\n                           gal=Address(array[6]),\n                           tab=Rad(array[7]))\n\n    def tend(self, id: int, lot: Wad, bid: Rad) -> Transact:\n        assert(isinstance(id, int))\n        assert(isinstance(lot, Wad))\n        assert(isinstance(bid, Rad))\n\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'tend', [id, lot.value, bid.value])\n\n    def dent(self, id: int, lot: Wad, bid: Rad) -> Transact:\n        assert(isinstance(id, int))\n        assert(isinstance(lot, Wad))\n        assert(isinstance(bid, Rad))\n\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'dent', [id, lot.value, bid.value])\n\n    def past_logs(self, from_block: int, to_block: int = None, chunk_size=20000):\n        logs = super().get_past_lognotes(Flipper.abi, from_block, to_block, chunk_size)\n\n        history = []\n        for log in logs:\n            if log is None:\n                continue\n            elif isinstance(log, Flipper.KickLog):\n                history.append(log)\n            elif log.sig == '0x4b43ed12':\n                history.append(Flipper.TendLog(log))\n            elif log.sig == '0x5ff3a382':\n                history.append(Flipper.DentLog(log))\n            elif log.sig == '0xc959c42b':\n                history.append(DealableAuctionContract.DealLog(log))\n        return history\n\n    def parse_event(self, event):\n        signature = Web3.toHex(event['topics'][0])\n        codec = ABICodec(default_registry)\n        if signature == \"0xc84ce3a1172f0dec3173f04caaa6005151a4bfe40d4c9f3ea28dba5f719b2a7a\":\n            event_data = get_event_data(codec, self.kick_abi, event)\n            return Flipper.KickLog(event_data)\n        else:\n            event_data = get_event_data(codec, self.log_note_abi, event)\n            return LogNote(event_data)\n\n    def __repr__(self):\n        return f\"Flipper('{self.address}')\"\n\n\nclass Flapper(DealableAuctionContract):\n    \"\"\"A client for the `Flapper` contract, used to interact with surplus auctions.\n\n    You can find the source code of the `Flapper` contract here:\n    <https://github.com/makerdao/dss/blob/master/src/flap.sol>.\n\n    Attributes:\n        web3: An instance of `Web` from `web3.py`.\n        address: Ethereum address of the `Flapper` contract.\n\n    Event signatures:\n        0x65fae35e: (deployment-related)\n        0x9c52a7f1: (deployment-related)\n        0xe6dde59cbc017becba89714a037778d234a84ce7f0a137487142a007e580d609: kick\n        0x29ae8114: file\n        0x4b43ed12: tend\n        0xc959c42b: deal\n    \"\"\"\n\n    abi = Contract._load_abi(__name__, 'abi/Flapper.abi')\n    bin = Contract._load_bin(__name__, 'abi/Flapper.bin')\n\n    class Bid:\n        def __init__(self, id: int, bid: Wad, lot: Rad, guy: Address, tic: int, end: int):\n            assert(isinstance(id, int))\n            assert(isinstance(bid, Wad))        # MKR\n            assert(isinstance(lot, Rad))        # DAI\n            assert(isinstance(guy, Address))\n            assert(isinstance(tic, int))\n            assert(isinstance(end, int))\n\n            self.id = id\n            self.bid = bid\n            self.lot = lot\n            self.guy = guy\n            self.tic = tic\n            self.end = end\n\n        def __repr__(self):\n            return f\"Flapper.Bid({pformat(vars(self))})\"\n\n    class KickLog:\n        def __init__(self, log):\n            args = log['args']\n            self.id = args['id']\n            self.lot = Rad(args['lot'])\n            self.bid = Wad(args['bid'])\n            self.block = log['blockNumber']\n            self.tx_hash = log['transactionHash'].hex()\n\n        def __repr__(self):\n            return f\"Flapper.KickLog({pformat(vars(self))})\"\n\n    class TendLog:\n        def __init__(self, lognote: LogNote):\n            self.guy = Address(lognote.usr)\n            self.id = Web3.toInt(lognote.arg1)\n            self.lot = Rad(Web3.toInt(lognote.arg2))\n            self.bid = Wad(Web3.toInt(lognote.get_bytes_at_index(2)))\n            self.block = lognote.block\n            self.tx_hash = lognote.tx_hash\n\n        def __repr__(self):\n            return f\"Flapper.TendLog({pformat(vars(self))})\"\n\n    def __init__(self, web3: Web3, address: Address):\n        super(Flapper, self).__init__(web3, address, Flapper.abi, self.bids)\n\n    def live(self) -> bool:\n        return self._contract.functions.live().call() > 0\n\n    def bids(self, id: int) -> Bid:\n        \"\"\"Returns the auction details.\n\n        Args:\n            id: Auction identifier.\n\n        Returns:\n            The auction details.\n        \"\"\"\n        assert(isinstance(id, int))\n\n        array = self._contract.functions.bids(id).call()\n\n        return Flapper.Bid(id=id,\n                           bid=Wad(array[0]),\n                           lot=Rad(array[1]),\n                           guy=Address(array[2]),\n                           tic=int(array[3]),\n                           end=int(array[4]))\n\n    def tend(self, id: int, lot: Rad, bid: Wad) -> Transact:\n        assert(isinstance(id, int))\n        assert(isinstance(lot, Rad))\n        assert(isinstance(bid, Wad))\n\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'tend', [id, lot.value, bid.value])\n\n    def yank(self, id: int) -> Transact:\n        \"\"\"While `cage`d, refund current bid to the bidder\"\"\"\n        assert (isinstance(id, int))\n\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'yank', [id])\n\n    def past_logs(self, from_block: int, to_block: int = None, chunk_size=20000):\n        logs = super().get_past_lognotes(Flapper.abi, from_block, to_block, chunk_size)\n\n        history = []\n        for log in logs:\n            if log is None:\n                continue\n            elif isinstance(log, Flapper.KickLog):\n                history.append(log)\n            elif log.sig == '0x4b43ed12':\n                history.append(Flapper.TendLog(log))\n            elif log.sig == '0xc959c42b':\n                history.append(DealableAuctionContract.DealLog(log))\n        return history\n\n    def parse_event(self, event):\n        signature = Web3.toHex(event['topics'][0])\n        codec = ABICodec(default_registry)\n        if signature == \"0xe6dde59cbc017becba89714a037778d234a84ce7f0a137487142a007e580d609\":\n            event_data = get_event_data(codec, self.kick_abi, event)\n            return Flapper.KickLog(event_data)\n        else:\n            event_data = get_event_data(codec, self.log_note_abi, event)\n            return LogNote(event_data)\n\n    def __repr__(self):\n        return f\"Flapper('{self.address}')\"\n\n\nclass Flopper(DealableAuctionContract):\n    \"\"\"A client for the `Flopper` contract, used to interact with debt auctions.\n\n    You can find the source code of the `Flopper` contract here:\n    <https://github.com/makerdao/dss/blob/master/src/flop.sol>.\n\n    Attributes:\n        web3: An instance of `Web` from `web3.py`.\n        address: Ethereum address of the `Flopper` contract.\n\n    Event signatures:\n        0x65fae35e: (deployment-related)\n        0x9c52a7f1: (deployment-related)\n        0x29ae8114: file\n        0x7e8881001566f9f89aedb9c5dc3d856a2b81e5235a8196413ed484be91cc0df6: kick\n        0x5ff3a382: dent\n        0xc959c42b: deal\n    \"\"\"\n\n    abi = Contract._load_abi(__name__, 'abi/Flopper.abi')\n    bin = Contract._load_bin(__name__, 'abi/Flopper.bin')\n\n    class Bid:\n        def __init__(self, id: int, bid: Rad, lot: Wad, guy: Address, tic: int, end: int):\n            assert(isinstance(id, int))\n            assert(isinstance(bid, Rad))\n            assert(isinstance(lot, Wad))\n            assert(isinstance(guy, Address))\n            assert(isinstance(tic, int))\n            assert(isinstance(end, int))\n\n            self.id = id\n            self.bid = bid\n            self.lot = lot\n            self.guy = guy\n            self.tic = tic\n            self.end = end\n\n        def __repr__(self):\n            return f\"Flopper.Bid({pformat(vars(self))})\"\n\n    class KickLog:\n        def __init__(self, log):\n            args = log['args']\n            self.id = args['id']\n            self.lot = Wad(args['lot'])\n            self.bid = Rad(args['bid'])\n            self.gal = Address(args['gal'])\n            self.block = log['blockNumber']\n            self.tx_hash = log['transactionHash'].hex()\n\n        def __repr__(self):\n            return f\"Flopper.KickLog({pformat(vars(self))})\"\n\n    class DentLog:\n        def __init__(self, lognote: LogNote):\n            self.guy = Address(lognote.usr)\n            self.id = Web3.toInt(lognote.arg1)\n            self.lot = Wad(Web3.toInt(lognote.arg2))\n            self.bid = Rad(Web3.toInt(lognote.get_bytes_at_index(2)))\n            self.block = lognote.block\n            self.tx_hash = lognote.tx_hash\n\n        def __repr__(self):\n            return f\"Flopper.DentLog({pformat(vars(self))})\"\n\n    def __init__(self, web3: Web3, address: Address):\n        assert isinstance(web3, Web3)\n        assert isinstance(address, Address)\n\n        super(Flopper, self).__init__(web3, address, Flopper.abi, self.bids)\n\n    def live(self) -> bool:\n        return self._contract.functions.live().call() > 0\n\n    def pad(self) -> Wad:\n        \"\"\"Returns the lot increase applied after an auction has been `tick`ed.\"\"\"\n\n        return Wad(self._contract.functions.pad().call())\n\n    def bids(self, id: int) -> Bid:\n        \"\"\"Returns the auction details.\n\n        Args:\n            id: Auction identifier.\n\n        Returns:\n            The auction details.\n        \"\"\"\n        assert(isinstance(id, int))\n\n        array = self._contract.functions.bids(id).call()\n\n        return Flopper.Bid(id=id,\n                           bid=Rad(array[0]),\n                           lot=Wad(array[1]),\n                           guy=Address(array[2]),\n                           tic=int(array[3]),\n                           end=int(array[4]))\n\n    def dent(self, id: int, lot: Wad, bid: Rad) -> Transact:\n        assert(isinstance(id, int))\n        assert(isinstance(lot, Wad))\n        assert(isinstance(bid, Rad))\n\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'dent', [id, lot.value, bid.value])\n\n    def yank(self, id: int) -> Transact:\n        \"\"\"While `cage`d, refund current bid to the bidder\"\"\"\n        assert (isinstance(id, int))\n\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'yank', [id])\n\n    def past_logs(self, from_block: int, to_block: int = None, chunk_size=20000):\n        logs = super().get_past_lognotes(Flopper.abi, from_block, to_block, chunk_size)\n\n        history = []\n        for log in logs:\n            if log is None:\n                continue\n            elif isinstance(log, Flopper.KickLog):\n                history.append(log)\n            elif log.sig == '0x5ff3a382':\n                history.append(Flopper.DentLog(log))\n            elif log.sig == '0xc959c42b':\n                history.append(DealableAuctionContract.DealLog(log))\n        return history\n\n    def parse_event(self, event):\n        signature = Web3.toHex(event['topics'][0])\n        codec = ABICodec(default_registry)\n        if signature == \"0x7e8881001566f9f89aedb9c5dc3d856a2b81e5235a8196413ed484be91cc0df6\":\n            event_data = get_event_data(codec, self.kick_abi, event)\n            return Flopper.KickLog(event_data)\n        else:\n            event_data = get_event_data(codec, self.log_note_abi, event)\n            return LogNote(event_data)\n\n    def __repr__(self):\n        return f\"Flopper('{self.address}')\"\n\n\nclass Clipper(AuctionContract):\n    \"\"\"A client for the `Clipper` contract, used to interact with collateral auctions.\n\n    You can find the source code of the `Clipper` contract here:\n    <https://github.com/makerdao/dss/blob/master/src/clip.sol>.\n\n    Attributes:\n        web3: An instance of `Web` from `web3.py`.\n        address: Ethereum address of the `Clipper` contract.\n    \"\"\"\n\n    abi = Contract._load_abi(__name__, 'abi/Clipper.abi')\n    bin = Contract._load_bin(__name__, 'abi/Clipper.bin')\n\n    class KickLog:\n        def __init__(self, log):\n            args = log['args']\n            self.id = args['id']\n            self.top = Ray(args['top'])         # starting price\n            self.tab = Rad(args['tab'])         # debt\n            self.lot = Wad(args['lot'])         # collateral\n            self.usr = Address(args['usr'])     # liquidated vault\n            self.kpr = Address(args['kpr'])     # keeper who barked\n            self.coin = Rad(args['coin'])       # total kick incentive (tip + tab*chip)\n            self.block = log['blockNumber']\n            self.tx_hash = log['transactionHash'].hex()\n\n        def __repr__(self):\n            return f\"Clipper.KickLog({pformat(vars(self))})\"\n\n    class TakeLog:\n        def __init__(self, log, sender):\n            args = log['args']\n            self.id = args['id']\n            self.max = Ray(args['max'])         # Max bid price specified\n            self.price = Ray(args['price'])     # Calculated bid price\n            self.owe = Rad(args['owe'])         # Dai needed to satisfy the calculated bid price\n            self.tab = Rad(args['tab'])         # Remaining debt\n            self.lot = Wad(args['lot'])         # Remaining lot\n            self.usr = Address(args['usr'])     # Liquidated vault\n            self.block = log['blockNumber']\n            self.tx_hash = log['transactionHash'].hex()\n            self.sender = sender\n\n        def __repr__(self):\n            return f\"Clipper.TakeLog({pformat(vars(self))})\"\n\n    class RedoLog(KickLog):\n        # Same fields as KickLog\n        def __repr__(self):\n            return f\"Clipper.RedoLog({pformat(vars(self))})\"\n\n    class Sale:\n        def __init__(self, id: int, pos: int, tab: Rad, lot: Wad, usr: Address, tic: int, top: Ray):\n            assert(isinstance(id, int))\n            assert(isinstance(pos, int))\n            assert(isinstance(tab, Rad))\n            assert(isinstance(lot, Wad))\n            assert(isinstance(usr, Address))\n            assert(isinstance(tic, int))\n            assert(isinstance(top, Ray))\n\n            self.id = id    # auction identifier\n            self.pos = pos  # active index\n            self.tab = tab  # dai to raise\n            self.lot = lot  # collateral to sell\n            self.usr = usr  # liquidated urn address\n            self.tic = tic  # auction start time\n            self.top = top  # starting price\n\n        def __repr__(self):\n            return f\"Clipper.Sale({pformat(vars(self))})\"\n\n    def __init__(self, web3: Web3, address: Address):\n        super(Clipper, self).__init__(web3, address, Clipper.abi)\n        assert isinstance(web3, Web3)\n        assert isinstance(address, Address)\n\n        self.web3 = web3\n        self.address = address\n        self._contract = self._get_contract(web3, self.abi, address)\n        # Albeit more elegant, this is inconsistent with AuctionContract.vat(), a method call\n        self.calc = Address(self._contract.functions.calc().call())\n        self.dog = Dog(web3, Address(self._contract.functions.dog().call()))\n        self.vat = Vat(web3, Address(self._contract.functions.vat().call()))\n\n        self.take_abi = None\n        self.redo_abi = None\n        for member in self.abi:\n            if not self.take_abi and member.get('name') == 'Take':\n                self.take_abi = member\n            if not self.redo_abi and member.get('name') == 'Redo':\n                self.redo_abi = member\n\n    def active_auctions(self) -> list:\n        active_auctions = []\n        for index in range(1, self.kicks()+1):\n            sale = self.sales(index)\n            if sale.usr != Address.zero():\n                active_auctions.append(sale)\n            index += 1\n        return active_auctions\n\n    def ilk_name(self) -> str:\n        ilk = self._contract.functions.ilk().call()\n        return Web3.toText(ilk.strip(bytes(1)))\n\n    def buf(self) -> Ray:\n        \"\"\"Multiplicative factor to increase starting price\"\"\"\n        return Ray(self._contract.functions.buf().call())\n\n    def tail(self) -> int:\n        \"\"\"Time elapsed before auction reset\"\"\"\n        return int(self._contract.functions.tail().call())\n\n    def cusp(self) -> Ray:\n        \"\"\"Percentage drop before auction reset\"\"\"\n        return Ray(self._contract.functions.cusp().call())\n\n    def chip(self) -> Wad:\n        \"\"\"Percentage of tab to suck from vow to incentivize keepers\"\"\"\n        return Wad(self._contract.functions.chip().call())\n\n    def tip(self) -> Rad:\n        \"\"\"Flat fee to suck from vow to incentivize keepers\"\"\"\n        return Rad(self._contract.functions.tip().call())\n\n    def chost(self) -> Rad:\n        \"\"\"Ilk dust times the ilk chop\"\"\"\n        return Rad(self._contract.functions.chost().call())\n\n    def kicks(self) -> int:\n        \"\"\"Number of auctions started so far.\"\"\"\n        return int(self._contract.functions.kicks().call())\n\n    def active_count(self) -> int:\n        \"\"\"Number of active and redoable auctions.\"\"\"\n        return int(self._contract.functions.count().call())\n\n    def status(self, id: int) -> (bool, Ray, Wad, Rad):\n        \"\"\"Indicates current state of the auction\n        Args:\n            id: Auction identifier.\n        \"\"\"\n        assert isinstance(id, int)\n        (needs_redo, price, lot, tab) = self._contract.functions.getStatus(id).call()\n        logging.debug(f\"Auction {id} {'needs redo ' if needs_redo else ''}with price={float(Ray(price))} \" \n                      f\"lot={float(Wad(lot))} tab={float(Rad(tab))}\")\n        return needs_redo, Ray(price), Wad(lot), Rad(tab)\n\n    def sales(self, id: int) -> Sale:\n        \"\"\"Returns the auction details.\n        Args:\n            id: Auction identifier.\n        Returns:\n            The auction details.\n        \"\"\"\n        assert(isinstance(id, int))\n\n        array = self._contract.functions.sales(id).call()\n\n        return Clipper.Sale(id=id,\n                            pos=int(array[0]),\n                            tab=Rad(array[1]),\n                            lot=Wad(array[2]),\n                            usr=Address(array[3]),\n                            tic=int(array[4]),\n                            top=Ray(array[5]))\n\n    def validate_take(self, id: int, amt: Wad, max: Ray, our_address: Address = None):\n        \"\"\"Raise assertion if collateral cannot be purchased from an auction as desired\"\"\"\n        assert isinstance(id, int)\n        assert isinstance(amt, Wad)\n        assert isinstance(max, Ray)\n\n        if our_address:\n            assert isinstance(our_address, Address)\n        else:\n            our_address = Address(self.web3.eth.defaultAccount)\n\n        (done, price, lot, tab) = self.status(id)\n        assert not done\n        assert max >= price\n\n        slice: Wad = min(lot, amt)          # Purchase as much as possible, up to amt\n        owe: Rad = Rad(slice) * Rad(price)  # DAI needed to buy a slice of this sale\n        chost = self.chost()\n\n        if Rad(owe) > tab:\n            owe = Rad(tab)\n            slice = Wad(owe / Rad(price))\n        elif owe < tab and slice < lot:\n            if (tab - owe) < chost:\n                assert tab > chost\n                owe = tab - chost\n                slice = Wad(owe / Rad(price))\n\n        tab: Rad = tab - owe\n        lot: Wad = lot - slice\n        assert self.vat.dai(our_address) >= owe\n        logger.debug(f\"Validated clip.take which will leave tab={float(tab)} and lot={float(lot)}\")\n\n    def take(self, id: int, amt: Wad, max: Ray, who: Address = None, data=b'') -> Transact:\n        \"\"\"Buy amount of collateral from auction indexed by id.\n        Args:\n            id:     Auction id\n            amt:    Upper limit on amount of collateral to buy\n            max:    Maximum acceptable price (DAI / collateral)\n            who:    Receiver of collateral and external call address\n            data:   Data to pass in external call; if length 0, no call is done\n        \"\"\"\n        assert isinstance(id, int)\n        assert isinstance(amt, Wad)\n        assert isinstance(max, Ray)\n\n        if who:\n            assert isinstance(who, Address)\n        else:\n            who = Address(self.web3.eth.defaultAccount)\n\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'take',\n                        [id, amt.value, max.value, who.address, data])\n\n    def redo(self, id: int, kpr: Address = None) -> Transact:\n        \"\"\"Restart an auction which ended without liquidating all collateral.\n            id:     Auction id\n            kpr:    Keeper that called dog.bark()\n        \"\"\"\n        assert isinstance(id, int)\n        assert isinstance(kpr, Address) or kpr is None\n\n        if kpr:\n            assert isinstance(kpr, Address)\n        else:\n            kpr = Address(self.web3.eth.defaultAccount)\n\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'redo', [id, kpr.address])\n\n    def upchost(self):\n        \"\"\"Update the the cached dust*chop value following a governance change\"\"\"\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'upchost', [])\n\n    def past_logs(self, from_block: int, to_block: int = None, chunk_size=20000):\n        logs = super().get_past_lognotes(Clipper.abi, from_block, to_block, chunk_size)\n\n        history = []\n        for log in logs:\n            if log is None:\n                continue\n            elif isinstance(log, Clipper.KickLog) \\\n                    or isinstance(log, Clipper.TakeLog) \\\n                    or isinstance(log, Clipper.RedoLog):\n                history.append(log)\n            else:\n                logger.debug(f\"Found log with signature {log.sig}\")\n        return history\n\n    def parse_event(self, event):\n        signature = Web3.toHex(event['topics'][0])\n        codec = ABICodec(default_registry)\n        if signature == \"0x7c5bfdc0a5e8192f6cd4972f382cec69116862fb62e6abff8003874c58e064b8\":\n            event_data = get_event_data(codec, self.kick_abi, event)\n            return Clipper.KickLog(event_data)\n        elif signature == \"0x05e309fd6ce72f2ab888a20056bb4210df08daed86f21f95053deb19964d86b1\":\n            event_data = get_event_data(codec, self.take_abi, event)\n            self._get_sender_for_eventlog(event_data)\n            return Clipper.TakeLog(event_data, self._get_sender_for_eventlog(event_data))\n        elif signature == \"0x275de7ecdd375b5e8049319f8b350686131c219dd4dc450a08e9cf83b03c865f\":\n            event_data = get_event_data(codec, self.redo_abi, event)\n            return Clipper.RedoLog(event_data)\n        else:\n            logger.debug(f\"Found event signature {signature}\")\n\n    def _get_sender_for_eventlog(self, event_data) -> Address:\n        tx_hash = event_data['transactionHash'].hex()\n        receipt = self.web3.eth.getTransactionReceipt(tx_hash)\n        return Address(receipt['from'])\n\n    def __repr__(self):\n        return f\"Clipper('{self.address}')\"\n"
  },
  {
    "path": "pymaker/auth.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2017-2018 reverendus\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nfrom web3 import Web3\n\nfrom pymaker import Contract, Address, Transact\nfrom pymaker.util import int_to_bytes32\n\n\nclass DSGuard(Contract):\n    \"\"\"A client for the `DSGuard` contract.\n\n    You can find the source code of the `DSGuard` contract here:\n    <https://github.com/dapphub/ds-guard>.\n\n    Attributes:\n        web3: An instance of `Web` from `web3.py`.\n        address: Ethereum address of the `DSGuard` contract.\n    \"\"\"\n\n    abi = Contract._load_abi(__name__, 'abi/DSGuard.abi')\n    bin = Contract._load_bin(__name__, 'abi/DSGuard.bin')\n\n    ANY = int_to_bytes32(2 ** 256 - 1)\n\n    def __init__(self, web3: Web3, address: Address):\n        assert(isinstance(web3, Web3))\n        assert(isinstance(address, Address))\n\n        self.web3 = web3\n        self.address = address\n        self._contract = self._get_contract(web3, self.abi, address)\n\n    @staticmethod\n    def deploy(web3: Web3):\n        return DSGuard(web3=web3, address=Contract._deploy(web3, DSGuard.abi, DSGuard.bin, []))\n\n    def permit(self, src, dst, sig: bytes) -> Transact:\n        \"\"\"Grant access to a function call.\n\n        Args:\n            src: Address of the caller, or `ANY`.\n            dst: Address of the called contract, or `ANY`.\n            sig: Signature of the called function, or `ANY`.\n\n        Returns:\n            A :py:class:`pymaker.Transact` instance, which can be used to trigger the transaction.\n        \"\"\"\n        assert(isinstance(src, Address) or isinstance(src, bytes))\n        assert(isinstance(dst, Address) or isinstance(dst, bytes))\n        assert(isinstance(sig, bytes) and len(sig) in (4, 32))\n\n        if isinstance(src, Address) and isinstance(dst, Address):\n            method = 'permit(address,address,bytes32)'\n            src = src.address\n            dst = dst.address\n\n        else:\n            method = 'permit(bytes32,bytes32,bytes32)'\n\n        return Transact(self, self.web3, self.abi, self.address, self._contract, method, [src, dst, sig])\n\n    def __repr__(self):\n        return f\"DSGuard('{self.address}')\"\n\n\n# TODO: Complete implementation and unit test\nclass DSAuth(Contract):\n\n    abi = Contract._load_abi(__name__, 'abi/DSAuth.abi')\n    bin = Contract._load_bin(__name__, 'abi/DSAuth.bin')\n\n    def __init__(self, web3: Web3, address: Address):\n        assert (isinstance(web3, Web3))\n        assert (isinstance(address, Address))\n\n        self.web3 = web3\n        self.address = address\n        self._contract = self._get_contract(web3, self.abi, address)\n\n    @staticmethod\n    def deploy(web3: Web3):\n        return DSAuth(web3=web3, address=Contract._deploy(web3, DSAuth.abi, DSAuth.bin, []))\n\n    def get_owner(self) -> Address:\n        return Address(self._contract.functions.owner().call())\n\n    def set_owner(self, owner: Address) -> Transact:\n        assert isinstance(owner, Address)\n\n        return Transact(self, self.web3, self.abi, self.address, self._contract,\n                        \"setOwner\", [owner.address])\n\n    def set_authority(self, ds_authority: Address):\n        assert isinstance(ds_authority, Address)\n\n        return Transact(self, self.web3, self.abi, self.address, self._contract,\n                        \"setAuthority\", [ds_authority.address])\n"
  },
  {
    "path": "pymaker/cdpmanager.py",
    "content": "\n# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2017-2020 ith-harvey\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n\nfrom web3 import Web3\nfrom pymaker import Address, Contract, Transact\nfrom pymaker.dss import Ilk, Urn, Vat\nfrom pymaker.numeric import Wad\n\n\nclass CdpManager(Contract):\n    \"\"\"A client for the `DSCdpManger` contract, which is a wrapper around the cdp system, for easier use.\n\n    Ref. <https://github.com/makerdao/dss-cdp-manager/blob/master/src/DssCdpManager.sol>\n    \"\"\"\n\n    abi = Contract._load_abi(__name__, 'abi/DssCdpManager.abi')\n    bin = Contract._load_bin(__name__, 'abi/DssCdpManager.bin')\n\n    def __init__(self, web3: Web3, address: Address):\n        assert isinstance(web3, Web3)\n        assert isinstance(address, Address)\n\n        self.web3 = web3\n        self.address = address\n        self._contract = self._get_contract(web3, self.abi, address)\n        self.vat = Vat(self.web3, Address(self._contract.functions.vat().call()))\n\n    def open(self, ilk: Ilk, address: Address) -> Transact:\n        assert isinstance(ilk, Ilk)\n        assert isinstance(address, Address)\n\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'open',\n                        [ilk.toBytes(), address.address])\n\n    def urn(self, cdpid: int) -> Urn:\n        '''Returns Urn for respective CDP ID'''\n        assert isinstance(cdpid, int)\n\n        urn_address = Address(self._contract.functions.urns(cdpid).call())\n        ilk = self.ilk(cdpid)\n        urn = self.vat.urn(ilk, Address(urn_address))\n\n        return urn\n\n    def owns(self, cdpid: int) -> Address:\n        '''Returns owner Address of respective CDP ID'''\n        assert isinstance(cdpid, int)\n\n        owner = Address(self._contract.functions.owns(cdpid).call())\n        return owner\n\n    def ilk(self, cdpid: int) -> Ilk:\n        '''Returns Ilk for respective CDP ID'''\n        assert isinstance(cdpid, int)\n\n        ilk = Ilk.fromBytes(self._contract.functions.ilks(cdpid).call())\n        return ilk\n\n    def first(self, address: Address) -> int:\n        '''Returns first CDP Id created by owner address'''\n        assert isinstance(address, Address)\n\n        cdpid = int(self._contract.functions.first(address.address).call())\n        return cdpid\n\n    def last(self, address: Address) -> int:\n        '''Returns last CDP Id created by owner address'''\n        assert isinstance(address, Address)\n\n        cdpid = self._contract.functions.last(address.address).call()\n        return int(cdpid)\n\n    def count(self, address: Address) -> int:\n        '''Returns number of CDP's created using the DS-Cdp-Manager contract specifically'''\n        assert isinstance(address, Address)\n\n        count = int(self._contract.functions.count(address.address).call())\n        return count\n\n    def __repr__(self):\n        return f\"CdpManager('{self.address}')\"\n"
  },
  {
    "path": "pymaker/collateral.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2019-2021 EdNoepel\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nimport logging\n\nfrom pymaker import Address, Contract\nfrom pymaker.approval import directly, hope_directly\nfrom pymaker.auctions import AuctionContract, Clipper, Flipper\nfrom pymaker.ilk import Ilk\nfrom pymaker.gas import DefaultGasPrice\nfrom pymaker.join import GemJoin\nfrom pymaker.token import DSToken, ERC20Token\n\n\nlogger = logging.getLogger()\n\n\nclass Collateral:\n    \"\"\"The `Collateral` object wraps accounting information in the Ilk with token-wide artifacts shared across\n    multiple collateral types for the same token.  For example, ETH-A and ETH-B are represented by different Ilks,\n    but will share the same gem (WETH token), GemJoin instance, and Flipper contract.\n    \"\"\"\n\n    def __init__(self, ilk: Ilk, gem: ERC20Token, adapter: GemJoin, auction: AuctionContract, pip, vat: Contract):\n        assert isinstance(ilk, Ilk)\n        assert isinstance(gem, ERC20Token)\n        assert isinstance(adapter, GemJoin)\n        assert isinstance(auction, AuctionContract)\n        assert isinstance(vat, Contract)\n\n        self.ilk = ilk\n        self.gem = gem\n        self.adapter = adapter\n        if isinstance(auction, Flipper):\n            self.flipper = auction\n            self.clipper = None\n        elif isinstance(auction, Clipper):\n            self.flipper = None\n            self.clipper = auction\n        # Points to `median` for official deployments, `DSValue` for testing purposes.\n        # Users generally have no need to interact with the pip.\n        self.pip = pip\n        self.vat = vat\n\n    def approve(self, usr: Address, **kwargs):\n        \"\"\"\n        Allows the user to move this collateral into and out of their CDP.\n\n        Args\n            usr: User making transactions with this collateral\n        \"\"\"\n        gas_price = kwargs['gas_price'] if 'gas_price' in kwargs else DefaultGasPrice()\n        self.adapter.approve(hope_directly(from_address=usr, gas_price=gas_price), self.vat.address)\n        self.adapter.approve_token(directly(from_address=usr, gas_price=gas_price))\n"
  },
  {
    "path": "pymaker/deployment.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2017-2018 reverendus, bargst\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nimport json\nimport os\nimport re\nfrom typing import Dict, List, Optional\n\nimport pkg_resources\nfrom pymaker.auctions import Clipper, Flapper, Flipper, Flopper\nfrom web3 import Web3, HTTPProvider\n\nfrom pymaker import Address\nfrom pymaker.approval import directly, hope_directly\nfrom pymaker.auth import DSGuard\nfrom pymaker.etherdelta import EtherDelta\nfrom pymaker.collateral import Collateral\nfrom pymaker.dss import Cat, Dog, Jug, Pot, Spotter, TokenFaucet, Vat, Vow\nfrom pymaker.join import DaiJoin, GemJoin, GemJoin5\nfrom pymaker.proxy import ProxyRegistry, DssProxyActionsDsr\nfrom pymaker.feed import DSValue\nfrom pymaker.gas import DefaultGasPrice\nfrom pymaker.governance import DSPause, DSChief\nfrom pymaker.numeric import Wad, Ray\nfrom pymaker.oracles import OSM\nfrom pymaker.sai import Tub, Tap, Top, Vox\nfrom pymaker.shutdown import ShutdownModule, End\nfrom pymaker.token import DSToken, DSEthToken\nfrom pymaker.vault import DSVault\nfrom pymaker.cdpmanager import CdpManager\nfrom pymaker.dsrmanager import DsrManager\n\n\ndef deploy_contract(web3: Web3, contract_name: str, args: Optional[list] = None) -> Address:\n    \"\"\"Deploys a new contract.\n\n    Args:\n        web3: An instance of `Web` from `web3.py`.\n        contract_name: Name of the contract, used to find the `abi` and `bin` files.\n        args: Optional list of contract constructor parameters.\n\n    Returns:\n        Ethereum address of the newly deployed contract, as a :py:class:`pymaker.Address` instance.\n    \"\"\"\n    assert(isinstance(web3, Web3))\n    assert(isinstance(contract_name, str))\n    assert(isinstance(args, list) or (args is None))\n\n    abi = json.loads(pkg_resources.resource_string('pymaker.deployment', f'abi/{contract_name}.abi'))\n    bytecode = str(pkg_resources.resource_string('pymaker.deployment', f'abi/{contract_name}.bin'), 'utf-8')\n    if args is not None:\n        tx_hash = web3.eth.contract(abi=abi, bytecode=bytecode).constructor(*args).transact()\n    else:\n        tx_hash = web3.eth.contract(abi=abi, bytecode=bytecode).constructor().transact()\n    receipt = web3.eth.getTransactionReceipt(tx_hash)\n    return Address(receipt['contractAddress'])\n\n\nclass Deployment:\n    \"\"\"Represents a test deployment of the Maker smart contract ecosystem for single collateral Dai (SCD).\n\n    Creating an instance of this class creates a testrpc web3 provider with the entire set\n    of Maker smart contracts deployed to it. It is used in unit tests of PyMaker, and also in\n    unit tests for individual keepers.\n    \"\"\"\n    def __init__(self):\n        web3 = Web3(HTTPProvider(\"http://localhost:8555\"))\n        web3.eth.defaultAccount = web3.eth.accounts[0]\n        our_address = Address(web3.eth.defaultAccount)\n        sai = DSToken.deploy(web3, 'DAI')\n        sin = DSToken.deploy(web3, 'SIN')\n        skr = DSToken.deploy(web3, 'PETH')\n        gem = DSToken.deploy(web3, 'WETH')\n        gov = DSToken.deploy(web3, 'MKR')\n        pip = DSValue.deploy(web3)\n        pep = DSValue.deploy(web3)\n        pit = DSVault.deploy(web3)\n\n        vox = Vox.deploy(web3, per=Ray.from_number(1))\n        tub = Tub.deploy(web3, sai=sai.address, sin=sin.address, skr=skr.address, gem=gem.address, gov=gov.address,\n                         pip=pip.address, pep=pep.address, vox=vox.address, pit=pit.address)\n        tap = Tap.deploy(web3, tub.address)\n        top = Top.deploy(web3, tub.address, tap.address)\n\n        tub._contract.functions.turn(tap.address.address).transact()\n\n        etherdelta = EtherDelta.deploy(web3,\n                                       admin=Address('0x1111100000999998888877777666665555544444'),\n                                       fee_account=Address('0x8888877777666665555544444111110000099999'),\n                                       account_levels_addr=Address('0x0000000000000000000000000000000000000000'),\n                                       fee_make=Wad.from_number(0.01),\n                                       fee_take=Wad.from_number(0.02),\n                                       fee_rebate=Wad.from_number(0.03))\n\n        # set permissions\n        dad = DSGuard.deploy(web3)\n        dad.permit(DSGuard.ANY, DSGuard.ANY, DSGuard.ANY).transact()\n        tub.set_authority(dad.address).transact()\n        for auth in [sai, sin, skr, gem, gov, pit, tap, top]:\n            auth.set_authority(dad.address).transact()\n\n        # approve\n        tub.approve(directly())\n        tap.approve(directly())\n\n        # mint some GEMs\n        gem.mint(Wad.from_number(1000000)).transact()\n\n        self.snapshot_id = web3.manager.request_blocking(\"evm_snapshot\", [])\n\n        self.web3 = web3\n        self.our_address = our_address\n        self.sai = sai\n        self.sin = sin\n        self.skr = skr\n        self.gem = gem\n        self.gov = gov\n        self.vox = vox\n        self.tub = tub\n        self.tap = tap\n        self.top = top\n        self.etherdelta = etherdelta\n\n    def reset(self):\n        \"\"\"Rollbacks all changes made since the initial deployment.\"\"\"\n        self.web3.manager.request_blocking(\"evm_revert\", [self.snapshot_id])\n        self.snapshot_id = self.web3.manager.request_blocking(\"evm_snapshot\", [])\n\n    def time_travel_by(self, seconds: int):\n        assert(isinstance(seconds, int))\n        self.web3.manager.request_blocking(\"evm_increaseTime\", [seconds])\n\n\nclass DssDeployment:\n    \"\"\"Represents a Dai Stablecoin System deployment for multi-collateral Dai (MCD).\n\n    Static method `from_json()` should be used to instantiate all the objet of\n    a deployment from a json description of all the system addresses.\n    \"\"\"\n\n    NETWORKS = {\n        \"1\": \"mainnet\",\n        \"42\": \"kovan\"\n    }\n\n    class Config:\n        def __init__(self, pause: DSPause, vat: Vat, vow: Vow, jug: Jug, cat: Cat, dog: Dog, flapper: Flapper,\n                     flopper: Flopper, pot: Pot, dai: DSToken, dai_join: DaiJoin, mkr: DSToken,\n                     spotter: Spotter, ds_chief: DSChief, esm: ShutdownModule, end: End,\n                     proxy_registry: ProxyRegistry, dss_proxy_actions: DssProxyActionsDsr, cdp_manager: CdpManager,\n                     dsr_manager: DsrManager, faucet: TokenFaucet, collaterals: Optional[Dict[str, Collateral]] = None):\n            self.pause = pause\n            self.vat = vat\n            self.vow = vow\n            self.jug = jug\n            self.cat = cat\n            self.dog = dog\n            self.flapper = flapper\n            self.flopper = flopper\n            self.pot = pot\n            self.dai = dai\n            self.dai_join = dai_join\n            self.mkr = mkr\n            self.spotter = spotter\n            self.ds_chief = ds_chief\n            self.esm = esm\n            self.end = end\n            self.proxy_registry = proxy_registry\n            self.dss_proxy_actions = dss_proxy_actions\n            self.cdp_manager = cdp_manager\n            self.dsr_manager = dsr_manager\n            self.faucet = faucet\n            self.collaterals = collaterals or {}\n\n        @staticmethod\n        def from_json(web3: Web3, conf: str):\n            def address_in_configs(key: str, conf: str) -> bool:\n                if key not in conf:\n                    return False\n                elif not conf[key]:\n                    return False\n                elif conf[key] == \"0x0000000000000000000000000000000000000000\":\n                    return False\n                else:\n                    return True\n\n            conf = json.loads(conf)\n            pause = DSPause(web3, Address(conf['MCD_PAUSE']))\n            vat = Vat(web3, Address(conf['MCD_VAT']))\n            vow = Vow(web3, Address(conf['MCD_VOW']))\n            jug = Jug(web3, Address(conf['MCD_JUG']))\n            cat = Cat(web3, Address(conf['MCD_CAT'])) if address_in_configs('MCD_CAT', conf) else None\n            dog = Dog(web3, Address(conf['MCD_DOG'])) if address_in_configs('MCD_DOG', conf) else None\n            dai = DSToken(web3, Address(conf['MCD_DAI']))\n            dai_adapter = DaiJoin(web3, Address(conf['MCD_JOIN_DAI']))\n            flapper = Flapper(web3, Address(conf['MCD_FLAP']))\n            flopper = Flopper(web3, Address(conf['MCD_FLOP']))\n            pot = Pot(web3, Address(conf['MCD_POT']))\n            mkr = DSToken(web3, Address(conf['MCD_GOV']))\n            spotter = Spotter(web3, Address(conf['MCD_SPOT']))\n            ds_chief = DSChief(web3, Address(conf['MCD_ADM']))\n            esm = ShutdownModule(web3, Address(conf['MCD_ESM']))\n            end = End(web3, Address(conf['MCD_END']))\n            proxy_registry = ProxyRegistry(web3, Address(conf['PROXY_REGISTRY']))\n            dss_proxy_actions = DssProxyActionsDsr(web3, Address(conf['PROXY_ACTIONS_DSR']))\n            cdp_manager = CdpManager(web3, Address(conf['CDP_MANAGER']))\n            dsr_manager = DsrManager(web3, Address(conf['DSR_MANAGER']))\n            faucet = TokenFaucet(web3, Address(conf['FAUCET'])) if address_in_configs('FAUCET', conf) else None\n\n            collaterals = {}\n            for name in DssDeployment.Config._infer_collaterals_from_addresses(conf.keys()):\n                ilk = vat.ilk(name[0].replace('_', '-'))\n                if name[1] == \"ETH\":\n                    gem = DSEthToken(web3, Address(conf[name[1]]))\n                else:\n                    gem = DSToken(web3, Address(conf[name[1]]))\n\n                if name[1] in ['USDC', 'WBTC', 'TUSD', 'USDT', 'GUSD', 'RENBTC']:\n                    adapter = GemJoin5(web3, Address(conf[f'MCD_JOIN_{name[0]}']))\n                else:\n                    adapter = GemJoin(web3, Address(conf[f'MCD_JOIN_{name[0]}']))\n\n                # PIP contract may be a DSValue, OSM, or bogus address.\n                pip_name = f'PIP_{name[1]}'\n                pip_address = Address(conf[pip_name]) if pip_name in conf and conf[pip_name] else None\n                val_name = f'VAL_{name[1]}'\n                val_address = Address(conf[val_name]) if val_name in conf and conf[val_name] else None\n                if pip_address:     # Configure OSM as price source\n                    pip = OSM(web3, pip_address)\n                elif val_address:   # Configure price using DSValue\n                    pip = DSValue(web3, val_address)\n                else:\n                    pip = None\n\n                auction = None\n                if f'MCD_FLIP_{name[0]}' in conf:\n                    auction = Flipper(web3, Address(conf[f'MCD_FLIP_{name[0]}']))\n                elif f'MCD_CLIP_{name[0]}' in conf:\n                    auction = Clipper(web3, Address(conf[f'MCD_CLIP_{name[0]}']))\n\n                collateral = Collateral(ilk=ilk, gem=gem, adapter=adapter, auction=auction, pip=pip, vat=vat)\n                collaterals[ilk.name] = collateral\n\n            return DssDeployment.Config(pause, vat, vow, jug, cat, dog, flapper, flopper, pot,\n                                        dai, dai_adapter, mkr, spotter, ds_chief, esm, end,\n                                        proxy_registry, dss_proxy_actions, cdp_manager,\n                                        dsr_manager, faucet, collaterals)\n\n        @staticmethod\n        def _infer_collaterals_from_addresses(keys: []) -> List:\n            collaterals = []\n            for key in keys:\n                match = re.search(r'MCD_[CF]LIP_(?!CALC)((\\w+)_\\w+)', key)\n                if match:\n                    collaterals.append((match.group(1), match.group(2)))\n                    continue\n                match = re.search(r'MCD_[CF]LIP_(?!CALC)(\\w+)', key)\n                if match:\n                    collaterals.append((match.group(1), match.group(1)))\n\n            return collaterals\n\n        def to_dict(self) -> dict:\n            conf_dict = {\n                'MCD_PAUSE': self.pause.address.address,\n                'MCD_VAT': self.vat.address.address,\n                'MCD_VOW': self.vow.address.address,\n                'MCD_JUG': self.jug.address.address,\n                'MCD_FLAP': self.flapper.address.address,\n                'MCD_FLOP': self.flopper.address.address,\n                'MCD_POT': self.pot.address.address,\n                'MCD_DAI': self.dai.address.address,\n                'MCD_JOIN_DAI': self.dai_join.address.address,\n                'MCD_GOV': self.mkr.address.address,\n                'MCD_SPOT': self.spotter.address.address,\n                'MCD_ADM': self.ds_chief.address.address,\n                'MCD_ESM': self.esm.address.address,\n                'MCD_END': self.end.address.address,\n                'PROXY_REGISTRY': self.proxy_registry.address.address,\n                'PROXY_ACTIONS_DSR': self.dss_proxy_actions.address.address,\n                'CDP_MANAGER': self.cdp_manager.address.address,\n                'DSR_MANAGER': self.dsr_manager.address.address\n            }\n\n            if self.cat:\n                conf_dict['MCD_CAT'] = self.cat.address.address\n            if self.dog:\n                conf_dict['MCD_DOG'] = self.dog.address.address\n            if self.faucet:\n                conf_dict['FAUCET'] = self.faucet.address.address\n\n            for collateral in self.collaterals.values():\n                match = re.search(r'(\\w+)(?:-\\w+)?', collateral.ilk.name)\n                name = (collateral.ilk.name.replace('-', '_'), match.group(1))\n                conf_dict[name[1]] = collateral.gem.address.address\n                if collateral.pip:\n                    conf_dict[f'PIP_{name[1]}'] = collateral.pip.address.address\n                conf_dict[f'MCD_JOIN_{name[0]}'] = collateral.adapter.address.address\n                if collateral.flipper:\n                    conf_dict[f'MCD_FLIP_{name[0]}'] = collateral.flipper.address.address\n                elif collateral.clipper:\n                    conf_dict[f'MCD_CLIP_{name[0]}'] = collateral.clipper.address.address\n\n            return conf_dict\n\n        def to_json(self) -> str:\n            return json.dumps(self.to_dict())\n\n    def __init__(self, web3: Web3, config: Config):\n        assert isinstance(web3, Web3)\n        assert isinstance(config, DssDeployment.Config)\n\n        self.web3 = web3\n        self.config = config\n        self.pause = config.pause\n        self.vat = config.vat\n        self.vow = config.vow\n        self.jug = config.jug\n        self.cat = config.cat\n        self.dog = config.dog\n        self.flapper = config.flapper\n        self.flopper = config.flopper\n        self.pot = config.pot\n        self.dai = config.dai\n        self.dai_adapter = config.dai_join\n        self.mkr = config.mkr\n        self.collaterals = config.collaterals\n        self.spotter = config.spotter\n        self.ds_chief = config.ds_chief\n        self.esm = config.esm\n        self.end = config.end\n        self.proxy_registry = config.proxy_registry\n        self.dss_proxy_actions = config.dss_proxy_actions\n        self.cdp_manager = config.cdp_manager\n        self.dsr_manager = config.dsr_manager\n        self.faucet = config.faucet\n\n    @staticmethod\n    def from_json(web3: Web3, conf: str):\n        return DssDeployment(web3, DssDeployment.Config.from_json(web3, conf))\n\n    def to_json(self) -> str:\n        return self.config.to_json()\n\n    @staticmethod\n    def from_node(web3: Web3):\n        assert isinstance(web3, Web3)\n\n        network = DssDeployment.NETWORKS.get(web3.net.version, \"testnet\")\n\n        return DssDeployment.from_network(web3=web3, network=network)\n\n    @staticmethod\n    def from_network(web3: Web3, network: str):\n        assert isinstance(web3, Web3)\n        assert isinstance(network, str)\n\n        cwd = os.path.dirname(os.path.realpath(__file__))\n        addresses_path = os.path.join(cwd, \"../config\", f\"{network}-addresses.json\")\n\n        return DssDeployment.from_json(web3=web3, conf=open(addresses_path, \"r\").read())\n\n    def approve_dai(self, usr: Address, **kwargs):\n        \"\"\"\n        Allows the user to draw Dai from and repay Dai to their CDPs.\n\n        Args\n            usr: Recipient of Dai from one or more CDPs\n        \"\"\"\n        assert isinstance(usr, Address)\n\n        gas_price = kwargs['gas_price'] if 'gas_price' in kwargs else DefaultGasPrice()\n        self.dai_adapter.approve(approval_function=hope_directly(from_address=usr, gas_price=gas_price),\n                                 source=self.vat.address)\n        self.dai.approve(self.dai_adapter.address).transact(from_address=usr, gas_price=gas_price)\n\n    def active_auctions(self) -> dict:\n        flips = {}\n        clips = {}\n        for collateral in self.collaterals.values():\n            # Each collateral has it's own liquidation contract; add auctions from each.\n            if collateral.flipper:\n                flips[collateral.ilk.name] = collateral.flipper.active_auctions()\n            elif collateral.clipper:\n                clips[collateral.ilk.name] = collateral.clipper.active_auctions()\n\n        return {\n            \"flips\": flips,\n            \"clips\": clips,\n            \"flaps\": self.flapper.active_auctions(),\n            \"flops\": self.flopper.active_auctions()\n        }\n\n    def __repr__(self):\n        return f'DssDeployment({self.config.to_json()})'\n"
  },
  {
    "path": "pymaker/dsr.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C)2019 grandizzy\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nimport logging\n\n\nfrom pymaker import Address, Transact, Calldata\nfrom pymaker.numeric import Wad, Ray\nfrom pymaker.proxy import DSProxy\nfrom pymaker.deployment import DssDeployment\n\n\nlogger = logging.getLogger()\n\n\nclass Dsr:\n    \"\"\" DSR Client implementation\n    \"\"\"\n\n    _ZERO_ADDRESS = Address(\"0x0000000000000000000000000000000000000000\")\n\n    def __init__(self, mcd: DssDeployment, owner: Address):\n        assert (isinstance(mcd, DssDeployment))\n        assert (isinstance(owner, Address))\n\n        self.owner = owner\n        self.mcd = mcd\n\n    def has_proxy(self) -> bool:\n        return self.mcd.proxy_registry.proxies(self.owner) != self._ZERO_ADDRESS\n\n    def get_proxy(self) -> DSProxy:\n        return DSProxy(self.mcd.web3, Address(self.mcd.proxy_registry.proxies(self.owner)))\n\n    def build_proxy(self) -> Transact:\n        return self.mcd.proxy_registry.build(self.owner)\n\n    def chi(self) -> Ray:\n        return self.mcd.pot.chi()\n\n    def get_total_dai(self) -> Wad:\n        return self.mcd.pot.pie() * self.chi()\n\n    def dsr(self) -> Ray:\n        return self.mcd.pot.dsr()\n\n    def get_balance(self, proxy: Address) -> Wad:\n        assert (isinstance(proxy, Address))\n\n        total_pie = self.mcd.pot.pie()\n        if total_pie == Wad.from_number(0):\n            return Wad.from_number(0)\n\n        slice = self.mcd.pot.pie_of(proxy)\n        portion = slice / total_pie\n        dai_in_pot = self.mcd.vat.dai(self.mcd.pot.address)\n\n        return Wad(dai_in_pot * portion)\n\n    def join(self, amount: Wad, proxy: DSProxy) -> Transact:\n        assert (isinstance(amount, Wad))\n        assert (isinstance(proxy, DSProxy))\n\n        return proxy.execute_at(self.mcd.dss_proxy_actions.address,\n                                Calldata.from_signature(\n                                    self.mcd.web3,\n                                    \"join(address,address,uint256)\",\n                                    [\n                                        self.mcd.dai_adapter.address.address,\n                                        self.mcd.pot.address.address,\n                                        amount.value\n                                    ])\n                                )\n\n    def exit(self, amount: Wad, proxy: DSProxy) -> Transact:\n        assert (isinstance(amount, Wad))\n        assert (isinstance(proxy, DSProxy))\n\n        return proxy.execute_at(self.mcd.dss_proxy_actions.address,\n                                Calldata.from_signature(\n                                    self.mcd.web3,\n                                    \"exit(address,address,uint256)\",\n                                    [\n                                        self.mcd.dai_adapter.address.address,\n                                        self.mcd.pot.address.address,\n                                        amount.value\n                                    ])\n                                )\n\n    def exit_all(self, proxy: DSProxy) -> Transact:\n        assert (isinstance(proxy, DSProxy))\n\n        return proxy.execute_at(self.mcd.dss_proxy_actions.address,\n                                Calldata.from_signature(\n                                    self.mcd.web3,\n                                    \"exitAll(address,address)\",\n                                    [\n                                        self.mcd.dai_adapter.address.address,\n                                        self.mcd.pot.address.address\n                                    ])\n                                )\n"
  },
  {
    "path": "pymaker/dsrmanager.py",
    "content": "\n# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2020 Maker Ecosystem Growth Holdings, INC\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n\nfrom web3 import Web3\nfrom pymaker import Address, Contract, Transact\nfrom pymaker.dss import Pot\nfrom pymaker.join import DaiJoin\nfrom pymaker.numeric import Wad, Rad\nfrom pymaker.token import DSToken\n\n\nclass DsrManager(Contract):\n    \"\"\"\n    A client for the `DsrManger` contract, which reduces the need for proxies\n    when interacting with the Pot contract.\n\n    Ref. <https://github.com/makerdao/dsr-manager/blob/master/src/DsrManager.sol>\n    \"\"\"\n\n    abi = Contract._load_abi(__name__, 'abi/DsrManager.abi')\n    bin = Contract._load_bin(__name__, 'abi/DsrManager.bin')\n\n    def __init__(self, web3: Web3, address: Address):\n        assert isinstance(web3, Web3)\n        assert isinstance(address, Address)\n\n        self.web3 = web3\n        self.address = address\n        self._contract = self._get_contract(web3, self.abi, address)\n\n    def pot(self) -> Pot:\n        address = Address(self._contract.functions.pot().call())\n        return Pot(self.web3, address)\n\n    def dai(self) -> DSToken:\n        address = Address(self._contract.functions.dai().call())\n        return DSToken(self.web3, address)\n\n    def dai_adapter(self) -> DaiJoin:\n        address = Address(self._contract.functions.daiJoin().call())\n        return DaiJoin(self.web3, address)\n\n    def supply(self) -> Wad:\n        \"\"\"Total supply of pie locked in Pot through DsrManager\"\"\"\n        return Wad(self._contract.functions.supply().call())\n\n    def pie_of(self, usr: Address) -> Wad:\n        \"\"\"Pie balance of a given usr address\"\"\"\n        assert isinstance(usr, Address)\n\n        return Wad(self._contract.functions.pieOf(usr.address).call())\n\n    def dai_of(self, usr: Address) -> Rad:\n        \"\"\"\n        Internal Dai balance of a given usr address - current Chi is used\n        i.e. Dai balance potentially stale\n        \"\"\"\n        assert isinstance(usr, Address)\n\n        pie = self.pie_of(usr)\n        chi = self.pot().chi()\n\n        dai = Rad(pie) * Rad(chi)\n\n        return dai\n\n    def join(self, dst: Address, dai: Wad) -> Transact:\n        \"\"\"Lock a given amount of ERC20 Dai into the DSR Contract and give to dst address \"\"\"\n        assert isinstance(dst, Address)\n        assert isinstance(dai, Wad)\n\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'join',\n                        [dst.address, dai.value])\n\n    def exit(self, dst: Address, dai: Wad) -> Transact:\n        \"\"\" Free a given amount of ERC20 Dai from the DSR Contract and give to dst address \"\"\"\n        assert isinstance(dst, Address)\n        assert isinstance(dai, Wad)\n\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'exit',\n                        [dst.address, dai.value])\n\n    def exitAll(self, dst: Address) -> Transact:\n        \"\"\" Free all ERC20 Dai from the DSR Contract and give to dst address \"\"\"\n        assert isinstance(dst, Address)\n\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'exitAll', [dst.address])\n\n    def __repr__(self):\n        return f\"DsrManager('{self.address}')\"\n"
  },
  {
    "path": "pymaker/dss.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2018-2021 bargst, EdNoepel\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nimport logging\nfrom datetime import datetime\nfrom pprint import pformat\nfrom typing import List\n\nfrom web3 import Web3\n\nfrom pymaker import Address, Contract, Transact\nfrom pymaker.ilk import Ilk\nfrom pymaker.logging import LogNote\nfrom pymaker.token import DSToken, ERC20Token\nfrom pymaker.numeric import Wad, Ray, Rad\n\n\nlogger = logging.getLogger()\n\n\nclass Urn:\n    \"\"\"Models one CDP for a single collateral type and account.  Note the \"address of the Urn\" is merely the address\n    of the CDP holder.\n    \"\"\"\n\n    def __init__(self, address: Address, ilk: Ilk = None, ink: Wad = None, art: Wad = None):\n        assert isinstance(address, Address)\n        assert isinstance(ilk, Ilk) or (ilk is None)\n        assert isinstance(ink, Wad) or (ink is None)\n        assert isinstance(art, Wad) or (art is None)\n\n        self.address = address\n        self.ilk = ilk\n        self.ink = ink\n        self.art = art\n\n    def toBytes(self):\n        addr_str = self.address.address\n        return Web3.toBytes(hexstr='0x' + addr_str[2:].zfill(64))\n\n    @staticmethod\n    def fromBytes(urn: bytes):\n        assert isinstance(urn, bytes)\n\n        address = Address(Web3.toHex(urn[-20:]))\n        return Urn(address)\n\n    def __eq__(self, other):\n        assert isinstance(other, Urn)\n\n        return (self.address == other.address) and (self.ilk == other.ilk)\n\n    def __repr__(self):\n        repr = ''\n        if self.ilk:\n            repr += f'[{self.ilk.name}]'\n        if self.ink:\n            repr += f' ink={self.ink}'\n        if self.art:\n            repr += f' art={self.art}'\n        if repr:\n            repr = f'[{repr.strip()}]'\n        return f\"Urn('{self.address}'){repr}\"\n\n\nclass Vat(Contract):\n    \"\"\"A client for the `Vat` contract, which manages accounting for all Urns (CDPs).\n\n    Ref. <https://github.com/makerdao/dss/blob/master/src/vat.sol>\n    \"\"\"\n\n    # Identifies vault holders and collateral types they have frobbed\n    class LogFrob:\n        def __init__(self, lognote: LogNote):\n            assert isinstance(lognote, LogNote)\n\n            self.ilk = str(Web3.toText(lognote.arg1)).replace('\\x00', '')\n            self.urn = Address(Web3.toHex(lognote.arg2)[26:])\n            self.collateral_owner = Address(Web3.toHex(lognote.arg3)[26:])\n            self.dai_recipient = Address(Web3.toHex(lognote.get_bytes_at_index(3))[26:])\n            self.dink = Wad(int.from_bytes(lognote.get_bytes_at_index(4), byteorder=\"big\", signed=True))\n            self.dart = Wad(int.from_bytes(lognote.get_bytes_at_index(5), byteorder=\"big\", signed=True))\n            self.block = lognote.block\n            self.tx_hash = lognote.tx_hash\n\n        def __repr__(self):\n            return f\"LogFrob({pformat(vars(self))})\"\n\n    # Tracks movement of stablecoin between urns\n    class LogMove:\n        def __init__(self, lognote: LogNote):\n            assert isinstance(lognote, LogNote)\n\n            self.src = Address(Web3.toHex(lognote.arg1)[26:])\n            self.dst = Address(Web3.toHex(lognote.arg2)[26:])\n            self.dart = Rad(int.from_bytes(lognote.get_bytes_at_index(2), byteorder=\"big\", signed=True))\n            self.block = lognote.block\n            self.tx_hash = lognote.tx_hash\n\n        def __repr__(self):\n            return f\"LogMove({pformat(vars(self))})\"\n\n    # Shows vaults being split or merged\n    class LogFork:\n        def __init__(self, lognote: LogNote):\n            assert isinstance(lognote, LogNote)\n\n            self.ilk = str(Web3.toText(lognote.arg1)).replace('\\x00', '')\n            self.src = Address(Web3.toHex(lognote.arg2)[26:])\n            self.dst = Address(Web3.toHex(lognote.arg3)[26:])\n            self.dink = Wad(int.from_bytes(lognote.get_bytes_at_index(3), byteorder=\"big\", signed=True))\n            self.dart = Wad(int.from_bytes(lognote.get_bytes_at_index(4), byteorder=\"big\", signed=True))\n            self.block = lognote.block\n            self.tx_hash = lognote.tx_hash\n\n        def __repr__(self):\n            return f\"LogFork({pformat(vars(self))})\"\n\n    abi = Contract._load_abi(__name__, 'abi/Vat.abi')\n    bin = Contract._load_bin(__name__, 'abi/Vat.bin')\n\n    def __init__(self, web3: Web3, address: Address):\n        assert isinstance(web3, Web3)\n        assert isinstance(address, Address)\n\n        self.web3 = web3\n        self.address = address\n        self._contract = self._get_contract(web3, self.abi, address)\n\n    def init(self, ilk: Ilk) -> Transact:\n        assert isinstance(ilk, Ilk)\n\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'init', [ilk.toBytes()])\n\n    def live(self) -> bool:\n        return self._contract.functions.live().call() > 0\n\n    def wards(self, address: Address):\n        assert isinstance(address, Address)\n\n        return bool(self._contract.functions.wards(address.address).call())\n\n    def hope(self, address: Address):\n        assert isinstance(address, Address)\n\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'hope', [address.address])\n\n    def can(self, sender: Address, usr: Address):\n        assert isinstance(sender, Address)\n        assert isinstance(usr, Address)\n\n        return bool(self._contract.functions.can(sender.address, usr.address).call())\n\n    def ilk(self, name: str) -> Ilk:\n        assert isinstance(name, str)\n\n        b32_ilk = Ilk(name).toBytes()\n        (art, rate, spot, line, dust) = self._contract.functions.ilks(b32_ilk).call()\n\n        # We could get \"ink\" from the urn, but caller must provide an address.\n        return Ilk(name, rate=Ray(rate), ink=Wad(0), art=Wad(art), spot=Ray(spot), line=Rad(line), dust=Rad(dust))\n\n    def gem(self, ilk: Ilk, urn: Address) -> Wad:\n        assert isinstance(ilk, Ilk)\n        assert isinstance(urn, Address)\n\n        return Wad(self._contract.functions.gem(ilk.toBytes(), urn.address).call())\n\n    def dai(self, urn: Address) -> Rad:\n        assert isinstance(urn, Address)\n\n        return Rad(self._contract.functions.dai(urn.address).call())\n\n    def sin(self, urn: Address) -> Rad:\n        assert isinstance(urn, Address)\n\n        return Rad(self._contract.functions.sin(urn.address).call())\n\n    def urn(self, ilk: Ilk, address: Address) -> Urn:\n        assert isinstance(ilk, Ilk)\n        assert isinstance(address, Address)\n\n        (ink, art) = self._contract.functions.urns(ilk.toBytes(), address.address).call()\n        return Urn(address, ilk, Wad(ink), Wad(art))\n\n    def debt(self) -> Rad:\n        return Rad(self._contract.functions.debt().call())\n\n    def vice(self) -> Rad:\n        return Rad(self._contract.functions.vice().call())\n\n    def line(self) -> Rad:\n        \"\"\" Total debt ceiling \"\"\"\n        return Rad(self._contract.functions.Line().call())\n\n    def flux(self, ilk: Ilk, src: Address, dst: Address, wad: Wad) -> Transact:\n        \"\"\"Move Ilk balance in Vat from source address to destiny address\n\n        Args:\n            ilk: Identifies the type of collateral.\n            src: Source of the collateral (address of the source).\n            dst: Destiny of the collateral (address of the recipient).\n            wad: Amount of collateral to move.\n        \"\"\"\n        assert isinstance(ilk, Ilk)\n        assert isinstance(src, Address)\n        assert isinstance(dst, Address)\n        assert isinstance(wad, Wad)\n\n        flux_args = [ilk.toBytes(), src.address, dst.address, wad.value]\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'flux', flux_args)\n\n    def move(self, src: Address, dst: Address, rad: Rad) -> Transact:\n        \"\"\"Move Dai balance in Vat from source address to destiny address\n\n        Args:\n            src: Source of the dai (address of the source).\n            dst: Destiny of the dai (address of the recipient).\n            rad: Amount of dai to move.\n        \"\"\"\n        assert isinstance(src, Address)\n        assert isinstance(dst, Address)\n        assert isinstance(rad, Rad)\n\n        move_args = [src.address, dst.address, rad.value]\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'move', move_args)\n\n    def fork(self, ilk: Ilk, src: Address, dst: Address, dink: Wad, dart: Wad) -> Transact:\n        \"\"\"Split a Vault - binary approval or splitting/merging Vault's\n\n        Args:\n            ilk: Identifies the type of collateral.\n            src: Address of the source Urn.\n            dst: Address of the destiny Urn.\n            dink: Amount of collateral to exchange.\n            dart: Amount of stable coin debt to exchange.\n        \"\"\"\n        assert isinstance(ilk, Ilk)\n        assert isinstance(src, Address)\n        assert isinstance(dst, Address)\n        assert isinstance(dink, Wad)\n        assert isinstance(dart, Wad)\n\n        fork_args = [ilk.toBytes(), src.address, dst.address, dink.value, dart.value]\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'fork', fork_args)\n\n    def frob(self, ilk: Ilk, urn_address: Address, dink: Wad, dart: Wad, collateral_owner=None, dai_recipient=None):\n        \"\"\"Adjust amount of collateral and reserved amount of Dai for the CDP\n\n        Args:\n            ilk: Identifies the type of collateral.\n            urn_address: CDP holder (address of the Urn).\n            dink: Amount of collateral to add/remove.\n            dart: Adjust CDP debt (amount of Dai available for borrowing).\n            collateral_owner: Holder of the collateral used to fund the CDP.\n            dai_recipient: Party receiving the Dai.\n        \"\"\"\n        assert isinstance(ilk, Ilk)\n        assert isinstance(urn_address, Address)\n        assert isinstance(dink, Wad)\n        assert isinstance(dart, Wad)\n        assert isinstance(collateral_owner, Address) or (collateral_owner is None)\n        assert isinstance(dai_recipient, Address) or (dai_recipient is None)\n\n        # Usually these addresses are the same as the account holding the urn\n        v = collateral_owner or urn_address\n        w = dai_recipient or urn_address\n        assert isinstance(v, Address)\n        assert isinstance(w, Address)\n\n        self.validate_frob(ilk, urn_address, dink, dart)\n\n        if v == urn_address and w == urn_address:\n            logger.info(f\"frobbing {ilk.name} urn {urn_address.address} with dink={dink}, dart={dart}\")\n        else:\n            logger.info(f\"frobbing {ilk.name} urn {urn_address.address} \"\n                        f\"with dink={dink} from {v.address}, \"\n                        f\"dart={dart} for {w.address}\")\n\n        return Transact(self, self.web3, self.abi, self.address, self._contract,\n                        'frob', [ilk.toBytes(), urn_address.address, v.address, w.address, dink.value, dart.value])\n\n    def get_wipe_all_dart(self, ilk: Ilk, urn: Address) -> Wad:\n        \"\"\"Returns the amount of Dai required to wipe an urn without leaving any dust\n        adapted from https://github.com/makerdao/dss-proxy-actions/blob/master/src/DssProxyActions.sol#L200\"\"\"\n        assert isinstance(urn, Address)\n        assert isinstance(ilk, Ilk)\n        assert ilk.rate >= Ray.from_number(1)\n\n        rad: Rad = Rad(self.urn(ilk, urn).art) * Rad(ilk.rate)\n        wad: Wad = Wad(rad)\n        wad = wad + Wad(1) if Rad(wad) < rad else wad\n        return wad\n\n    def validate_frob(self, ilk: Ilk, address: Address, dink: Wad, dart: Wad):\n        \"\"\"Helps diagnose `frob` transaction failures by asserting on `require` conditions in the contract\"\"\"\n\n        def r(value, decimals=1):  # rounding function\n            return round(float(value), decimals)\n\n        def f(value, decimals=1):  # formatting function\n            return f\"{r(value):16,.{decimals}f}\"\n\n        assert isinstance(ilk, Ilk)\n        assert isinstance(address, Address)\n        assert isinstance(dink, Wad)\n        assert isinstance(dart, Wad)\n\n        assert self.live()  # system is live\n\n        urn = self.urn(ilk, address)\n        ilk = self.ilk(ilk.name)\n        assert ilk.rate != Ray(0)  # ilk has been initialised\n\n        ink = urn.ink + dink\n        art = urn.art + dart\n        ilk_art = ilk.art + dart\n\n        logger.debug(f\"System     | debt {f(self.debt())} | ceiling {f(self.line())}\")\n        logger.debug(f\"Collateral | debt {f(Ray(ilk_art) * ilk.rate)} | ceiling {f(ilk.line)}\")\n\n        dtab = Rad(ilk.rate * Ray(dart))\n        tab = ilk.rate * art\n        debt = self.debt() + dtab\n        logger.debug(f\"Frobbing ink={r(urn.ink)}, art={urn.art}, dtab={r(dtab)}, tab={tab}, \"\n                     f\"ilk.rate={r(ilk.rate,8)}, ilk.spot={r(ilk.spot, 4)}, vat.debt={r(debt)}\")\n\n        # either debt has decreased, or debt ceilings are not exceeded\n        under_collateral_debt_ceiling = Rad(Ray(ilk_art) * ilk.rate) <= ilk.line\n        under_system_debt_ceiling = debt < self.line()\n        calm = dart <= Wad(0) or (under_collateral_debt_ceiling and under_system_debt_ceiling)\n\n        # urn is either less risky than before, or it is safe\n        safe = (dart <= Wad(0) and dink >= Wad(0)) or tab <= Ray(ink) * ilk.spot\n\n        # urn has no debt, or a non-dusty amount\n        neat = art == Wad(0) or Rad(tab) >= ilk.dust\n\n        if not under_collateral_debt_ceiling:\n            logger.warning(\"collateral debt ceiling would be exceeded\")\n        if not under_system_debt_ceiling:\n            logger.warning(\"system debt ceiling would be exceeded\")\n        if not safe:\n            logger.warning(\"urn would be unsafe\")\n        if not neat:\n            logger.warning(\"debt would not exceed dust cutoff\")\n        assert calm and safe and neat\n\n    def past_frobs(self, from_block: int, to_block: int = None, ilk: Ilk = None, chunk_size=20000) -> List[LogFrob]:\n        \"\"\"Synchronously retrieve a list showing which ilks and urns have been frobbed.\n         Args:\n            from_block: Oldest Ethereum block to retrieve the events from.\n            to_block: Optional newest Ethereum block to retrieve the events from, defaults to current block\n            ilk: Optionally filter frobs by ilk.name\n            chunk_size: Number of blocks to fetch from chain at one time, for performance tuning\n         Returns:\n            List of past `LogFrob` events represented as :py:class:`pymaker.dss.Vat.LogFrob` class.\n        \"\"\"\n        return self.past_logs(from_block, to_block, ilk,\n                              include_forks=False, include_moves=False, chunk_size=chunk_size)\n\n    def past_logs(self, from_block: int, to_block: int = None, ilk: Ilk = None,\n                   include_forks=True, include_moves=True, chunk_size=20000) -> List[object]:\n        \"\"\"Synchronously retrieve a unordered list of vat activity, optionally filtered by collateral type.\n        Args:\n            from_block: Oldest Ethereum block to retrieve the events from.\n            to_block: Optional newest Ethereum block to retrieve the events from, defaults to current block\n            ilk: Optionally filter frobs by ilk.name\n            chunk_size: Number of blocks to fetch from chain at one time, for performance tuning\n        Returns:\n            Unordered list of past `LogFork`, `LogFrob`, and `LogMove` events.\n        \"\"\"\n        current_block = self._contract.web3.eth.blockNumber\n        assert isinstance(from_block, int)\n        assert from_block <= current_block\n        if to_block is None:\n            to_block = current_block\n        else:\n            assert isinstance(to_block, int)\n            assert to_block >= from_block\n            assert to_block <= current_block\n        assert isinstance(ilk, Ilk) or ilk is None\n        assert chunk_size > 0\n\n        logger.debug(f\"Consumer requested frob data from block {from_block} to {to_block}\")\n        start = from_block\n        end = None\n        chunks_queried = 0\n        retval = []\n        while end is None or start <= to_block:\n            chunks_queried += 1\n            end = min(to_block, start+chunk_size)\n\n            filter_params = {\n                'address': self.address.address,\n                'fromBlock': start,\n                'toBlock': end\n            }\n            logger.debug(f\"Querying logs from block {start} to {end} ({end-start} blocks); \"\n                         f\"accumulated {len(retval)} logs in {chunks_queried-1} requests\")\n\n            logs = self.web3.eth.getLogs(filter_params)\n\n            lognotes = list(map(lambda l: LogNote.from_event(l, Vat.abi), logs))\n\n            # '0x7cdd3fde' is Vat.slip (from GemJoin.join) and '0x76088703' is Vat.frob\n            logfrobs = list(filter(lambda l: l.sig == '0x76088703', lognotes))\n            logfrobs = list(map(lambda l: Vat.LogFrob(l), logfrobs))\n            if ilk is not None:\n                logfrobs = list(filter(lambda l: l.ilk == ilk.name, logfrobs))\n            retval.extend(logfrobs)\n\n            # '0xbb35783b' is Vat.move\n            if include_moves:\n                logmoves = list(filter(lambda l: l.sig == '0xbb35783b', lognotes))\n                logmoves = list(map(lambda l: Vat.LogMove(l), logmoves))\n                retval.extend(logmoves)\n\n            # '0x870c616d' is Vat.fork\n            if include_forks:\n                logforks = list(filter(lambda l: l.sig == '0x870c616d', lognotes))\n                logforks = list(map(lambda l: Vat.LogFork(l), logforks))\n                if ilk is not None:\n                    logforks = list(filter(lambda l: l.ilk == ilk.name, logforks))\n                retval.extend(logforks)\n\n            start += chunk_size\n\n        logger.debug(f\"Found {len(retval)} logs in {chunks_queried} requests\")\n        return retval\n\n    def heal(self, vice: Rad) -> Transact:\n        assert isinstance(vice, Rad)\n\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'heal', [vice.value])\n\n    def __eq__(self, other):\n        assert isinstance(other, Vat)\n        return self.address == other.address\n\n    def __repr__(self):\n        return f\"Vat('{self.address}')\"\n\n\nclass Spotter(Contract):\n    \"\"\"A client for the `Spotter` contract, which interacts with Vat for the purpose of managing collateral prices.\n    Users generally have no need to interact with this contract; it is included for unit testing purposes.\n\n    Ref. <https://github.com/makerdao/dss-deploy/blob/master/src/poke.sol>\n    \"\"\"\n\n    abi = Contract._load_abi(__name__, 'abi/Spotter.abi')\n    bin = Contract._load_bin(__name__, 'abi/Spotter.bin')\n\n    def __init__(self, web3: Web3, address: Address):\n        assert isinstance(web3, Web3)\n        assert isinstance(address, Address)\n\n        self.web3 = web3\n        self.address = address\n        self._contract = self._get_contract(web3, self.abi, address)\n\n    def poke(self, ilk: Ilk) -> Transact:\n        assert isinstance(ilk, Ilk)\n\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'poke', [ilk.toBytes()])\n\n    def vat(self) -> Address:\n        return Address(self._contract.functions.vat().call())\n\n    def par(self) -> Ray:\n        return Ray(self._contract.functions.par().call())\n\n    def mat(self, ilk: Ilk) -> Ray:\n        assert isinstance(ilk, Ilk)\n        (pip, mat) = self._contract.functions.ilks(ilk.toBytes()).call()\n\n        return Ray(mat)\n\n    def __repr__(self):\n        return f\"Spotter('{self.address}')\"\n\n\nclass Vow(Contract):\n    \"\"\"A client for the `Vow` contract, which manages liquidation of surplus Dai and settlement of collateral debt.\n    Specifically, this contract is useful for Flap and Flop auctions.\n\n    Ref. <https://github.com/makerdao/dss/blob/master/src/heal.sol>\n    \"\"\"\n\n    abi = Contract._load_abi(__name__, 'abi/Vow.abi')\n    bin = Contract._load_bin(__name__, 'abi/Vow.bin')\n\n    def __init__(self, web3: Web3, address: Address):\n        assert isinstance(web3, Web3)\n        assert isinstance(address, Address)\n\n        self.web3 = web3\n        self.address = address\n        self._contract = self._get_contract(web3, self.abi, address)\n        self.vat = Vat(web3, Address(self._contract.functions.vat().call()))\n\n    def rely(self, guy: Address) -> Transact:\n        assert isinstance(guy, Address)\n\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'rely', [guy.address])\n\n    def live(self) -> bool:\n        return self._contract.functions.live().call() > 0\n\n    def flapper(self) -> Address:\n        return Address(self._contract.functions.flapper().call())\n\n    def flopper(self) -> Address:\n        return Address(self._contract.functions.flopper().call())\n\n    def sin(self) -> Rad:\n        return Rad(self._contract.functions.Sin().call())\n\n    def sin_of(self, era: int) -> Rad:\n        return Rad(self._contract.functions.sin(era).call())\n\n    def ash(self) -> Rad:\n        return Rad(self._contract.functions.Ash().call())\n\n    def woe(self) -> Rad:\n        return (self.vat.sin(self.address) - self.sin()) - self.ash()\n\n    def wait(self) -> int:\n        return int(self._contract.functions.wait().call())\n\n    def dump(self) -> Wad:\n        return Wad(self._contract.functions.dump().call())\n\n    def sump(self) -> Rad:\n        return Rad(self._contract.functions.sump().call())\n\n    def bump(self) -> Rad:\n        return Rad(self._contract.functions.bump().call())\n\n    def hump(self) -> Rad:\n        return Rad(self._contract.functions.hump().call())\n\n    def flog(self, era: int) -> Transact:\n        assert isinstance(era, int)\n\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'flog', [era])\n\n    def heal(self, rad: Rad) -> Transact:\n        assert isinstance(rad, Rad)\n        logger.info(f\"Healing joy={self.vat.dai(self.address)} woe={self.woe()}\")\n\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'heal', [rad.value])\n\n    def kiss(self, rad: Rad) -> Transact:\n        assert isinstance(rad, Rad)\n\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'kiss', [rad.value])\n\n    def flop(self) -> Transact:\n        \"\"\"Initiate a debt auction\"\"\"\n        logger.info(f\"Initiating a flop auction with woe={self.woe()}\")\n\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'flop', [])\n\n    def flap(self) -> Transact:\n        \"\"\"Initiate a surplus auction\"\"\"\n        logger.info(f\"Initiating a flap auction with joy={self.vat.dai(self.address)}\")\n\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'flap', [])\n\n    def __repr__(self):\n        return f\"Vow('{self.address}')\"\n\n\nclass Jug(Contract):\n    \"\"\"A client for the `Jug` contract, which manages stability fees.\n\n    Ref. <https://github.com/makerdao/dss/blob/master/src/jug.sol>\n    \"\"\"\n\n    abi = Contract._load_abi(__name__, 'abi/Jug.abi')\n    bin = Contract._load_bin(__name__, 'abi/Jug.bin')\n\n    def __init__(self, web3: Web3, address: Address):\n        assert isinstance(web3, Web3)\n        assert isinstance(address, Address)\n\n        self.web3 = web3\n        self.address = address\n        self._contract = self._get_contract(web3, self.abi, address)\n        self.vat = Vat(web3, Address(self._contract.functions.vat().call()))\n        self.vow = Vow(web3, Address(self._contract.functions.vow().call()))\n\n    def init(self, ilk: Ilk) -> Transact:\n        assert isinstance(ilk, Ilk)\n\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'init', [ilk.toBytes()])\n\n    def wards(self, address: Address):\n        assert isinstance(address, Address)\n\n        return bool(self._contract.functions.wards(address.address).call())\n\n    def drip(self, ilk: Ilk) -> Transact:\n        assert isinstance(ilk, Ilk)\n\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'drip', [ilk.toBytes()])\n\n    def base(self) -> Ray:\n        return Ray(self._contract.functions.base().call())\n\n    def duty(self, ilk: Ilk) -> Ray:\n        assert isinstance(ilk, Ilk)\n\n        return Ray(self._contract.functions.ilks(ilk.toBytes()).call()[0])\n\n    def rho(self, ilk: Ilk) -> int:\n        assert isinstance(ilk, Ilk)\n\n        return Web3.toInt(self._contract.functions.ilks(ilk.toBytes()).call()[1])\n\n    def __repr__(self):\n        return f\"Jug('{self.address}')\"\n\n\nclass Cat(Contract):\n    \"\"\"A client for the `Cat` contract, used to liquidate unsafe Urns (CDPs).\n    Specifically, this contract is useful for Flip auctions.\n\n    Ref. <https://github.com/makerdao/dss/blob/master/src/cat.sol>\n    \"\"\"\n\n    # This information is read from the `Bite` event emitted from `Cat.bite`\n    class LogBite:\n        def __init__(self, log):\n            self.ilk = Ilk.fromBytes(log['args']['ilk'])\n            self.urn = Urn(Address(log['args']['urn']))\n            self.ink = Wad(log['args']['ink'])\n            self.art = Wad(log['args']['art'])\n            self.tab = Rad(log['args']['tab'])\n            self.flip = Address(log['args']['flip'])\n            self.id = int(log['args']['id'])\n            self.raw = log\n\n        def era(self, web3: Web3):\n            return web3.eth.getBlock(self.raw['blockNumber'])['timestamp']\n\n        def __eq__(self, other):\n            assert isinstance(other, Cat.LogBite)\n            return self.__dict__ == other.__dict__\n\n        def __repr__(self):\n            return pformat(vars(self))\n\n    abi = Contract._load_abi(__name__, 'abi/Cat.abi')\n    bin = Contract._load_bin(__name__, 'abi/Cat.bin')\n\n    def __init__(self, web3: Web3, address: Address):\n        assert isinstance(web3, Web3)\n        assert isinstance(address, Address)\n\n        self.web3 = web3\n        self.address = address\n        self._contract = self._get_contract(web3, self.abi, address)\n        self.vat = Vat(web3, Address(self._contract.functions.vat().call()))\n        self.vow = Vow(web3, Address(self._contract.functions.vow().call()))\n\n    def live(self) -> bool:\n        return self._contract.functions.live().call() > 0\n\n    def can_bite(self, ilk: Ilk, urn: Urn) -> bool:\n        \"\"\" Determine whether a vault can be liquidated\n\n        Args:\n            ilk: Collateral type\n            urn: Identifies the vault holder or proxy\n        \"\"\"\n        assert isinstance(ilk, Ilk)\n        assert isinstance(urn, Urn)\n        ilk = self.vat.ilk(ilk.name)\n        urn = self.vat.urn(ilk, urn.address)\n        rate = ilk.rate\n\n        # Collateral value should be less than the product of our stablecoin debt and the debt multiplier\n        safe = Ray(urn.ink) * ilk.spot >= Ray(urn.art) * rate\n        if safe:\n            return False\n\n        # Ensure there's room in the litter box\n        box: Rad = self.box()\n        litter: Rad = self.litter()\n        room: Rad = box - litter\n        if litter >= box:\n            logger.debug(f\"biting {urn.address} would exceed maximum Dai out for liquidation\")\n            return False\n        if room < ilk.dust:\n            return False\n\n        # Prevent null auction (ilk.dunk [Rad], ilk.rate [Ray], ilk.chop [Wad])\n        assert self.chop(ilk) > Wad(0)  # ensure liquidations are enabled and this uses flipper instead of clipper\n        dart: Wad = min(urn.art, Wad(min(self.dunk(ilk), room) / Rad(ilk.rate) / Rad(self.chop(ilk))))\n        dink: Wad = min(urn.ink, urn.ink * dart / urn.art)\n\n        return dart > Wad(0) and dink > Wad(0)\n\n    def bite(self, ilk: Ilk, urn: Urn) -> Transact:\n        \"\"\" Initiate liquidation of a vault, kicking off a flip auction\n\n        Args:\n            ilk: Identifies the type of collateral.\n            urn: Address of the vault holder.\n        \"\"\"\n        assert isinstance(ilk, Ilk)\n        assert isinstance(urn, Urn)\n\n        ilk = self.vat.ilk(ilk.name)\n        urn = self.vat.urn(ilk, urn.address)\n        rate = self.vat.ilk(ilk.name).rate\n        logger.info(f'Biting {ilk.name} vault {urn.address.address} with ink={urn.ink} spot={ilk.spot} '\n                    f'art={urn.art} rate={rate}')\n\n        return Transact(self, self.web3, self.abi, self.address, self._contract,\n                        'bite', [ilk.toBytes(), urn.address.address])\n\n    def chop(self, ilk: Ilk) -> Wad:\n        assert isinstance(ilk, Ilk)\n\n        (flip, chop, dunk) = self._contract.functions.ilks(ilk.toBytes()).call()\n        return Wad(chop)\n\n    def dunk(self, ilk: Ilk) -> Rad:\n        assert isinstance(ilk, Ilk)\n\n        (flip, chop, dunk) = self._contract.functions.ilks(ilk.toBytes()).call()\n        return Rad(dunk)\n\n    def flipper(self, ilk: Ilk) -> Address:\n        assert isinstance(ilk, Ilk)\n\n        (flip, chop, dunk) = self._contract.functions.ilks(ilk.toBytes()).call()\n        return Address(flip)\n\n    def box(self) -> Rad:\n        return Rad(self._contract.functions.box().call())\n\n    def litter(self) -> Rad:\n        return Rad(self._contract.functions.litter().call())\n\n    def past_bites(self, number_of_past_blocks: int, event_filter: dict = None) -> List[LogBite]:\n        \"\"\"Synchronously retrieve past LogBite events.\n\n        `LogBite` events are emitted every time someone bites a CDP.\n\n        Args:\n            number_of_past_blocks: Number of past Ethereum blocks to retrieve the events from.\n            event_filter: Filter which will be applied to returned events.\n\n        Returns:\n            List of past `LogBite` events represented as :py:class:`pymaker.dss.Cat.LogBite` class.\n        \"\"\"\n        assert isinstance(number_of_past_blocks, int)\n        assert isinstance(event_filter, dict) or (event_filter is None)\n\n        return self._past_events(self._contract, 'Bite', Cat.LogBite, number_of_past_blocks, event_filter)\n\n    def __repr__(self):\n        return f\"Cat('{self.address}')\"\n\n\nclass Dog(Contract):\n    \"\"\"A client for the `Dog` contract, used to liquidate unsafe vaults.\n    Specifically, this contract is useful for Clip auctions.\n\n    Ref. <https://github.com/makerdao/dss/blob/master/src/dog.sol>\n    \"\"\"\n\n    # This information is read from the `Bark` event emitted from `Dog.bark`\n    class LogBark:\n        def __init__(self, log):\n            self.ilk = Ilk.fromBytes(log['args']['ilk'])\n            self.urn = Urn(Address(log['args']['urn']))\n            self.ink = Wad(log['args']['ink'])\n            self.art = Wad(log['args']['art'])\n            self.due = Rad(log['args']['due'])\n            self.clip = Address(log['args']['clip'])\n            self.id = int(log['args']['id'])\n            self.raw = log\n\n        def era(self, web3: Web3):\n            return web3.eth.getBlock(self.raw['blockNumber'])['timestamp']\n\n        def __eq__(self, other):\n            assert isinstance(other, Cat.LogBite)\n            return self.__dict__ == other.__dict__\n\n        def __repr__(self):\n            return pformat(vars(self))\n\n    abi = Contract._load_abi(__name__, 'abi/Dog.abi')\n    bin = Contract._load_bin(__name__, 'abi/Dog.bin')\n\n    def __init__(self, web3: Web3, address: Address):\n        assert isinstance(web3, Web3)\n        assert isinstance(address, Address)\n\n        self.web3 = web3\n        self.address = address\n        self._contract = self._get_contract(web3, self.abi, address)\n        vat_address = Address(self._contract.functions.vat().call())\n        self.vat = Vat(web3, vat_address) if vat_address != Address.zero() else None\n        vow_address = Address(self._contract.functions.vow().call())\n        self.vow = Vow(web3, vow_address) if vow_address != Address.zero() else None\n\n    def live(self) -> bool:\n        return self._contract.functions.live().call() > 0\n\n    def clipper(self, ilk: Ilk) -> Address:\n        assert isinstance(ilk, Ilk)\n\n        (clip, chop, hole, dirt) = self._contract.functions.ilks(ilk.toBytes()).call()\n        return Address(clip)\n\n    def chop(self, ilk: Ilk) -> Wad:\n        assert isinstance(ilk, Ilk)\n        (clip, chop, hole, dirt) = self._contract.functions.ilks(ilk.toBytes()).call()\n        return Wad(chop)\n\n    def hole(self, ilk: Ilk) -> Rad:\n        assert isinstance(ilk, Ilk)\n        (clip, chop, hole, dirt) = self._contract.functions.ilks(ilk.toBytes()).call()\n        return Rad(hole)\n\n    def dirt(self, ilk: Ilk) -> Rad:\n        assert isinstance(ilk, Ilk)\n        (clip, chop, hole, dirt) = self._contract.functions.ilks(ilk.toBytes()).call()\n        return Rad(dirt)\n\n    def dog_hole(self) -> Rad:\n        return Rad(self._contract.functions.Hole().call())\n\n    def dog_dirt(self) -> Rad:\n        return Rad(self._contract.functions.Dirt().call())\n\n    def bark(self, ilk: Ilk, urn: Urn, kpr: Address = None) -> Transact:\n        \"\"\" Initiate liquidation of a vault, kicking off a flip auction\n\n        Args:\n            ilk: Identifies the type of collateral.\n            urn: Address of the vault holder.\n            kpr: Keeper address; leave empty to use web3 default.\n        \"\"\"\n        assert isinstance(ilk, Ilk)\n        assert isinstance(urn, Urn)\n        if kpr:\n            assert isinstance(kpr, Address)\n        else:\n            kpr = Address(self.web3.eth.defaultAccount)\n\n        ilk = self.vat.ilk(ilk.name)\n        urn = self.vat.urn(ilk, urn.address)\n        rate = self.vat.ilk(ilk.name).rate\n        logger.info(f'Barking {ilk.name} vault {urn.address.address} with ink={urn.ink} spot={ilk.spot} '\n                    f'art={urn.art} rate={rate}')\n\n        return Transact(self, self.web3, self.abi, self.address, self._contract,\n                        'bark', [ilk.toBytes(), urn.address.address, kpr.address])\n\n    def past_barks(self, number_of_past_blocks: int, event_filter: dict = None) -> List[LogBark]:\n        \"\"\"Synchronously retrieve past LogBark events.\n\n        `LogBark` events are emitted every time someone bites a vault.\n\n        Args:\n            number_of_past_blocks: Number of past Ethereum blocks to retrieve the events from.\n            event_filter: Filter which will be applied to returned events.\n\n        Returns:\n            List of past `LogBark` events represented as :py:class:`pymaker.dss.Dog.LogBark` class.\n        \"\"\"\n        assert isinstance(number_of_past_blocks, int)\n        assert isinstance(event_filter, dict) or (event_filter is None)\n\n        return self._past_events(self._contract, 'Bark', Dog.LogBark, number_of_past_blocks, event_filter)\n\n\nclass Pot(Contract):\n    \"\"\"A client for the `Pot` contract, which implements the DSR.\n\n    Ref. <https://github.com/makerdao/dss/blob/master/src/pot.sol>\n    \"\"\"\n\n    abi = Contract._load_abi(__name__, 'abi/Pot.abi')\n    bin = Contract._load_bin(__name__, 'abi/Pot.bin')\n\n    def __init__(self, web3: Web3, address: Address):\n        assert isinstance(web3, Web3)\n        assert isinstance(address, Address)\n\n        self.web3 = web3\n        self.address = address\n        self._contract = self._get_contract(web3, self.abi, address)\n\n    def approve(self, source: Address, approval_function, **kwargs):\n        \"\"\"Approve the pot to access Dai from our Urns\"\"\"\n        assert isinstance(source, Address)\n        assert(callable(approval_function))\n\n        approval_function(ERC20Token(web3=self.web3, address=source), self.address, self.__class__.__name__, **kwargs)\n\n    def pie_of(self, address: Address) -> Wad:\n        assert isinstance(address, Address)\n        return Wad(self._contract.functions.pie(address.address).call())\n\n    def pie(self) -> Wad:\n        pie = self._contract.functions.Pie().call()\n        return Wad(pie)\n\n    def dsr(self) -> Ray:\n        dsr = self._contract.functions.dsr().call()\n        return Ray(dsr)\n\n    def chi(self) -> Ray:\n        chi = self._contract.functions.chi().call()\n        return Ray(chi)\n\n    def rho(self) -> datetime:\n        rho = self._contract.functions.rho().call()\n        return datetime.fromtimestamp(rho)\n\n    def drip(self) -> Transact:\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'drip', [])\n\n    \"\"\" Join/Exit in Pot can be invoked through pymaker/dsrmanager.py and pymaker/dsr.py \"\"\"\n\n    def __repr__(self):\n        return f\"Pot('{self.address}')\"\n\n\nclass TokenFaucet(Contract):\n    \"\"\"A client for the `TokenFaucet` contract, to obtain ERC-20 tokens on testnets for testing purposes.\n\n    Ref. <https://github.com/makerdao/token-faucet/blob/master/src/TokenFaucet.sol>\n    \"\"\"\n\n    abi = Contract._load_abi(__name__, 'abi/TokenFaucet.abi')\n    bin = Contract._load_bin(__name__, 'abi/TokenFaucet.bin')\n\n    def __init__(self, web3: Web3, address: Address):\n        assert isinstance(web3, Web3)\n        assert isinstance(address, Address)\n\n        self.web3 = web3\n        self.address = address\n        self._contract = self._get_contract(web3, self.abi, address)\n\n    def gulp(self, address: Address):\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'gulp(address)', [address.address])\n"
  },
  {
    "path": "pymaker/etherdelta.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2017-2018 reverendus\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nimport hashlib\nimport json\nimport logging\nimport random\nimport threading\nfrom pprint import pformat\nfrom subprocess import Popen, PIPE\nfrom typing import List\n\nfrom web3 import Web3\n\nfrom pymaker import Contract, Address, Transact\nfrom pymaker.numeric import Wad\nfrom pymaker.sign import eth_sign, to_vrs\nfrom pymaker.tightly_packed import encode_address, encode_uint256\nfrom pymaker.token import ERC20Token\nfrom pymaker.util import bytes_to_hexstring, hexstring_to_bytes\n\n\nclass Order:\n    \"\"\"An off-chain order placed on the EtherDelta exchange.\n\n    Attributes:\n        maker: Order creator.\n        pay_token: Address of the ERC20 token put on sale.\n        pay_amount: Amount of the `pay_token` token put on sale.\n        buy_token: Address of the ERC20 token to be bought.\n        buy_amount: Amount of the `buy_token` to be bought.\n        expires: The block number after which the order will expire.\n        nonce: Nonce number, used to make orders similar unique and randomize signatures.\n        v: V component of the order signature.\n        r: R component of the order signature.\n        s: S component of the order signature.\n    \"\"\"\n    def __init__(self, ether_delta, maker: Address, pay_token: Address, pay_amount: Wad, buy_token: Address,\n                 buy_amount: Wad, expires: int, nonce: int, v: int, r: bytes, s: bytes):\n\n        assert(isinstance(maker, Address))\n        assert(isinstance(pay_token, Address))\n        assert(isinstance(pay_amount, Wad))\n        assert(isinstance(buy_token, Address))\n        assert(isinstance(buy_amount, Wad))\n        assert(isinstance(expires, int))\n        assert(isinstance(nonce, int))\n        assert(isinstance(v, int))\n        assert(isinstance(r, bytes))\n        assert(isinstance(s, bytes))\n\n        self._ether_delta = ether_delta\n        self.maker = maker\n        self.pay_token = pay_token\n        self.pay_amount = pay_amount\n        self.buy_token = buy_token\n        self.buy_amount = buy_amount\n        self.expires = expires\n        self.nonce = nonce\n        self.v = v\n        self.r = r\n        self.s = s\n\n    @property\n    def sell_to_buy_price(self) -> Wad:\n        return self.pay_amount / self.buy_amount\n\n    @property\n    def buy_to_sell_price(self) -> Wad:\n        return self.buy_amount / self.pay_amount\n\n    @property\n    def remaining_buy_amount(self) -> Wad:\n        return self.buy_amount - self._ether_delta.amount_filled(self)\n\n    @property\n    def remaining_sell_amount(self) -> Wad:\n        return self.pay_amount - (self._ether_delta.amount_filled(self) * self.pay_amount / self.buy_amount)\n\n    @staticmethod\n    def from_json(ether_delta, data: dict):\n        assert(isinstance(data, dict))\n        return Order(ether_delta=ether_delta, maker=Address(data['user']), pay_token=Address(data['tokenGive']),\n                     pay_amount=Wad(int(data['amountGive'])), buy_token=Address(data['tokenGet']),\n                     buy_amount=Wad(int(data['amountGet'])), expires=int(data['expires']), nonce=int(data['nonce']),\n                     v=int(data['v']), r=hexstring_to_bytes(data['r']), s=hexstring_to_bytes(data['s']))\n\n    def to_json(self) -> dict:\n        return {'contractAddr': self._ether_delta.address.address,\n                'tokenGet': self.buy_token.address,\n                'amountGet': self.buy_amount.value,\n                'tokenGive': self.pay_token.address,\n                'amountGive': self.pay_amount.value,\n                'expires': self.expires,\n                'nonce': self.nonce,\n                'v': self.v,\n                'r': bytes_to_hexstring(self.r),\n                's': bytes_to_hexstring(self.s),\n                'user': self.maker.address}\n\n    def __eq__(self, other):\n        assert(isinstance(other, Order))\n        return self.maker == other.maker and \\\n               self.pay_token == other.pay_token and \\\n               self.pay_amount == other.pay_amount and \\\n               self.buy_token == other.buy_token and \\\n               self.buy_amount == other.buy_amount and \\\n               self.expires == other.expires and \\\n               self.nonce == other.nonce and \\\n               self.v == other.v and \\\n               self.r == other.r and \\\n               self.s == other.s\n\n    def __hash__(self):\n        return hash((self.maker,\n                     self.pay_token,\n                     self.pay_amount,\n                     self.buy_token,\n                     self.buy_amount,\n                     self.expires,\n                     self.nonce,\n                     self.v,\n                     self.r,\n                     self.s))\n\n    def __str__(self):\n        return f\"('{self.buy_token}', '{self.buy_amount}',\" \\\n               f\" '{self.pay_token}', '{self.pay_amount}',\" \\\n               f\" '{self.expires}', '{self.nonce}')\"\n\n    def __repr__(self):\n        return pformat(vars(self))\n\n\nclass LogTrade:\n    def __init__(self, log):\n        self.maker = Address(log['args']['get'])\n        self.taker = Address(log['args']['give'])\n        self.pay_token = Address(log['args']['tokenGive'])\n        self.take_amount = Wad(log['args']['amountGive'])\n        self.buy_token = Address(log['args']['tokenGet'])\n        self.give_amount = Wad(log['args']['amountGet'])\n        self.raw = log\n\n    def __repr__(self):\n        return pformat(vars(self))\n\n\nclass EtherDelta(Contract):\n    \"\"\"A client for the EtherDelta exchange contract.\n\n    You can find the source code of the `EtherDelta` contract here:\n    <https://etherscan.io/address/0x8d12a197cb00d4747a1fe03395095ce2a5cc6819#code>.\n\n    Attributes:\n        web3: An instance of `Web` from `web3.py`.\n        address: Ethereum address of the `EtherDelta` contract.\n    \"\"\"\n\n    abi = Contract._load_abi(__name__, 'abi/EtherDelta.abi')\n    bin = Contract._load_bin(__name__, 'abi/EtherDelta.bin')\n\n    ETH_TOKEN = Address('0x0000000000000000000000000000000000000000')\n\n    @staticmethod\n    def deploy(web3: Web3,\n               admin: Address,\n               fee_account: Address,\n               account_levels_addr: Address,\n               fee_make: Wad,\n               fee_take: Wad,\n               fee_rebate: Wad):\n        \"\"\"Deploy a new instance of the `EtherDelta` contract.\n\n        Args:\n            web3: An instance of `Web` from `web3.py`.\n\n        Returns:\n            A `EtherDelta` class instance.\n        \"\"\"\n        return EtherDelta(web3=web3,\n                          address=Contract._deploy(web3, EtherDelta.abi, EtherDelta.bin, [\n                              admin.address,\n                              fee_account.address,\n                              account_levels_addr.address,\n                              fee_make.value,\n                              fee_take.value,\n                              fee_rebate.value\n                          ]))\n\n    def __init__(self, web3: Web3, address: Address):\n        assert(isinstance(web3, Web3))\n        assert(isinstance(address, Address))\n\n        self.web3 = web3\n        self.address = address\n        self._contract = self._get_contract(web3, self.abi, address)\n\n    def approve(self, tokens: List[ERC20Token], approval_function):\n        \"\"\"Approve the EtherDelta contract to fully access balances of specified tokens.\n\n        For available approval functions (i.e. approval modes) see `directly` and `via_tx_manager`\n        in `pymaker.approval`.\n\n        Args:\n            tokens: List of :py:class:`pymaker.token.ERC20Token` class instances.\n            approval_function: Approval function (i.e. approval mode).\n        \"\"\"\n        assert(isinstance(tokens, list))\n        assert(callable(approval_function))\n\n        for token in tokens:\n            approval_function(token, self.address, 'EtherDelta')\n\n    def admin(self) -> Address:\n        \"\"\"Returns the address of the admin account.\n\n        Returns:\n            The address of the admin account.\n        \"\"\"\n        return Address(self._contract.functions.admin().call())\n\n    def fee_account(self) -> Address:\n        \"\"\"Returns the address of the fee account i.e. the account that receives all fees collected.\n\n        Returns:\n            The address of the fee account.\n        \"\"\"\n        return Address(self._contract.functions.feeAccount().call())\n\n    def account_levels_addr(self) -> Address:\n        \"\"\"Returns the address of the AccountLevels contract.\n\n        Returns:\n            The address of the AccountLevels contract.\n        \"\"\"\n        return Address(self._contract.functions.accountLevelsAddr().call())\n\n    def fee_make(self) -> Wad:\n        \"\"\"Returns the maker fee configured in the contract.\n\n        Returns:\n            The maker fee.\n        \"\"\"\n        return Wad(self._contract.functions.feeMake().call())\n\n    def fee_take(self) -> Wad:\n        \"\"\"Returns the taker fee configured in the contract.\n\n        Returns:\n            The taker fee.\n        \"\"\"\n        return Wad(self._contract.functions.feeTake().call())\n\n    def fee_rebate(self) -> Wad:\n        \"\"\"Returns the rebate fee configured in the contract.\n\n        Plase see the contract source code for more details.\n\n        Returns:\n            The rebate fee.\n        \"\"\"\n        return Wad(self._contract.functions.feeRebate().call())\n\n    def past_trade(self, number_of_past_blocks: int, event_filter: dict = None) -> List[LogTrade]:\n        \"\"\"Synchronously retrieve past LogTrade events.\n\n        `LogTrade` events are emitted by the EtherDelta contract every time someone takes an order.\n\n        Args:\n            number_of_past_blocks: Number of past Ethereum blocks to retrieve the events from.\n            event_filter: Filter which will be applied to returned events.\n\n        Returns:\n            List of past `LogTrade` events represented as :py:class:`pymaker.etherdelta.LogTrade` class.\n        \"\"\"\n        assert(isinstance(number_of_past_blocks, int))\n        assert(isinstance(event_filter, dict) or (event_filter is None))\n\n        return self._past_events(self._contract, 'Trade', LogTrade, number_of_past_blocks, event_filter)\n\n    def deposit(self, amount: Wad) -> Transact:\n        \"\"\"Deposits `amount` of raw ETH to EtherDelta.\n\n        Args:\n            amount: Amount of raw ETH to be deposited on EtherDelta.\n\n        Returns:\n            A :py:class:`pymaker.Transact` instance, which can be used to trigger the transaction.\n        \"\"\"\n        assert(isinstance(amount, Wad))\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'deposit', [], {'value': amount.value})\n\n    def withdraw(self, amount: Wad) -> Transact:\n        \"\"\"Withdraws `amount` of raw ETH from EtherDelta.\n\n        The withdrawn ETH will get transferred to the calling account.\n\n        Args:\n            amount: Amount of raw ETH to be withdrawn from EtherDelta.\n\n        Returns:\n            A :py:class:`pymaker.Transact` instance, which can be used to trigger the transaction.\n        \"\"\"\n        assert(isinstance(amount, Wad))\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'withdraw', [amount.value])\n\n    def balance_of(self, user: Address) -> Wad:\n        \"\"\"Returns the amount of raw ETH deposited by the specified user.\n\n        Args:\n            user: Address of the user to check the balance of.\n\n        Returns:\n            The raw ETH balance kept in the EtherDelta contract by the specified user.\n        \"\"\"\n        assert(isinstance(user, Address))\n        return Wad(self._contract.functions.balanceOf('0x0000000000000000000000000000000000000000', user.address).call())\n\n    def deposit_token(self, token: Address, amount: Wad) -> Transact:\n        \"\"\"Deposits `amount` of ERC20 token `token` to EtherDelta.\n\n        Tokens will be pulled from the calling account, so the EtherDelta contract needs\n        to have appropriate allowance. Either call `approve()` or set the allowance manually\n        before trying to deposit tokens.\n\n        Args:\n            token: Address of the ERC20 token to be deposited.\n            amount: Amount of token `token` to be deposited to EtherDelta.\n\n        Returns:\n            A :py:class:`pymaker.Transact` instance, which can be used to trigger the transaction.\n        \"\"\"\n        assert(isinstance(token, Address))\n        assert(isinstance(amount, Wad))\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'depositToken',\n                        [token.address, amount.value])\n\n    def withdraw_token(self, token: Address, amount: Wad) -> Transact:\n        \"\"\"Withdraws `amount` of ERC20 token `token` from EtherDelta.\n\n        Tokens will get transferred to the calling account.\n\n        Args:\n            token: Address of the ERC20 token to be withdrawn.\n            amount: Amount of token `token` to be withdrawn from EtherDelta.\n\n        Returns:\n            A :py:class:`pymaker.Transact` instance, which can be used to trigger the transaction.\n        \"\"\"\n        assert(isinstance(token, Address))\n        assert(isinstance(amount, Wad))\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'withdrawToken',\n                        [token.address, amount.value])\n\n    def balance_of_token(self, token: Address, user: Address) -> Wad:\n        \"\"\"Returns the amount of ERC20 token `token` deposited by the specified user.\n\n        Args:\n            token: Address of the ERC20 token return the balance of.\n            user: Address of the user to check the balance of.\n\n        Returns:\n            The ERC20 token `token` balance kept in the EtherDelta contract by the specified user.\n        \"\"\"\n        assert(isinstance(token, Address))\n        assert(isinstance(user, Address))\n        return Wad(self._contract.functions.balanceOf(token.address, user.address).call())\n\n    def create_order(self,\n                     pay_token: Address,\n                     pay_amount: Wad,\n                     buy_token: Address,\n                     buy_amount: Wad,\n                     expires: int) -> Order:\n        \"\"\"Creates a new off-chain order.\n\n        Although it's not necessary to have any amount of `pay_token` deposited to EtherDelta\n        before placing an order, nobody will be able to take this order until some balance of\n        'pay_token' is provided.\n\n        If you want to trade raw ETH, pass `Address('0x0000000000000000000000000000000000000000')`\n        as either `pay_token` or `buy_token`.\n\n        Args:\n            pay_token: Address of the ERC20 token you want to put on sale.\n            pay_amount: Amount of the `pay_token` token you want to put on sale.\n            buy_token: Address of the ERC20 token you want to be paid with.\n            buy_amount:  Amount of the `buy_token` you want to receive.\n            expires: The block number after which the order will expire.\n\n        Returns:\n            Newly created order as an instance of the :py:class:`pymaker.etherdelta.Order` class.\n        \"\"\"\n\n        assert(isinstance(pay_token, Address))\n        assert(isinstance(pay_amount, Wad))\n        assert(isinstance(buy_token, Address))\n        assert(isinstance(buy_amount, Wad))\n        assert(isinstance(expires, int) and (expires > 0))\n        assert(pay_amount > Wad(0))\n        assert(buy_amount > Wad(0))\n\n        nonce = self.random_nonce()\n        order_hash = hashlib.sha256(encode_address(self.address) +\n                                    encode_address(buy_token) +\n                                    encode_uint256(buy_amount.value) +\n                                    encode_address(pay_token) +\n                                    encode_uint256(pay_amount.value) +\n                                    encode_uint256(expires) +\n                                    encode_uint256(nonce)).digest()\n\n        signature = eth_sign(order_hash, self.web3)\n        v, r, s = to_vrs(signature)\n\n        return Order(self, Address(self.web3.eth.defaultAccount), pay_token, pay_amount, buy_token, buy_amount,\n                     expires, nonce, v, r, s)\n\n    def amount_available(self, order: Order) -> Wad:\n        \"\"\"Returns the amount that is still available (tradeable) for an order.\n\n        The result will never be greater than `order.buy_amount - amount_filled(order)`.\n        It can be lower though if the order maker does not have enough balance on EtherDelta.\n\n        Args:\n            order: The order object you want to know the available amount of.\n\n        Returns:\n            The available amount for the order, in terms of `buy_token`.\n        \"\"\"\n        assert(isinstance(order, Order))\n\n        return Wad(self._contract.functions.availableVolume(order.buy_token.address,\n                                                         order.buy_amount.value,\n                                                         order.pay_token.address,\n                                                         order.pay_amount.value,\n                                                         order.expires,\n                                                         order.nonce,\n                                                         order.maker.address,\n                                                         order.v if hasattr(order, 'v') else 0,\n                                                         order.r if hasattr(order, 'r') else bytes(),\n                                                         order.s if hasattr(order, 's') else bytes()).call())\n\n    def amount_filled(self, order: Order) -> Wad:\n        \"\"\"Returns the amount that has been already filled for an order.\n\n        The result will never be greater than `order.buy_amount`. It can be lower though\n        if the order maker does not have enough balance on EtherDelta.\n\n        If an order has been cancelled, `amount_filled(order)` will be always equal\n        to `order.buy_amount`. Cancelled orders basically look like completely filled ones.\n\n        Args:\n            order: The order object you want to know the filled amount of.\n\n        Returns:\n            The amount already filled for the order, in terms of `buy_token`.\n        \"\"\"\n        assert(isinstance(order, Order))\n\n        return Wad(self._contract.functions.amountFilled(order.buy_token.address,\n                                                      order.buy_amount.value,\n                                                      order.pay_token.address,\n                                                      order.pay_amount.value,\n                                                      order.expires,\n                                                      order.nonce,\n                                                      order.maker.address,\n                                                      order.v if hasattr(order, 'v') else 0,\n                                                      order.r if hasattr(order, 'r') else bytes(),\n                                                      order.s if hasattr(order, 's') else bytes()).call())\n\n    def trade(self, order: Order, amount: Wad) -> Transact:\n        \"\"\"Takes (buys) an order.\n\n        `amount` is in `buy_token` terms, it is the amount you want to buy with. It can not be higher\n        than `amount_available(order)`.\n\n        The 'amount' of `buy_token` tokens will get deducted from your EtherDelta balance if the trade was\n        successful. The corresponding amount of `pay_token` tokens will be added to your EtherDelta balance.\n\n        Args:\n            order: The order you want to take (buy).\n            amount: Amount of `buy_token` tokens that you want to be deducted from your EtherDelta balance\n                in order to buy a corresponding amount of `pay_token` tokens.\n\n        Returns:\n            A :py:class:`pymaker.Transact` instance, which can be used to trigger the transaction.\n        \"\"\"\n        assert(isinstance(order, Order))\n        assert(isinstance(amount, Wad))\n\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'trade',\n                        [order.buy_token.address,\n                         order.buy_amount.value,\n                         order.pay_token.address,\n                         order.pay_amount.value,\n                         order.expires,\n                         order.nonce,\n                         order.maker.address,\n                         order.v if hasattr(order, 'v') else 0,\n                         order.r if hasattr(order, 'r') else bytes(),\n                         order.s if hasattr(order, 's') else bytes(),\n                         amount.value])\n\n    def can_trade(self, order: Order, amount: Wad) -> bool:\n        \"\"\"Verifies whether a trade can be executed.\n\n        Verifies whether amount `amount` can be traded on order `order` i.e. whether the `trade()`\n        method executed with exactly the same parameters should succeed.\n\n        Args:\n            order: The order you want to verify the trade for.\n            amount: Amount expressed in terms of `buy_token` that you want to verify the trade for.\n\n        Returns:\n            'True' if the given amount can be traded on this order. `False` otherwise.\n        \"\"\"\n        assert(isinstance(order, Order))\n        assert(isinstance(amount, Wad))\n\n        return self._contract.functions.testTrade(order.buy_token.address,\n                                               order.buy_amount.value,\n                                               order.pay_token.address,\n                                               order.pay_amount.value,\n                                               order.expires,\n                                               order.nonce,\n                                               order.maker.address,\n                                               order.v if hasattr(order, 'v') else 0,\n                                               order.r if hasattr(order, 'r') else bytes(),\n                                               order.s if hasattr(order, 's') else bytes(),\n                                               amount.value,\n                                               self.web3.eth.defaultAccount).call()\n\n    def cancel_order(self, order: Order) -> Transact:\n        \"\"\"Cancels an existing order.\n\n        Orders can be cancelled only by their owners.\n\n        Args:\n            order: The order you want to cancel.\n\n        Returns:\n            A :py:class:`pymaker.Transact` instance, which can be used to trigger the transaction.\n        \"\"\"\n        assert(isinstance(order, Order))\n\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'cancelOrder',\n                        [order.buy_token.address,\n                         order.buy_amount.value,\n                         order.pay_token.address,\n                         order.pay_amount.value,\n                         order.expires,\n                         order.nonce,\n                         order.v if hasattr(order, 'v') else 0,\n                         order.r if hasattr(order, 'r') else bytes(),\n                         order.s if hasattr(order, 's') else bytes()])\n\n    @staticmethod\n    def random_nonce():\n        return random.randint(1, 2**32 - 1)\n\n    def __repr__(self):\n        return f\"EtherDelta('{self.address}')\"\n\n\nclass EtherDeltaApi:\n    \"\"\"A client for the EtherDelta API backend.\n\n    Attributes:\n        client_tool_directory: Directory containing the `etherdelta-client` tool.\n        client_tool_command: Command for running the `etherdelta-client` tool.\n        api_server: Base URL of the EtherDelta API backend server.\n        number_of_attempts: Number of attempts to run the `etherdelta-client` tool.\n        retry_interval: Interval between subsequent retries if order placement failed,\n            within one `etherdelta-client` run.\n        timeout: Timeout after which publish order is considered as failed by the\n            `etherdelta-client` tool. If number_of_attempts > 1, this tool will be\n            run several times though.\n    \"\"\"\n    logger = logging.getLogger()\n\n    def __init__(self,\n                 client_tool_directory: str,\n                 client_tool_command: str,\n                 api_server: str,\n                 number_of_attempts: int,\n                 retry_interval: int,\n                 timeout: int):\n        assert(isinstance(client_tool_directory, str))\n        assert(isinstance(client_tool_command, str))\n        assert(isinstance(api_server, str))\n        assert(isinstance(number_of_attempts, int))\n        assert(isinstance(retry_interval, int))\n        assert(isinstance(timeout, int))\n\n        self.client_tool_directory = client_tool_directory\n        self.client_tool_command = client_tool_command\n        self.api_server = api_server\n        self.number_of_attempts = number_of_attempts\n        self.retry_interval = retry_interval\n        self.timeout = timeout\n\n    def publish_order(self, order: Order):\n        assert(isinstance(order, Order))\n\n        def _publish_order_via_client() -> bool:\n            process = Popen(self.client_tool_command.split() + ['--url', self.api_server,\n                                                                '--timeout', str(self.timeout),\n                                                                '--retry-interval', str(self.retry_interval),\n                                                                json.dumps(order.to_json())],\n                            cwd=self.client_tool_directory, stdin=PIPE, stdout=PIPE, stderr=PIPE, shell=False)\n\n            result = process.communicate(None, timeout=self.timeout+15)\n            stdout = result[0].decode(\"utf-8\").rstrip().replace('\\n', ' -> ')\n            stderr = result[1].decode(\"utf-8\").rstrip().replace('\\n', ' -> ')\n\n            if len(stdout) > 0:\n                if process.returncode == 0:\n                    self.logger.info(f\"Output from 'etherdelta-client': {stdout}\")\n                else:\n                    self.logger.warning(f\"Non-zero exit code output from 'etherdelta-client': {stdout}\")\n\n            if len(stderr) > 0:\n                self.logger.fatal(f\"Error from 'etherdelta-client': {stderr}\")\n\n            return process.returncode == 0\n\n        def _run():\n            for attempt in range(self.number_of_attempts):\n                self.logger.info(f\"Sending order (attempt #{attempt+1}): {order}\")\n                if _publish_order_via_client():\n                    self.logger.info(f\"Order {order} sent successfully\")\n                    return\n\n            self.logger.warning(f\"Failed to send order {order}\")\n\n        threading.Thread(target=_run, daemon=True).start()\n\n    def __repr__(self):\n        return f\"EtherDeltaApi()\"\n"
  },
  {
    "path": "pymaker/feed.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2017-2018 reverendus\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nfrom web3 import Web3\n\nfrom pymaker import Contract, Address, Transact\nfrom pymaker.auth import DSAuth\n\n\nclass DSValue(DSAuth):\n    \"\"\"A client for the `DSValue` contract, a single-value data feed.\n\n    `DSValue` is a single-value data feed, which means it can be in one of two states.\n    It can either contain a value (in which case `has_value()` returns `True` and the read methods\n    return that value) or be empty (in which case `has_value()` returns `False` and the read\n    methods throw exceptions).\n\n    `DSValue` can be populated with a new value using `poke()` and cleared using `void()`.\n\n    Everybody can read from a `DSValue`.\n    Calling `poke()` and `void()` is usually whitelisted to some addresses only.\n\n    The `DSValue` contract keeps the value as a 32-byte array (Ethereum `bytes32` type).\n    Methods have been provided to cast it into `int`, read as hex etc.\n\n    You can find the source code of the `DSValue` contract here:\n    <https://github.com/dapphub/ds-value>.\n\n    Attributes:\n        web3: An instance of `Web` from `web3.py`.\n        address: Ethereum address of the `DSValue` contract.\n    \"\"\"\n\n    abi = Contract._load_abi(__name__, 'abi/DSValue.abi')\n    bin = Contract._load_bin(__name__, 'abi/DSValue.bin')\n\n    @staticmethod\n    def deploy(web3: Web3):\n        return DSValue(web3=web3, address=Contract._deploy(web3, DSValue.abi, DSValue.bin, []))\n\n    def __init__(self, web3: Web3, address: Address):\n        assert(isinstance(web3, Web3))\n        assert(isinstance(address, Address))\n\n        self.web3 = web3\n        self.address = address\n        self._contract = self._get_contract(web3, self.abi, address)\n\n    def has_value(self) -> bool:\n        \"\"\"Checks whether this instance contains a value.\n\n        Returns:\n            `True` if this instance contains a value, which can be read. `False` otherwise.\n        \"\"\"\n        return self._contract.functions.peek().call()[1]\n\n    def read(self) -> bytes:\n        \"\"\"Reads the current value from this instance as a byte array.\n\n        If this instance does not contain a value, throws an exception.\n\n        Returns:\n            A 32-byte array with the current value of this instance.\n        \"\"\"\n        return self._contract.functions.read().call()\n\n    def read_as_hex(self) -> str:\n        \"\"\"Reads the current value from this instance and converts it to a hex string.\n\n        If this instance does not contain a value, throws an exception.\n\n        Returns:\n            A string with a hexadecimal representation of the current value of this instance.\n        \"\"\"\n        return ''.join(hex(x)[2:].zfill(2) for x in self.read())\n\n    def read_as_int(self) -> int:\n        \"\"\"Reads the current value from this instance and converts it to an int.\n\n        If the value is actually a `Ray` or a `Wad`, you can convert it to one using `Ray(...)`\n        or `Wad(...)`. Please see `Ray` or `Wad` for more details.\n\n        If this instance does not contain a value, throws an exception.\n\n        Returns:\n            An integer representation of the current value of this instance.\n        \"\"\"\n        return int(self.read_as_hex(), 16)\n\n    def poke(self, new_value: bytes) -> Transact:\n        \"\"\"Populates this instance with a new value.\n\n        Args:\n            new_value: A 32-byte array with the new value to be set.\n\n        Returns:\n            A :py:class:`pymaker.Transact` instance, which can be used to trigger the transaction.\n        \"\"\"\n        assert(isinstance(new_value, bytes))\n        assert(len(new_value) == 32)\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'poke', [new_value])\n\n    def poke_with_int(self, new_value: int) -> Transact:\n        \"\"\"Populates this instance with a new value.\n\n        Handles the conversion of a Python `int` into the Solidity `bytes32` type automatically.\n\n        If the value you want to set is actually a `Ray` or a `Wad`, you can get the integer value from them\n        by accessing their `value` property. Please see `Ray` or `Wad` for more details.\n\n        Args:\n            new_value: A non-negative integer with the new value to be set.\n\n        Returns:\n            A :py:class:`pymaker.Transact` instance, which can be used to trigger the transaction.\n        \"\"\"\n        assert(isinstance(new_value, int))\n        assert(new_value >= 0)\n        return self.poke(new_value.to_bytes(32, byteorder='big'))\n\n    def void(self) -> Transact:\n        \"\"\"Removes the current value from this instance.\n\n        Returns:\n            A :py:class:`pymaker.Transact` instance, which can be used to trigger the transaction.\n        \"\"\"\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'void', [])\n\n    def __repr__(self):\n        return f\"DSValue('{self.address}')\"\n"
  },
  {
    "path": "pymaker/gas.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2017-2018 reverendus\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nimport math\nfrom typing import Optional\nfrom web3 import Web3\n\n\nclass GasPrice(object):\n    GWEI = 1000000000\n\n    \"\"\"Abstract class, which can be inherited for implementing different gas price strategies.\n\n    `GasPrice` class contains only one method, `get_gas_price`, which is responsible for\n    returning the gas price (in Wei) for a specific point in time. It is possible to build\n    custom gas price strategies by implementing this method so the gas price returned\n    increases over time. The piece of code responsible for sending Ethereum transactions\n    (please see :py:class:`pymaker.Transact`) will in this case overwrite the transaction\n    with another one, using the same `nonce` but increasing gas price. If the value returned\n    by `get_gas_price` does not go up, no new transaction gets submitted to the network.\n\n    An example custom gas price strategy my be: start with 10 GWei. If transaction has not been\n    confirmed within 10 minutes, try again with 15 GWei. If still no confirmation, increase\n    to 30 GWei and then wait indefinitely for confirmation.\n    \"\"\"\n\n    def get_gas_price(self, time_elapsed: int) -> Optional[int]:\n        \"\"\"Return gas price applicable for a given point in time.\n\n        Bear in mind that Parity (don't know about other Ethereum nodes) requires the gas\n        price for overwritten transactions to go up by at least 10%. Also, you may return\n        `None` which will make the node use the default gas price, but once you returned\n        a numeric value (gas price in Wei), you shouldn't switch back to `None` as such\n        transaction also may not get properly overwritten.\n\n        Args:\n            time_elapsed: Number of seconds since this specific Ethereum transaction\n                has been originally sent for the first time.\n\n        Returns:\n            Gas price in Wei, or `None` if default gas price should be used. Default gas price\n            means it's the Ethereum node the keeper is connected to will decide on the gas price.\n        \"\"\"\n        raise NotImplementedError(\"Please implement this method\")\n\n\nclass DefaultGasPrice(GasPrice):\n    \"\"\"Default gas price.\n\n    Uses the default gas price i.e. gas price will be decided by the Ethereum node\n    the keeper is connected to.\n    \"\"\"\n\n    def get_gas_price(self, time_elapsed: int) -> Optional[int]:\n        return None\n\n\nclass NodeAwareGasPrice(GasPrice):\n    \"\"\"Abstract baseclass which is Web3-aware.\n\n    Retrieves the default gas price provided by the Ethereum node to be consumed by subclasses.\n    \"\"\"\n\n    def __init__(self, web3: Web3):\n        assert isinstance(web3, Web3)\n        if self.__class__ == NodeAwareGasPrice:\n            raise NotImplementedError('This class is not intended to be used directly')\n        self.web3 = web3\n\n    def get_gas_price(self, time_elapsed: int) -> Optional[int]:\n        \"\"\"If user wants node to choose gas price, they should use DefaultGasPrice for the same functionality\n        without an additional HTTP request.  This baseclass exists to let a subclass manipulate the node price.\"\"\"\n        raise NotImplementedError(\"Please implement this method\")\n\n    def get_node_gas_price(self):\n        return max(self.web3.manager.request_blocking(\"eth_gasPrice\", []), 1 * self.GWEI)\n\n\nclass FixedGasPrice(GasPrice):\n    \"\"\"Fixed gas price.\n\n    Uses specified gas price instead of the default price suggested by the Ethereum\n    node the keeper is connected to. The gas price may be later changed (while the transaction\n    is still in progress) by calling the `update_gas_price` method.\n\n    Attributes:\n        gas_price: Gas price to be used (in Wei).\n    \"\"\"\n    def __init__(self, gas_price: int):\n        assert(isinstance(gas_price, int))\n        self.gas_price = gas_price\n\n    def update_gas_price(self, new_gas_price: int):\n        \"\"\"Changes the initial gas price to a higher value, preferably higher.\n\n        The only reason when calling this function makes sense is when an async transaction is in progress.\n        In this case, the loop waiting for the transaction to be mined (see :py:class:`pymaker.Transact`)\n        will resend the pending transaction again with the new gas price.\n\n        As Parity excepts the gas price to rise by at least 10% in replacement transactions, the price\n        argument supplied to this method should be accordingly higher.\n\n        Args:\n            new_gas_price: New gas price to be set (in Wei).\n        \"\"\"\n        assert(isinstance(new_gas_price, int))\n\n        self.gas_price = new_gas_price\n\n    def get_gas_price(self, time_elapsed: int) -> Optional[int]:\n        assert(isinstance(time_elapsed, int))\n        return self.gas_price\n\n\nclass IncreasingGasPrice(GasPrice):\n    \"\"\"Constantly increasing gas price.\n\n    Start with `initial_price`, then increase it by fixed amount `increase_by` every `every_secs` seconds\n    until the transaction gets confirmed. There is an optional upper limit.\n\n    Attributes:\n        initial_price: The initial gas price in Wei i.e. the price the transaction\n            is originally sent with.\n        increase_by: Gas price increase in Wei, which will happen every `every_secs` seconds.\n        every_secs: Gas price increase interval (in seconds).\n        max_price: Optional upper limit.\n    \"\"\"\n    def __init__(self, initial_price: int, increase_by: int, every_secs: int, max_price: Optional[int]):\n        assert(isinstance(initial_price, int))\n        assert(isinstance(increase_by, int))\n        assert(isinstance(every_secs, int))\n        assert(isinstance(max_price, int) or max_price is None)\n        assert(initial_price > 0)\n        assert(increase_by > 0)\n        assert(every_secs > 0)\n        if max_price is not None:\n            assert(max_price > 0)\n\n        self.initial_price = initial_price\n        self.increase_by = increase_by\n        self.every_secs = every_secs\n        self.max_price = max_price\n\n    def get_gas_price(self, time_elapsed: int) -> Optional[int]:\n        assert(isinstance(time_elapsed, int))\n\n        result = self.initial_price + int(time_elapsed/self.every_secs)*self.increase_by\n        if self.max_price is not None:\n            result = min(result, self.max_price)\n\n        return result\n\n\nclass GeometricGasPrice(GasPrice):\n    \"\"\"Geometrically increasing gas price.\n\n    Start with `initial_price`, then increase it every 'every_secs' seconds by a fixed coefficient.\n    Coefficient defaults to 1.125 (12.5%), the minimum increase for Parity to replace a transaction.\n    Coefficient can be adjusted, and there is an optional upper limit.\n\n    Attributes:\n        initial_price: The initial gas price in Wei i.e. the price the transaction is originally sent with.\n        every_secs: Gas price increase interval (in seconds).\n        coefficient: Gas price multiplier, defaults to 1.125.\n        max_price: Optional upper limit, defaults to None.\n    \"\"\"\n    def __init__(self, initial_price: int, every_secs: int, coefficient=1.125, max_price: Optional[int] = None):\n        assert (isinstance(initial_price, int))\n        assert (isinstance(every_secs, int))\n        assert (isinstance(max_price, int) or max_price is None)\n        assert (initial_price > 0)\n        assert (every_secs > 0)\n        assert (coefficient > 1)\n        if max_price is not None:\n            assert(max_price >= initial_price)\n\n        self.initial_price = initial_price\n        self.every_secs = every_secs\n        self.coefficient = coefficient\n        self.max_price = max_price\n\n    def get_gas_price(self, time_elapsed: int) -> Optional[int]:\n        assert(isinstance(time_elapsed, int))\n\n        result = self.initial_price\n        if time_elapsed >= self.every_secs:\n            for second in range(math.floor(time_elapsed/self.every_secs)):\n                result *= self.coefficient\n        if self.max_price is not None:\n            result = min(result, self.max_price)\n\n        return math.ceil(result)\n"
  },
  {
    "path": "pymaker/governance.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2019 EdNoepel\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nimport datetime\nfrom web3 import Web3\nfrom typing import List\nfrom pprint import pformat\n\nfrom pymaker import Contract, Address, Transact, Wad\nfrom pymaker.auth import DSAuth\nfrom pymaker.token import DSToken\n\n\n# TODO: Complete implementation and unit test\nclass DSPause(Contract):\n    \"\"\"A client for the `DSPause` contract, which schedules function calls after a predefined delay.\n\n    You can find the source code of the `DSPause` contract here:\n    <https://github.com/dapphub/ds-pause>.\n\n    Attributes:\n        web3: An instance of `Web` from `web3.py`.\n        address: Ethereum address of the `DSPause` contract.\n    \"\"\"\n\n    class Plan:\n        def __init__(self, usr: Address, fax: bytes, eta: datetime):\n            \"\"\"Creates a plan to be executed later.\n\n            Args:\n            usr: Address of the caller\n            fax: Identifies the calldata\n            eta: Identifies the earliest time of execution\n            \"\"\"\n            assert isinstance(usr, Address)\n            assert isinstance(fax, bytes)\n            assert isinstance(eta, datetime.datetime)\n\n            self.usr = usr\n            self.fax = fax\n            self.eta = eta.timestamp()\n\n    abi = Contract._load_abi(__name__, 'abi/DSPause.abi')\n    bin = Contract._load_bin(__name__, 'abi/DSPause.bin')\n\n    def __init__(self, web3: Web3, address: Address):\n        assert (isinstance(web3, Web3))\n        assert (isinstance(address, Address))\n\n        self.web3 = web3\n        self.address = address\n        self._contract = self._get_contract(web3, self.abi, address)\n\n    @staticmethod\n    def deploy(web3: Web3, delay: int, owner: Address, ds_auth: DSAuth):\n        return DSPause(web3=web3, address=Contract._deploy(web3, DSPause.abi, DSPause.bin,\n                                                           [delay, owner.address, ds_auth.address.address]))\n\n    # TODO: Awaiting updated ABI/BIN from dss-deploy\n    # def plot(self, plan: Plan):\n    #     return self._transact(plan, \"plot\")\n\n    def drop(self, plan: Plan):\n        return self._transact(plan, \"drop\")\n\n    def exec(self, plan: Plan) -> Transact:\n        return self._transact(plan, \"exec\")\n\n    def _transact(self, plan: Plan, function_name: str) -> Transact:\n        assert isinstance(plan, DSPause.Plan)\n        assert isinstance(function_name, str)\n\n        return Transact(self, self.web3, self.abi, self.address, self._contract, function_name,\n                        [plan.usr.address, plan.fax, int(plan.eta)])\n\n\n# TODO: Implement and unit test\nclass DSRoles(Contract):\n    \"\"\"A client for the `DSRoles` contract, which manages lists of user roles and capabilities.\n\n    You can find the source code of the `DSRoles` contract here:\n    <https://github.com/dapphub/ds-roles>.\n\n    Attributes:\n        web3: An instance of `Web` from `web3.py`.\n        address: Ethereum address of the `DSRoles` contract.\n    \"\"\"\n\n    abi = Contract._load_abi(__name__, 'abi/DSRoles.abi')\n    bin = Contract._load_bin(__name__, 'abi/DSRoles.bin')\n\n    def __init__(self, web3: Web3, address: Address):\n        assert (isinstance(web3, Web3))\n        assert (isinstance(address, Address))\n\n        self.web3 = web3\n        self.address = address\n        self._contract = self._get_contract(web3, self.abi, address)\n\n    def is_root_user(self, who: Address) -> bool:\n        assert isinstance(who, Address)\n\n        return bool(self._contract.functions.isUserRoot(who.address).call())\n\n    def set_root_user(self, who: Address, enabled=True) -> Transact:\n        assert isinstance(who, Address)\n\n        return Transact(self, self.web3, self.abi, self.address, self._contract,\n                        \"setRootUser\", [who.address, enabled])\n\n    def has_user_role(self, who: Address, role: int) -> bool:\n        assert isinstance(who, Address)\n        assert isinstance(role, int)\n        assert 0 <= role <= int('0xFFFFFFFF')\n\n        return bool(self._contract.functions.hasUserRole(who.address, role).call())\n\n    def set_user_role(self, who: Address, role: int, enabled=True) -> Transact:\n        assert isinstance(who, Address)\n        assert isinstance(role, int)\n        assert 0 <= role <= int('0xFFFFFFFF')\n\n        return Transact(self, self.web3, self.abi, self.address, self._contract,\n                        \"setUserRole\", [who.address, role, enabled])\n\n\nclass Etch:\n    def __init__(self, log):\n        self.slate = log['args']['slate']\n        self.address = log['address']\n        self.block_number = log['blockNumber']\n        self.log_index = log['logIndex']\n        self.tx_hash = log['transactionHash']\n\n    def __repr__(self):\n        return pformat(vars(self))\n\n\nclass DSChief(Contract):\n    \"\"\"A client for the `DSChief` contract, which manages lists of user roles and capabilities.\n\n    You can find the source code of the `DSChief` contract here:\n    <https://github.com/dapphub/ds-chief>.\n\n    Attributes:\n        web3: An instance of `Web` from `web3.py`.\n        address: Ethereum address of the `DSChief` contract.\n    \"\"\"\n\n    abi = Contract._load_abi(__name__, 'abi/DSChief.abi')\n    bin = Contract._load_bin(__name__, 'abi/DSChief.bin')\n\n    def __init__(self, web3: Web3, address: Address):\n        assert (isinstance(web3, Web3))\n        assert (isinstance(address, Address))\n\n        self.web3 = web3\n        self.address = address\n        self._contract = self._get_contract(web3, self.abi, address)\n\n    def live(self) -> bool:\n        return self._contract.functions.live().call()\n\n    def iou(self) -> DSToken:\n        return DSToken(self.web3, Address(self._contract.functions.IOU().call()))\n\n    def get_votes(self, address):\n        return self._contract.functions.votes(address).call()\n\n    def get_yay(self, slate, position) -> str:\n        return self._contract.functions.slates(slate, position).call()\n\n    def get_deposits(self, address) -> Wad:\n        return Wad(self._contract.functions.deposits(address).call())\n\n    def get_approvals(self, address) -> Wad:\n        return Wad(self._contract.functions.approvals(address).call())\n\n    def get_hat(self) -> Address:\n        return Address(self._contract.functions.hat().call())\n\n    def get_max_yays(self) -> int:\n        return self._contract.functions.MAX_YAYS().call()\n\n    def launch(self) -> Transact:\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'launch', [])\n\n    def lock(self, amount: Wad) -> Transact:\n        assert isinstance(amount, Wad)\n\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'lock', [amount.value])\n\n    def free(self, amount: Wad) -> Transact:\n        assert isinstance(amount, Wad)\n\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'free', [amount.value])\n\n    def etch(self, yays: List) -> Transact:\n        assert isinstance(yays, List)\n\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'etch(address[])', [yays])\n\n    def vote_yays(self, yays: List) -> Transact:\n        assert isinstance(yays, List)\n\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'vote(address[])', [yays])\n\n    def vote_etch(self, etch: Etch) -> Transact:\n        assert isinstance(etch, Etch)\n\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'vote(bytes32)', [etch.slate])\n\n    def lift(self, whom: Address) -> Transact:\n        assert isinstance(whom, Address)\n\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'lift', [whom.address])\n\n    def past_etch(self, number_of_past_blocks: int, event_filter: dict = None) -> List[Etch]:\n        \"\"\"Synchronously retrieve past Etch events.\n\n        `Etch` events are emitted by the ds-chief contract every time someone places a vote.\n\n        Args:\n            number_of_past_blocks: Number of past Ethereum blocks to retrieve the events from.\n            event_filter: Filter which will be applied to returned events.\n\n        Returns:\n            List of past `Etch` events represented as :py:class:`pymaker.governance.Etch` class.\n        \"\"\"\n        assert(isinstance(number_of_past_blocks, int))\n        assert(isinstance(event_filter, dict) or (event_filter is None))\n\n        return self._past_events(self._contract, 'Etch', Etch, number_of_past_blocks, event_filter)\n\n    def past_etch_in_range(self, from_block: int, to_block: int, event_filter: dict = None) -> List[Etch]:\n        \"\"\"Synchronously retrieve past Etch events.\n\n        `Etch` events are emitted by the ds-chief contract every time someone places a vote.\n\n        Args:\n            from_block: Starting block to retrieve the events from.\n            to_block: Last block to retrieve the events to.\n            event_filter: Filter which will be applied to returned events.\n\n        Returns:\n            List of past `Etch` events represented as :py:class:`pymaker.governance.Etch` class.\n        \"\"\"\n        assert(isinstance(from_block, int))\n        assert(isinstance(to_block, int))\n        assert(isinstance(event_filter, dict) or (event_filter is None))\n\n        return self._past_events_in_block_range(self._contract, 'Etch', Etch, from_block, to_block, event_filter)\n"
  },
  {
    "path": "pymaker/ilk.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2019-2021 EdNoepel\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nfrom typing import Optional\nfrom web3 import Web3\n\nfrom pymaker.numeric import Wad, Ray, Rad\n\n\nclass Ilk:\n    \"\"\"Models one collateral type, the combination of a token and a set of risk parameters.\n    For example, ETH-A and ETH-B are different collateral types with the same underlying token (WETH) but with\n    different risk parameters.\n    \"\"\"\n\n    def __init__(self, name: str, rate: Optional[Ray] = None,\n                 ink: Optional[Wad] = None,\n                 art: Optional[Wad] = None,\n                 spot: Optional[Ray] = None,\n                 line: Optional[Rad] = None,\n                 dust: Optional[Rad] = None):\n        assert (isinstance(name, str))\n        assert (isinstance(rate, Ray) or (rate is None))\n        assert (isinstance(ink, Wad) or (ink is None))\n        assert (isinstance(art, Wad) or (art is None))\n        assert (isinstance(spot, Ray) or (spot is None))\n        assert (isinstance(line, Rad) or (line is None))\n        assert (isinstance(dust, Rad) or (dust is None))\n\n        self.name = name\n        self.rate = rate\n        self.ink = ink\n        self.art = art\n        self.spot = spot\n        self.line = line\n        self.dust = dust\n\n    def toBytes(self):\n        return Web3.toBytes(text=self.name).ljust(32, bytes(1))\n\n    @staticmethod\n    def fromBytes(ilk: bytes):\n        assert (isinstance(ilk, bytes))\n\n        name = Web3.toText(ilk.strip(bytes(1)))\n        return Ilk(name)\n\n    def __eq__(self, other):\n        assert isinstance(other, Ilk)\n\n        return (self.name == other.name) \\\n           and (self.rate == other.rate) \\\n           and (self.ink == other.ink) \\\n           and (self.art == other.art) \\\n           and (self.spot == other.spot) \\\n           and (self.line == other.line) \\\n           and (self.dust == other.dust)\n\n    def __repr__(self):\n        repr = ''\n        if self.rate:\n            repr += f' rate={self.rate}'\n        if self.ink:\n            repr += f' Ink={self.ink}'\n        if self.art:\n            repr += f' Art={self.art}'\n        if self.spot:\n            repr += f' spot={self.spot}'\n        if self.line:\n            repr += f' line={self.line}'\n        if self.dust:\n            repr += f' dust={self.dust}'\n        if repr:\n            repr = f'[{repr.strip()}]'\n\n        return f\"Ilk('{self.name}'){repr}\"\n"
  },
  {
    "path": "pymaker/join.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2019-2021 EdNoepel\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nimport logging\n\nfrom web3 import Web3\n\nfrom pymaker import Address, Contract, Transact\nfrom pymaker.ilk import Ilk\nfrom pymaker.token import DSToken, ERC20Token\nfrom pymaker.numeric import Wad, Ray, Rad\n\n\nlogger = logging.getLogger()\n\n\nclass Join(Contract):\n    def __init__(self, web3: Web3, address: Address):\n        assert isinstance(web3, Web3)\n        assert isinstance(address, Address)\n\n        self.web3 = web3\n        self.address = address\n        self._contract = self._get_contract(web3, self.abi, address)\n        self._token: DSToken = None\n\n    def approve(self, approval_function, source: Address):\n        assert(callable(approval_function))\n        assert isinstance(source, Address)\n\n        approval_function(ERC20Token(web3=self.web3, address=source), self.address, self.__class__.__name__)\n\n    def approve_token(self, approval_function, **kwargs):\n        return self.approve(approval_function, self._token.address, **kwargs)\n\n    def join(self, usr: Address, value: Wad) -> Transact:\n        assert isinstance(usr, Address)\n        assert isinstance(value, Wad)\n\n        return Transact(self, self.web3, self.abi, self.address, self._contract,\n                        'join', [usr.address, value.value])\n\n    def exit(self, usr: Address, value: Wad) -> Transact:\n        assert isinstance(usr, Address)\n        assert isinstance(value, Wad)\n\n        return Transact(self, self.web3, self.abi, self.address, self._contract,\n                        'exit', [usr.address, value.value])\n\n\nclass DaiJoin(Join):\n    \"\"\"A client for the `DaiJoin` contract, which allows the CDP holder to draw Dai from their Urn and repay it.\n\n    Ref. <https://github.com/makerdao/dss/blob/master/src/join.sol>\n    \"\"\"\n\n    abi = Contract._load_abi(__name__, 'abi/DaiJoin.abi')\n    bin = Contract._load_bin(__name__, 'abi/DaiJoin.bin')\n\n    def __init__(self, web3: Web3, address: Address):\n        super(DaiJoin, self).__init__(web3, address)\n        self._token = self.dai()\n\n    def dai(self) -> DSToken:\n        address = Address(self._contract.functions.dai().call())\n        return DSToken(self.web3, address)\n\n\nclass GemJoin(Join):\n    \"\"\"A client for the `GemJoin` contract, which allows the user to deposit collateral into a new or existing vault.\n\n    Ref. <https://github.com/makerdao/dss/blob/master/src/join.sol>\n    \"\"\"\n\n    abi = Contract._load_abi(__name__, 'abi/GemJoin.abi')\n    bin = Contract._load_bin(__name__, 'abi/GemJoin.bin')\n\n    def __init__(self, web3: Web3, address: Address):\n        super(GemJoin, self).__init__(web3, address)\n        self._token = self.gem()\n\n    def ilk(self):\n        return Ilk.fromBytes(self._contract.functions.ilk().call())\n\n    def gem(self) -> DSToken:\n        address = Address(self._contract.functions.gem().call())\n        return DSToken(self.web3, address)\n\n    def dec(self) -> int:\n        return 18\n\n\nclass GemJoin5(GemJoin):\n    \"\"\"A client for the `GemJoin5` contract, which allows the user to deposit collateral into a new or existing vault.\n\n    Ref. <https://github.com/makerdao/dss-deploy/blob/master/src/join.sol#L274>\n    \"\"\"\n    abi = Contract._load_abi(__name__, 'abi/GemJoin5.abi')\n    bin = Contract._load_bin(__name__, 'abi/GemJoin5.bin')\n\n    def __init__(self, web3: Web3, address: Address):\n        super(GemJoin5, self).__init__(web3, address)\n        self._token = self.gem()\n\n    def dec(self) -> int:\n        return int(self._contract.functions.dec().call())\n"
  },
  {
    "path": "pymaker/keys.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2018 reverendus\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nimport getpass\nfrom typing import Optional\n\nfrom eth_account import Account\nfrom web3 import Web3\nfrom web3.middleware import construct_sign_and_send_raw_middleware\n\nfrom pymaker import Address\n\n_registered_accounts = {}\n\n\ndef register_keys(web3: Web3, keys: Optional[list]):\n    for key in keys or []:\n        register_key(web3, key)\n\n\ndef register_key(web3: Web3, key: str):\n    assert(isinstance(web3, Web3))\n\n    parsed = {}\n    for p in key.split(\",\"):\n        var, val = p.split(\"=\")\n        parsed[var] = val\n\n    register_key_file(web3, parsed.get('key_file'), parsed.get('pass_file', None))\n\n\ndef register_key_file(web3: Web3, key_file: str, pass_file: Optional[str] = None):\n    assert(isinstance(web3, Web3))\n    assert(isinstance(key_file, str))\n    assert(isinstance(pass_file, str) or (pass_file is None))\n\n    with open(key_file) as key_file_open:\n        read_key = key_file_open.read()\n        if pass_file:\n            with open(pass_file) as pass_file_open:\n                read_pass = pass_file_open.read().replace(\"\\n\", \"\")\n        else:\n            read_pass = getpass.getpass(prompt=f\"Password for {key_file}: \")\n\n        private_key = Account.decrypt(read_key, read_pass)\n        register_private_key(web3, private_key)\n\ndef get_private_key(web3: Web3, key: str):\n    assert(isinstance(web3, Web3))\n    assert(isinstance(key, str))\n\n    parsed = {}\n    for p in key.split(\",\"):\n        var, val = p.split(\"=\")\n        parsed[var] = val\n\n    with open(parsed.get('key_file')) as key_file_open:\n        read_key = key_file_open.read()\n        private_key = web3\n        if parsed.get('pass_file'):\n            with open(parsed.get('pass_file')) as pass_file_open:\n                read_pass = pass_file_open.read().replace(\"\\n\", \"\")\n        else:\n            read_pass = getpass.getpass(prompt=f\"Password for {key_file}: \")\n\n        private_key = Account.decrypt(read_key, read_pass).hex()\n        return private_key\n\ndef register_private_key(web3: Web3, private_key):\n    assert(isinstance(web3, Web3))\n\n    account = Account.privateKeyToAccount(private_key)\n\n    _registered_accounts[(web3, Address(account.address))] = account\n    web3.middleware_onion.add(construct_sign_and_send_raw_middleware(account))\n"
  },
  {
    "path": "pymaker/lifecycle.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2017-2018 reverendus\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nimport datetime\nimport logging\nimport signal\nimport threading\nimport time\n\nimport pytz\nfrom pymaker.sign import eth_sign\nfrom web3 import Web3\nfrom web3.exceptions import BlockNotFound, BlockNumberOutofRange\n\nfrom pymaker import register_filter_thread, any_filter_thread_present, stop_all_filter_threads, all_filter_threads_alive\nfrom pymaker.util import AsyncCallback\n\n\ndef trigger_event(event: threading.Event):\n    assert(isinstance(event, threading.Event))\n\n    event.set()\n\n\nclass Lifecycle:\n    \"\"\"Main keeper lifecycle controller.\n\n    This is a utility class helping to build a proper keeper lifecycle. Lifecycle\n    consists of startup phase, subscribing to Web3 events and/or timers, and\n    a shutdown phase at the end.\n\n    One could as well initialize the keeper and start listening for events themselves\n    i.e. without using `Lifecycle`, just that this class takes care of some quirks.\n    For example the listener threads of web3.py tend to die at times, which causes\n    the client to stop receiving events without even knowing something might be wrong.\n    `Lifecycle` does some tricks to monitor for it, and shutdowns the keeper the\n    moment it detects something may be wrong with the listener threads.\n\n    Other quirk is the new block filter callback taking more time to execute that\n    the time between subsequent blocks. If you do not handle it explicitly,\n    the event queue will pile up and the keeper won't work as expected.\n    `Lifecycle` used :py:class:`pymaker.util.AsyncCallback` to handle it properly.\n\n    It also handles:\n    - waiting for the node to have at least one peer and sync before starting the keeper,\n    - checking if the keeper account (`web3.eth.defaultAccount`) is unlocked.\n\n    Also, once the lifecycle is initialized, keeper starts listening for SIGINT/SIGTERM\n    signals and starts a graceful shutdown if it receives any of them.\n\n    The typical usage pattern is as follows:\n\n        with Web3Lifecycle(self.web3) as lifecycle:\n            lifecycle.on_startup(self.some_startup_function)\n            lifecycle.on_block(self.do_something)\n            lifecycle.every(15, self.do_something_else)\n            lifecycle.on_shutdown(self.some_shutdown_function)\n\n    once called like that, `Lifecycle` will enter an infinite loop.\n\n    Attributes:\n        web3: Instance of the `Web3` class from `web3.py`. Optional.\n    \"\"\"\n    logger = logging.getLogger()\n\n    def __init__(self, web3: Web3 = None):\n        self.web3 = web3\n\n        self.do_wait_for_sync = True\n        self.delay = 0\n        self.wait_for_functions = []\n        self.startup_function = None\n        self.shutdown_function = None\n        self.block_function = None\n        self.every_timers = []\n        self.event_timers = []\n\n        self.terminated_internally = False\n        self.terminated_externally = False\n        self.fatal_termination = False\n        self._at_least_one_every = False\n        self._last_block_time = None\n        self._on_block_callback = None\n\n    def __enter__(self):\n        return self\n\n    def __exit__(self, exc_type, exc_val, exc_tb):\n        # Initialization phase\n        if self.web3:\n            self.logger.info(f\"Keeper connected to {self.web3.provider}\")\n            if self.web3.eth.defaultAccount and self.web3.eth.defaultAccount != \"0x0000000000000000000000000000000000000000\":\n                self.logger.info(f\"Keeper operating as {self.web3.eth.defaultAccount}\")\n                self._check_account_unlocked()\n            else:\n                self.logger.info(f\"Keeper not operating as any particular account\")\n                # web3 calls do not work correctly if defaultAccount is empty\n                self.web3.eth.defaultAccount = \"0x0000000000000000000000000000000000000000\"\n        else:\n            self.logger.info(f\"Keeper initializing\")\n\n        # Wait for sync and peers\n        if self.web3 and self.do_wait_for_sync:\n            self._wait_for_init()\n\n        # Initial delay\n        if self.delay > 0:\n            self.logger.info(f\"Waiting for {self.delay} seconds of initial delay...\")\n            time.sleep(self.delay)\n\n        # Initial checks\n        if len(self.wait_for_functions) > 0:\n            self.logger.info(\"Waiting for initial checks to pass...\")\n\n            for index, (wait_for_function, max_wait) in enumerate(self.wait_for_functions, start=1):\n                start_time = time.time()\n                while True:\n                    try:\n                        result = wait_for_function()\n                    except Exception as e:\n                        self.logger.exception(f\"Initial check #{index} failed with an exception: '{e}'\")\n                        result = False\n\n                    if result:\n                        break\n\n                    if time.time() - start_time >= max_wait:\n                        self.logger.warning(f\"Initial check #{index} took more than {max_wait} seconds to pass, skipping\")\n                        break\n\n                    time.sleep(0.1)\n\n        # Startup phase\n        if self.startup_function:\n            self.logger.info(\"Executing keeper startup logic\")\n            self.startup_function()\n\n        # Bind `on_block`, bind `every`\n        # Enter the main loop\n        self._start_watching_blocks()\n        self._start_every_timers()\n        self._main_loop()\n\n        # Enter shutdown process\n        self.logger.info(\"Shutting down the keeper\")\n\n        # Disable all filters\n        if any_filter_thread_present():\n            self.logger.info(\"Waiting for all threads to terminate...\")\n            stop_all_filter_threads()\n\n        # If the `on_block` callback is still running, wait for it to terminate\n        if self._on_block_callback is not None:\n            self.logger.info(\"Waiting for outstanding callback to terminate...\")\n            self._on_block_callback.wait()\n\n        # If any every (timer) callback is still running, wait for it to terminate\n        if len(self.every_timers) > 0:\n            self.logger.info(\"Waiting for outstanding timers to terminate...\")\n            for timer in self.every_timers:\n                timer[1].wait()\n\n        # If any event callback is still running, wait for it to terminate\n        if len(self.event_timers) > 0:\n            self.logger.info(\"Waiting for outstanding events to terminate...\")\n            for timer in self.event_timers:\n                timer[2].wait()\n\n        # Shutdown phase\n        if self.shutdown_function:\n            self.logger.info(\"Executing keeper shutdown logic...\")\n            self.shutdown_function()\n            self.logger.info(\"Shutdown logic finished\")\n        self.logger.info(\"Keeper terminated\")\n        exit(10 if self.fatal_termination else 0)\n\n    def _wait_for_init(self):\n        # In unit-tests waiting for the node to sync does not work correctly.\n        # So we skip it.\n        if 'TestRPC' in self.web3.clientVersion:\n            return\n\n        # wait for the client to have at least one peer\n        if self.web3.net.peer_count == 0:\n            self.logger.info(f\"Waiting for the node to have at least one peer...\")\n            while self.web3.net.peer_count == 0:\n                time.sleep(0.25)\n\n        # wait for the client to sync completely,\n        # as we do not want to apply keeper logic to stale blocks\n        if self.web3.eth.syncing:\n            self.logger.info(f\"Waiting for the node to sync...\")\n            while self.web3.eth.syncing:\n                time.sleep(0.25)\n\n    def _check_account_unlocked(self):\n        try:\n            eth_sign(bytes(\"pymaker testing if account is unlocked\", \"utf-8\"), self.web3)\n        except:\n            self.logger.exception(f\"Account {self.web3.eth.defaultAccount} is not unlocked and no private key supplied for it\")\n            self.logger.fatal(f\"Unlocking the account or providing the private key is necessary for the keeper to operate\")\n            exit(-1)\n\n    def wait_for_sync(self, wait_for_sync: bool):\n        assert(isinstance(wait_for_sync, bool))\n\n        self.do_wait_for_sync = wait_for_sync\n\n    def initial_delay(self, initial_delay: int):\n        \"\"\"Make the keeper wait for specified amount of time before startup.\n\n        The primary use case is to allow background threads to have a chance to pull necessary\n        information like prices, gas prices etc. At the same time we may not want to wait indefinitely\n        for that information to become available as the price source may be down etc.\n\n        Args:\n            initial_delay: Initial delay on keeper startup (in seconds).\n        \"\"\"\n        assert(isinstance(initial_delay, int))\n\n        self.delay = initial_delay\n\n    def wait_for(self, initial_check, max_wait: int):\n        \"\"\"Make the keeper wait for the function to turn true before startup.\n\n        The primary use case is to allow background threads to have a chance to pull necessary\n        information like prices, gas prices etc. At the same time we may not want to wait indefinitely\n        for that information to become available as the price source may be down etc.\n\n        Args:\n            initial_check: Function which will be evaluated and its result compared to True.\n            max_wait: Maximum waiting time (in seconds).\n        \"\"\"\n        assert(callable(initial_check))\n        assert(isinstance(max_wait, int))\n\n        self.wait_for_functions.append((initial_check, max_wait))\n\n    def on_startup(self, callback):\n        \"\"\"Register the specified callback to be run on keeper startup.\n\n        Args:\n            callback: Function to be called on keeper startup.\n        \"\"\"\n        assert(callable(callback))\n\n        assert(self.startup_function is None)\n        self.startup_function = callback\n\n    def on_shutdown(self, callback):\n        \"\"\"Register the specified callback to be run on keeper shutdown.\n\n        Args:\n            callback: Function to be called on keeper shutdown.\n        \"\"\"\n        assert(callable(callback))\n\n        assert(self.shutdown_function is None)\n        self.shutdown_function = callback\n\n    def terminate(self, message=None):\n        if message is not None:\n            self.logger.warning(message)\n\n        self.terminated_internally = True\n\n    def on_block(self, callback):\n        \"\"\"Register the specified callback to be run for each new block received by the node.\n\n        Args:\n            callback: Function to be called for each new blocks.\n        \"\"\"\n        assert(callable(callback))\n\n        assert(self.web3 is not None)\n        assert(self.block_function is None)\n        self.block_function = callback\n\n    def on_event(self, event: threading.Event, min_frequency_in_seconds: int, callback):\n        \"\"\"\n        Register the specified callback to be called every time event is triggered,\n        but at least once every `min_frequency_in_seconds`.\n\n        Args:\n            event: Event which should be monitored.\n            min_frequency_in_seconds: Minimum execution frequency (in seconds).\n            callback: Function to be called by the timer.\n        \"\"\"\n        assert(isinstance(event, threading.Event))\n        assert(isinstance(min_frequency_in_seconds, int))\n        assert(callable(callback))\n\n        self.event_timers.append((event, min_frequency_in_seconds, AsyncCallback(callback)))\n\n    def every(self, frequency_in_seconds: int, callback):\n        \"\"\"Register the specified callback to be called by a timer.\n\n        Args:\n            frequency_in_seconds: Execution frequency (in seconds).\n            callback: Function to be called by the timer.\n        \"\"\"\n        self.every_timers.append((frequency_in_seconds, AsyncCallback(callback)))\n\n    def _sigint_sigterm_handler(self, sig, frame):\n        if self.terminated_externally:\n            self.logger.warning(\"Graceful keeper termination due to SIGINT/SIGTERM already in progress\")\n        else:\n            self.logger.warning(\"Keeper received SIGINT/SIGTERM signal, will terminate gracefully\")\n            self.terminated_externally = True\n\n    def _start_watching_blocks(self):\n        def new_block_callback(block_hash):\n            self._last_block_time = datetime.datetime.now(tz=pytz.UTC)\n            block = self.web3.eth.getBlock(block_hash)\n            block_number = block['number']\n            if not self.web3.eth.syncing:\n                max_block_number = self.web3.eth.blockNumber\n                if block_number >= max_block_number:\n                    def on_start():\n                        self.logger.debug(f\"Processing block #{block_number} ({block_hash.hex()})\")\n\n                    def on_finish():\n                        self.logger.debug(f\"Finished processing block #{block_number} ({block_hash.hex()})\")\n\n                    if not self.terminated_internally and not self.terminated_externally and not self.fatal_termination:\n                        if not self._on_block_callback.trigger(on_start, on_finish):\n                            self.logger.debug(f\"Ignoring block #{block_number} ({block_hash.hex()}),\"\n                                              f\" as previous callback is still running\")\n                    else:\n                        self.logger.debug(f\"Ignoring block #{block_number} as keeper is already terminating\")\n                else:\n                    self.logger.debug(f\"Ignoring block #{block_number} ({block_hash.hex()}),\"\n                                      f\" as there is already block #{max_block_number} available\")\n            else:\n                self.logger.info(f\"Ignoring block #{block_number} ({block_hash.hex()}), as the node is syncing\")\n\n        def new_block_watch():\n            event_filter = self.web3.eth.filter('latest')\n            logging.debug(f\"Created event filter: {event_filter}\")\n            while True:\n                try:\n                    for event in event_filter.get_new_entries():\n                        new_block_callback(event)\n                except (BlockNotFound, BlockNumberOutofRange, ValueError) as ex:\n                    self.logger.warning(f\"Node dropped event emitter; recreating latest block filter: {ex}\")\n                    event_filter = self.web3.eth.filter('latest')\n                finally:\n                    time.sleep(1)\n\n        if self.block_function:\n            self._on_block_callback = AsyncCallback(self.block_function)\n\n            block_filter = threading.Thread(target=new_block_watch, daemon=True)\n            block_filter.start()\n            register_filter_thread(block_filter)\n\n            self.logger.info(\"Watching for new blocks\")\n\n    def _start_thread_safely(self, t: threading.Thread):\n        delay = 10\n\n        while True:\n            try:\n                t.start()\n                break\n            except Exception as e:\n                self.logger.critical(f\"Failed to start a thread ({e}), trying again in {delay} seconds\")\n                time.sleep(delay)\n\n    def _start_every_timers(self):\n        for idx, timer in enumerate(self.every_timers, start=1):\n            self._start_every_timer(idx, timer[0], timer[1])\n\n        for idx, event_timer in enumerate(self.event_timers, start=1):\n            self._start_event_timer(idx, event_timer[0], event_timer[1], event_timer[2])\n\n        if len(self.every_timers) > 0:\n            self.logger.info(f\"Started {len(self.every_timers)} timer(s)\")\n\n        if len(self.event_timers) > 0:\n            self.logger.info(f\"Started {len(self.event_timers)} event(s)\")\n\n    def _start_every_timer(self, idx: int, frequency_in_seconds: int, callback):\n        def setup_timer(delay):\n            timer = threading.Timer(delay, func)\n            timer.daemon = True\n\n            self._start_thread_safely(timer)\n\n        def func():\n            try:\n                if not self.terminated_internally and not self.terminated_externally and not self.fatal_termination:\n                    def on_start():\n                        self.logger.debug(f\"Processing the timer #{idx}\")\n\n                    def on_finish():\n                        self.logger.debug(f\"Finished processing the timer #{idx}\")\n\n                    if not callback.trigger(on_start, on_finish):\n                        self.logger.debug(f\"Ignoring timer #{idx} as previous one is already running\")\n                else:\n                    self.logger.debug(f\"Ignoring timer #{idx} as keeper is already terminating\")\n            except:\n                setup_timer(frequency_in_seconds)\n                raise\n            setup_timer(frequency_in_seconds)\n\n        setup_timer(1)\n        self._at_least_one_every = True\n\n    def _start_event_timer(self, idx: int, event: threading.Event, min_frequency_in_seconds: int, callback):\n        def setup_thread():\n            self._start_thread_safely(threading.Thread(target=func, daemon=True))\n\n        def func():\n            event_happened = False\n\n            while True:\n                try:\n                    if not self.terminated_internally and not self.terminated_externally and not self.fatal_termination:\n                        def on_start():\n                            self.logger.debug(f\"Processing the event #{idx}\" if event_happened\n                                              else f\"Processing the event #{idx} because of minimum frequency\")\n\n                        def on_finish():\n                            self.logger.debug(f\"Finished processing the event #{idx}\" if event_happened\n                                              else f\"Finished processing the event #{idx} because of minimum frequency\")\n\n                        assert callback.trigger(on_start, on_finish)\n                        callback.wait()\n\n                    else:\n                        self.logger.debug(f\"Ignoring event #{idx} as keeper is terminating\" if event_happened\n                                          else f\"Ignoring event #{idx} because of minimum frequency as keeper is terminating\")\n                except:\n                    setup_thread()\n                    raise\n\n                event_happened = event.wait(timeout=min_frequency_in_seconds)\n                event.clear()\n\n        setup_thread()\n        self._at_least_one_every = True\n\n    def _main_loop(self):\n        # terminate gracefully on either SIGINT or SIGTERM\n        signal.signal(signal.SIGINT, self._sigint_sigterm_handler)\n        signal.signal(signal.SIGTERM, self._sigint_sigterm_handler)\n\n        # in case at least one filter has been set up, we enter an infinite loop and let\n        # the callbacks do the job. in case of no filters, we will not enter this loop\n        # and the keeper will terminate soon after it started\n        while any_filter_thread_present() or self._at_least_one_every:\n            time.sleep(1)\n\n            # if the keeper logic asked us to terminate, we do so\n            if self.terminated_internally:\n                self.logger.warning(\"Keeper logic asked for termination, the keeper will terminate\")\n                break\n\n            # if SIGINT/SIGTERM asked us to terminate, we do so\n            if self.terminated_externally:\n                self.logger.warning(\"The keeper is terminating due do SIGINT/SIGTERM signal received\")\n                break\n\n            # if any exception is raised in filter handling thread (could be an HTTP exception\n            # while communicating with the node), web3.py does not retry and the filter becomes\n            # dysfunctional i.e. no new callbacks will ever be fired. we detect it and terminate\n            # the keeper so it can be restarted.\n            if not all_filter_threads_alive():\n                self.logger.fatal(\"One of filter threads is dead, the keeper will terminate\")\n                self.fatal_termination = True\n                break\n\n            # if we are watching for new blocks and no new block has been reported during\n            # some time, we assume the watching filter died and terminate the keeper\n            # so it can be restarted.\n            #\n            # this used to happen when the machine that has the node and the keeper running\n            # was put to sleep and then woken up.\n            #\n            # TODO the same thing could possibly happen if we watch any event other than\n            # TODO a new block. if that happens, we have no reliable way of detecting it now.\n            if self._last_block_time and (datetime.datetime.now(tz=pytz.UTC) - self._last_block_time).total_seconds() > 300:\n                if not self.web3.eth.syncing:\n                    self.logger.fatal(\"No new blocks received for 300 seconds, the keeper will terminate\")\n                    self.fatal_termination = True\n                    break\n"
  },
  {
    "path": "pymaker/logging.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2019 EdNoepel\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nimport logging\nfrom pprint import pformat\nfrom web3 import Web3\nfrom web3._utils.events import get_event_data\n\nfrom eth_abi.codec import ABICodec\nfrom eth_abi.registry import registry as default_registry\n\n# Shared between DSNote and many MCD contracts\nclass LogNote:\n    def __init__(self, log):\n        args = log['args']\n        self.sig = Web3.toHex(args['sig'])\n        self.usr = args['usr'] if 'usr' in args else None     # vat.frob doesn't offer `usr`\n        self.arg1 = args['arg1'] if 'arg1' in args else None\n        self.arg2 = args['arg2'] if 'arg2' in args else None\n        self.arg3 = args['arg3'] if 'arg3' in args else None  # Special variant used for vat.frob\n        self.block = log['blockNumber']\n        self.tx_hash = log['transactionHash'].hex()\n        self._data = args['data']\n\n    @classmethod\n    def from_event(cls, event: dict, contract_abi: list):\n        assert isinstance(event, dict)\n        assert isinstance(contract_abi, list)\n\n        log_note_abi = [abi for abi in contract_abi if abi.get('name') == 'LogNote'][0]\n        try:\n            codec = ABICodec(default_registry)\n            event_data = get_event_data(codec, log_note_abi, event)\n            return LogNote(event_data)\n        except ValueError:\n            # event is not a LogNote\n            return None\n\n    def get_bytes_at_index(self, index: int) -> bytes:\n        assert isinstance(index, int)\n        if index > 5:\n            raise ValueError(\"Only six words of calldata are provided\")\n\n        start_index = len(self._data) - ((6-index) * 32) - 28\n        return self._data[start_index:start_index+32]\n\n    def __eq__(self, other):\n        assert isinstance(other, LogNote)\n        return self.__dict__ == other.__dict__\n\n    def __repr__(self):\n        return f\"LogNote({pformat(vars(self))})\"\n"
  },
  {
    "path": "pymaker/model.py",
    "content": "# This file is part of Maker Keeper Framework.\n# \n# Copyright (C) 2017-2020 mitakash, MikeHathaway\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nfrom pprint import pformat\nfrom typing import Optional, List\n\nfrom pymaker import Address\nfrom pymaker.numeric import Wad\n\n\nclass Token:\n    def __init__(self, name: str, address: Optional[Address], decimals: int):\n        assert(isinstance(name, str))\n        assert(isinstance(address, Address) or (address is None))\n        assert(isinstance(decimals, int))\n\n        self.name = name\n        self.address = address\n        self.decimals = decimals\n\n        self.min_amount = Wad.from_number(10 ** -self.decimals)\n\n    def normalize_amount(self, amount: Wad) -> Wad:\n        assert(isinstance(amount, Wad))\n\n        return amount * Wad.from_number(10 ** (18 - self.decimals))\n\n    def unnormalize_amount(self, amount: Wad) -> Wad:\n        assert(isinstance(amount, Wad))\n\n        return amount * Wad.from_number(10 ** (self.decimals - 18))\n\n    def is_eth(self) -> bool:\n        return self.address == Address('0x0000000000000000000000000000000000000000')\n\n    def __eq__(self, other):\n        assert(isinstance(other, Token))\n        return self.name == other.name and \\\n               self.address == other.address and \\\n               self.decimals == other.decimals\n\n    def __hash__(self):\n        return hash((self.name, self.address, self.decimals))\n\n    def __str__(self):\n        return self.name\n\n    def __repr__(self):\n        return pformat(vars(self))\n\n\nclass TokenConfig:\n    def __init__(self, data: dict):\n        assert (isinstance(data, dict))\n\n        self.token_list = []\n        self.token_config = data['tokens']\n\n    def set_token_list(self, data):\n        assert (isinstance(data, dict))\n\n        self.token_list = [Token(name=key,\n                             address=Address(value['tokenAddress']) if 'tokenAddress' in value else None,\n                             decimals=value['tokenDecimals'] if 'tokenDecimals' in value else 18) for key, value in\n                       data['tokens'].items()]\n\n    def get_token_list(self) -> List[Token]:\n        return self.token_list\n\n    def __repr__(self):\n        return pformat(vars(self))\n"
  },
  {
    "path": "pymaker/numeric.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2017-2018 reverendus\n# Copyright (C) 2018 bargst\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nimport math\nfrom functools import total_ordering, reduce\nfrom decimal import *\n\n\n_context = Context(prec=1000, rounding=ROUND_DOWN)\n\n\n@total_ordering\nclass Wad:\n    \"\"\"Represents a number with 18 decimal places.\n\n    `Wad` implements comparison, addition, subtraction, multiplication and division operators. Comparison, addition,\n    subtraction and division only work with other instances of `Wad`. Multiplication works with instances\n    of `Wad` and `Ray` and also with `int` numbers. The result of multiplication is always a `Wad`.\n\n    `Wad`, along with `Ray`, are the two basic numeric types used by Maker contracts.\n\n    Notes:\n        The internal representation of `Wad` is an unbounded integer, the last 18 digits of it being treated\n        as decimal places. It is similar to the representation used in Maker contracts (`uint128`).\n    \"\"\"\n\n    def __init__(self, value):\n        \"\"\"Creates a new Wad number.\n\n        Args:\n            value: an instance of `Wad`, `Ray` or an integer. In case of an integer, the internal representation\n                of Maker contracts is used which means that passing `1` will create an instance of `Wad`\n                with a value of `0.000000000000000001'.\n        \"\"\"\n        if isinstance(value, Wad):\n            self.value = value.value\n        elif isinstance(value, Ray):\n            self.value = int((Decimal(value.value) // (Decimal(10)**Decimal(9))).quantize(1, context=_context))\n        elif isinstance(value, Rad):\n            self.value = int((Decimal(value.value) // (Decimal(10)**Decimal(27))).quantize(1, context=_context))\n        elif isinstance(value, int):\n            # assert(value >= 0)\n            self.value = value\n        else:\n            raise ArithmeticError\n\n    @classmethod\n    def from_number(cls, number):\n        # assert(number >= 0)\n        pwr = Decimal(10) ** 18\n        dec = Decimal(str(number)) * pwr\n        return Wad(int(dec.quantize(1, context=_context)))\n\n    def __repr__(self):\n        return \"Wad(\" + str(self.value) + \")\"\n\n    def __str__(self):\n        tmp = str(self.value).zfill(19)\n        return (tmp[0:len(tmp)-18] + \".\" + tmp[len(tmp)-18:len(tmp)]).replace(\"-.\", \"-0.\")\n\n    def __add__(self, other):\n        if isinstance(other, Wad):\n            return Wad(self.value + other.value)\n        else:\n            raise ArithmeticError\n\n    def __sub__(self, other):\n        if isinstance(other, Wad):\n            return Wad(self.value - other.value)\n        else:\n            raise ArithmeticError\n\n    def __mod__(self, other):\n        if isinstance(other, Wad):\n            return Wad(self.value % other.value)\n        else:\n            raise ArithmeticError\n    \n    # z = cast((uint256(x) * y + WAD / 2) / WAD);\n    def __mul__(self, other):\n        if isinstance(other, Wad):\n            result = Decimal(self.value) * Decimal(other.value) / (Decimal(10) ** Decimal(18))\n            return Wad(int(result.quantize(1, context=_context)))\n        elif isinstance(other, Ray):\n            result = Decimal(self.value) * Decimal(other.value) / (Decimal(10) ** Decimal(27))\n            return Wad(int(result.quantize(1, context=_context)))\n        elif isinstance(other, Rad):\n            result = Decimal(self.value) * Decimal(other.value) / (Decimal(10) ** Decimal(45))\n            return Wad(int(result.quantize(1, context=_context)))\n        elif isinstance(other, int):\n            return Wad(int((Decimal(self.value) * Decimal(other)).quantize(1, context=_context)))\n        else:\n            raise ArithmeticError\n\n    def __truediv__(self, other):\n        if isinstance(other, Wad):\n            return Wad(int((Decimal(self.value) * (Decimal(10) ** Decimal(18)) / Decimal(other.value)).quantize(1, context=_context)))\n        else:\n            raise ArithmeticError\n\n    def __abs__(self):\n        return Wad(abs(self.value))\n\n    def __eq__(self, other):\n        if isinstance(other, Wad):\n            return self.value == other.value\n        else:\n            raise ArithmeticError\n\n    def __hash__(self):\n        return hash(self.value)\n\n    def __lt__(self, other):\n        if isinstance(other, Wad):\n            return self.value < other.value\n        else:\n            raise ArithmeticError\n\n    def __int__(self):\n        return int(self.value / 10**18)\n\n    def __float__(self):\n        return self.value / 10**18\n\n    def __round__(self, ndigits: int = 0):\n        return Wad(round(self.value, -18 + ndigits))\n\n    def __sqrt__(self):\n        return Wad.from_number(math.sqrt(self.__float__()))\n\n    @staticmethod\n    def min(*args):\n        \"\"\"Returns the lower of the Wad values\"\"\"\n        return reduce(lambda x, y: x if x < y else y, args[1:], args[0])\n\n    @staticmethod\n    def max(*args):\n        \"\"\"Returns the higher of the Wad values\"\"\"\n        return reduce(lambda x, y: x if x > y else y, args[1:], args[0])\n\n\n@total_ordering\nclass Ray:\n    \"\"\"Represents a number with 27 decimal places.\n\n    `Ray` implements comparison, addition, subtraction, multiplication and division operators. Comparison, addition,\n    subtraction and division only work with other instances of `Ray`. Multiplication works with instances\n    of `Ray` and `Wad` and also with `int` numbers. The result of multiplication is always a `Ray`.\n\n    `Ray`, along with `Wad`, are the two basic numeric types used by Maker contracts.\n\n    Notes:\n        The internal representation of `Ray` is an unbounded integer, the last 27 digits of it being treated\n        as decimal places. It is similar to the representation used in Maker contracts (`uint128`).\n    \"\"\"\n\n    def __init__(self, value):\n        \"\"\"Creates a new Ray number.\n\n        Args:\n            value: an instance of `Ray`, `Wad` or an integer. In case of an integer, the internal representation\n                of Maker contracts is used which means that passing `1` will create an instance of `Ray`\n                with a value of `0.000000000000000000000000001'.\n        \"\"\"\n        if isinstance(value, Ray):\n            self.value = value.value\n        elif isinstance(value, Wad):\n            self.value = int((Decimal(value.value) * (Decimal(10)**Decimal(9))).quantize(1, context=_context))\n        elif isinstance(value, Rad):\n            self.value = int((Decimal(value.value) / (Decimal(10)**Decimal(18))).quantize(1, context=_context))\n        elif isinstance(value, int):\n            # assert(value >= 0)\n            self.value = value\n        else:\n            raise ArithmeticError\n\n    @classmethod\n    def from_number(cls, number):\n        # assert(number >= 0)\n        pwr = Decimal(10) ** 27\n        dec = Decimal(str(number)) * pwr\n        return Ray(int(dec.quantize(1, context=_context)))\n\n    def __repr__(self):\n        return \"Ray(\" + str(self.value) + \")\"\n\n    def __str__(self):\n        tmp = str(self.value).zfill(28)\n        return (tmp[0:len(tmp)-27] + \".\" + tmp[len(tmp)-27:len(tmp)]).replace(\"-.\", \"-0.\")\n\n    def __add__(self, other):\n        if isinstance(other, Ray):\n            return Ray(self.value + other.value)\n        else:\n            raise ArithmeticError\n\n    def __sub__(self, other):\n        if isinstance(other, Ray):\n            return Ray(self.value - other.value)\n        else:\n            raise ArithmeticError\n    \n    def __mod__(self, other):\n        if isinstance(other, Ray):\n            return Ray(self.value % other.value)\n        else:\n            raise ArithmeticError\n\n    def __mul__(self, other):\n        if isinstance(other, Ray):\n            result = Decimal(self.value) * Decimal(other.value) / (Decimal(10) ** Decimal(27))\n            return Ray(int(result.quantize(1, context=_context)))\n        elif isinstance(other, Wad):\n            result = Decimal(self.value) * Decimal(other.value) / (Decimal(10) ** Decimal(18))\n            return Ray(int(result.quantize(1, context=_context)))\n        elif isinstance(other, Rad):\n            result = Decimal(self.value) * Decimal(other.value) / (Decimal(10) ** Decimal(45))\n            return Ray(int(result.quantize(1, context=_context)))\n        elif isinstance(other, int):\n            return Ray(int((Decimal(self.value) * Decimal(other)).quantize(1, context=_context)))\n        else:\n            raise ArithmeticError\n\n    def __truediv__(self, other):\n        if isinstance(other, Ray):\n            return Ray(int((Decimal(self.value) * (Decimal(10) ** Decimal(27)) / Decimal(other.value)).quantize(1, context=_context)))\n        else:\n            raise ArithmeticError\n\n    def __abs__(self):\n        return Ray(abs(self.value))\n\n    def __eq__(self, other):\n        if isinstance(other, Ray):\n            return self.value == other.value\n        else:\n            raise ArithmeticError\n\n    def __hash__(self):\n        return hash(self.value)\n\n    def __lt__(self, other):\n        if isinstance(other, Ray):\n            return self.value < other.value\n        else:\n            raise ArithmeticError\n\n    def __int__(self):\n        return int(self.value / 10**27)\n\n    def __float__(self):\n        return self.value / 10**27\n\n    def __round__(self, ndigits: int = 0):\n        return Ray(round(self.value, -27 + ndigits))\n\n    def __sqrt__(self):\n        return Ray.from_number(math.sqrt(self.__float__()))\n\n    @staticmethod\n    def min(*args):\n        \"\"\"Returns the lower of the Ray values\"\"\"\n        return reduce(lambda x, y: x if x < y else y, args[1:], args[0])\n\n    @staticmethod\n    def max(*args):\n        \"\"\"Returns the higher of the Ray values\"\"\"\n        return reduce(lambda x, y: x if x > y else y, args[1:], args[0])\n\n\n@total_ordering\nclass Rad:\n    \"\"\"Represents a number with 45 decimal places.\n\n    `Rad` implements comparison, addition, subtraction, multiplication and division operators. Comparison, addition,\n    subtraction and division only work with other instances of `Rad`. Multiplication works with instances\n    of `Rad`, `Ray and `Wad` and also with `int` numbers. The result of multiplication is always a `Rad`.\n\n    `Rad` is rad is a new unit that exists to prevent precision loss in the core CDP engine of MCD.\n\n    Notes:\n        The internal representation of `Rad` is an unbounded integer, the last 45 digits of it being treated\n        as decimal places.\n    \"\"\"\n\n    def __init__(self, value):\n        \"\"\"Creates a new Rad number.\n\n        Args:\n            value: an instance of `Rad`, `Ray`, `Wad` or an integer. In case of an integer, the internal representation\n                of Maker contracts is used which means that passing `1` will create an instance of `Rad`\n                with a value of `0.000000000000000000000000000000000000000000001'.\n        \"\"\"\n        if isinstance(value, Rad):\n            self.value = value.value\n        elif isinstance(value, Ray):\n            self.value = int((Decimal(value.value) * (Decimal(10)**Decimal(18))).quantize(1, context=_context))\n        elif isinstance(value, Wad):\n            self.value = int((Decimal(value.value) * (Decimal(10)**Decimal(27))).quantize(1, context=_context))\n        elif isinstance(value, int):\n            # assert(value >= 0)\n            self.value = value\n        else:\n            raise ArithmeticError\n\n    @classmethod\n    def from_number(cls, number):\n        # assert(number >= 0)\n        pwr = Decimal(10) ** 45\n        dec = Decimal(str(number)) * pwr\n        return Rad(int(dec.quantize(1, context=_context)))\n\n    def __repr__(self):\n        return \"Rad(\" + str(self.value) + \")\"\n\n    def __str__(self):\n        tmp = str(self.value).zfill(46)\n        return (tmp[0:len(tmp)-45] + \".\" + tmp[len(tmp)-45:len(tmp)]).replace(\"-.\", \"-0.\")\n\n    def __add__(self, other):\n        if isinstance(other, Rad):\n            return Rad(self.value + other.value)\n        else:\n            raise ArithmeticError\n\n    def __sub__(self, other):\n        if isinstance(other, Rad):\n            return Rad(self.value - other.value)\n        else:\n            raise ArithmeticError\n\n    def __mod__(self, other):\n        if isinstance(other, Rad):\n            return Rad(self.value % other.value)\n        else:\n            raise ArithmeticError\n\n    def __mul__(self, other):\n        if isinstance(other, Rad):\n            result = Decimal(self.value) * Decimal(other.value) / (Decimal(10) ** Decimal(45))\n            return Rad(int(result.quantize(1, context=_context)))\n        elif isinstance(other, Ray):\n            result = Decimal(self.value) * Decimal(other.value) / (Decimal(10) ** Decimal(27))\n            return Rad(int(result.quantize(1, context=_context)))\n        elif isinstance(other, Wad):\n            result = Decimal(self.value) * Decimal(other.value) / (Decimal(10) ** Decimal(18))\n            return Rad(int(result.quantize(1, context=_context)))\n        elif isinstance(other, int):\n            return Rad(int((Decimal(self.value) * Decimal(other)).quantize(1, context=_context)))\n        else:\n            raise ArithmeticError\n\n    def __truediv__(self, other):\n        if isinstance(other, Rad):\n            return Rad(int((Decimal(self.value) * (Decimal(10) ** Decimal(45)) / Decimal(other.value)).quantize(1, context=_context)))\n        else:\n            raise ArithmeticError\n\n    def __abs__(self):\n        return Rad(abs(self.value))\n\n    def __eq__(self, other):\n        if isinstance(other, Rad):\n            return self.value == other.value\n        else:\n            raise ArithmeticError\n\n    def __hash__(self):\n        return hash(self.value)\n\n    def __lt__(self, other):\n        if isinstance(other, Rad):\n            return self.value < other.value\n        else:\n            raise ArithmeticError\n\n    def __int__(self):\n        return int(self.value / 10**45)\n\n    def __float__(self):\n        return self.value / 10**45\n\n    def __round__(self, ndigits: int = 0):\n        return Rad(round(self.value, -45 + ndigits))\n\n    def __sqrt__(self):\n        return Rad.from_number(math.sqrt(self.__float__()))\n\n    @staticmethod\n    def min(*args):\n        \"\"\"Returns the lower of the Rad values\"\"\"\n        return reduce(lambda x, y: x if x < y else y, args[1:], args[0])\n\n    @staticmethod\n    def max(*args):\n        \"\"\"Returns the higher of the Rad values\"\"\"\n        return reduce(lambda x, y: x if x > y else y, args[1:], args[0])\n"
  },
  {
    "path": "pymaker/oasis.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2017-2018 reverendus\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nfrom pprint import pformat\nfrom typing import Optional, List, Iterable, Iterator\n\nfrom hexbytes import HexBytes\nfrom web3 import Web3\nfrom web3._utils.events import get_event_data\n\nfrom eth_abi.codec import ABICodec\nfrom eth_abi.registry import registry as default_registry\n\nfrom pymaker import Contract, Address, Transact, Receipt\nfrom pymaker.numeric import Wad\nfrom pymaker.token import ERC20Token\nfrom pymaker.util import int_to_bytes32, bytes_to_int\nfrom pymaker.model import Token\n\nclass Order:\n    \"\"\"Represents a single order on `OasisDEX`.\n\n    Instances of this class shouldn't be created directly. Instead of that, new orders can be queried\n    using methods of :py:class:`pymaker.oasis.SimpleMarket` or :py:class:`pymaker.oasis.MatchingMarket`.\n\n    Attributes:\n        order_id: Id of the order.\n        maker: Ethereum address of the owner of this order.\n        pay_token: The address of the token which is put on sale.\n        pay_amount: The amount of the `pay_token` token which is put on sale.\n        buy_token: The address of the token the order creator wants to be paid with.\n        buy_amount: The price the order creator wants to be paid, denominated in the `buy_token` token.\n        timestamp: Date and time when this order has been created, as a unix timestamp.\n    \"\"\"\n\n    def __init__(self, market, order_id: int, maker: Address, pay_token: Address, pay_amount: Wad, buy_token: Address,\n                buy_amount: Wad, timestamp: int):\n        assert(isinstance(order_id, int))\n        assert(isinstance(maker, Address))\n        assert(isinstance(pay_token, Address))\n        assert(isinstance(pay_amount, Wad))\n        assert(isinstance(buy_token, Address))\n        assert(isinstance(buy_amount, Wad))\n        assert(isinstance(timestamp, int))\n\n        self._market = market\n        self.order_id = order_id\n        self.maker = maker\n        self.pay_token = pay_token\n        self.pay_amount = pay_amount\n        self.buy_token = buy_token\n        self.buy_amount = buy_amount\n        self.timestamp = timestamp\n\n    @property\n    def sell_to_buy_price(self) -> Wad:\n        return self.pay_amount / self.buy_amount\n\n    @property\n    def buy_to_sell_price(self) -> Wad:\n        return self.buy_amount / self.pay_amount\n\n    @property\n    def remaining_buy_amount(self) -> Wad:\n        return self.buy_amount\n\n    @property\n    def remaining_sell_amount(self) -> Wad:\n        return self.pay_amount\n\n    def __eq__(self, other):\n        assert(isinstance(other, Order))\n        return self._market.address == other._market.address and self.order_id == other.order_id\n\n    def __hash__(self):\n        return self.order_id\n\n    def __repr__(self):\n        return pformat(vars(self))\n\n\nclass LogMake:\n    def __init__(self, log):\n        self.order_id = bytes_to_int(log['args']['id'])\n        self.maker = Address(log['args']['maker'])\n        self.pay_token = Address(log['args']['pay_gem'])\n        self.pay_amount = Wad(log['args']['pay_amt'])\n        self.buy_token = Address(log['args']['buy_gem'])\n        self.buy_amount = Wad(log['args']['buy_amt'])\n        self.timestamp = log['args']['timestamp']\n        self.raw = log\n\n    @classmethod\n    def from_receipt(cls, receipt: Receipt):\n        assert(isinstance(receipt, Receipt))\n\n        if receipt.logs is not None:\n            for log in receipt.logs:\n                if len(log['topics']) > 0 and log['topics'][0] == HexBytes('0x773ff502687307abfa024ac9f62f9752a0d210dac2ffd9a29e38e12e2ea82c82'):\n                    log_make_abi = [abi for abi in SimpleMarket.abi if abi.get('name') == 'LogMake'][0]\n                    codec = ABICodec(default_registry)\n                    event_data = get_event_data(codec, log_make_abi, log)\n\n                    yield LogMake(event_data)\n\n    def __repr__(self):\n        return pformat(vars(self))\n\n\nclass LogBump:\n    def __init__(self, log):\n        self.order_id = bytes_to_int(log['args']['id'])\n        self.maker = Address(log['args']['maker'])\n        self.pay_token = Address(log['args']['pay_gem'])\n        self.pay_amount = Wad(log['args']['pay_amt'])\n        self.buy_token = Address(log['args']['buy_gem'])\n        self.buy_amount = Wad(log['args']['buy_amt'])\n        self.timestamp = log['args']['timestamp']\n        self.raw = log\n\n    def __repr__(self):\n        return pformat(vars(self))\n\n\nclass LogTake:\n    def __init__(self, log):\n        self.order_id = bytes_to_int(log['args']['id'])\n        self.maker = Address(log['args']['maker'])\n        self.taker = Address(log['args']['taker'])\n        self.pay_token = Address(log['args']['pay_gem'])\n        self.take_amount = Wad(log['args']['take_amt'])\n        self.buy_token = Address(log['args']['buy_gem'])\n        self.give_amount = Wad(log['args']['give_amt'])\n        self.timestamp = log['args']['timestamp']\n        self.raw = log\n\n    @classmethod\n    def from_event(cls, event: dict):\n        assert(isinstance(event, dict))\n\n        topics = event.get('topics')\n        if topics and topics[0] == HexBytes('0x3383e3357c77fd2e3a4b30deea81179bc70a795d053d14d5b7f2f01d0fd4596f'):\n            log_take_abi = [abi for abi in SimpleMarket.abi if abi.get('name') == 'LogTake'][0]\n            codec = ABICodec(default_registry)\n            event_data = get_event_data(codec, log_take_abi, event)\n\n            return LogTake(event_data)\n\n    def __eq__(self, other):\n        assert(isinstance(other, LogTake))\n        return self.__dict__ == other.__dict__\n\n    def __repr__(self):\n        return pformat(vars(self))\n\n\nclass LogKill:\n    def __init__(self, log):\n        self.order_id = bytes_to_int(log['args']['id'])\n        self.maker = Address(log['args']['maker'])\n        self.pay_token = Address(log['args']['pay_gem'])\n        self.pay_amount = Wad(log['args']['pay_amt'])\n        self.buy_token = Address(log['args']['buy_gem'])\n        self.buy_amount = Wad(log['args']['buy_amt'])\n        self.timestamp = log['args']['timestamp']\n        self.raw = log\n\n    def __repr__(self):\n        return pformat(vars(self))\n\n\nclass SimpleMarket(Contract):\n    \"\"\"A client for a `SimpleMarket` contract.\n\n    `SimpleMarket` is a simple on-chain OTC market for ERC20-compatible tokens.\n    It powers the `OasisDEX` decentralized exchange.\n\n    You can find the source code of the `OasisDEX` contracts here:\n    <https://github.com/makerdao/maker-otc>.\n\n    Attributes:\n        web3: An instance of `Web` from `web3.py`.\n        address: Ethereum address of the `SimpleMarket` contract.\n    \"\"\"\n\n    abi = Contract._load_abi(__name__, 'abi/SimpleMarket.abi')\n    bin = Contract._load_bin(__name__, 'abi/SimpleMarket.bin')\n\n    def __init__(self, web3: Web3, address: Address):\n        assert(isinstance(web3, Web3))\n        assert(isinstance(address, Address))\n\n        self.web3 = web3\n        self.address = address\n        self._contract = self._get_contract(web3, self.abi, address)\n\n    @staticmethod\n    def deploy(web3: Web3):\n        \"\"\"Deploy a new instance of the `SimpleMarket` contract.\n\n        Args:\n            web3: An instance of `Web3` from `web3.py`.\n\n        Returns:\n            A `SimpleMarket` class instance.\n        \"\"\"\n        return SimpleMarket(web3=web3, address=Contract._deploy(web3, SimpleMarket.abi, SimpleMarket.bin, []))\n\n    def approve(self, tokens: List[ERC20Token], approval_function):\n        \"\"\"Approve the OasisDEX contract to fully access balances of specified tokens.\n\n        For available approval functions (i.e. approval modes) see `directly` and `via_tx_manager`\n        in `pymaker.approval`.\n\n        Args:\n            tokens: List of :py:class:`pymaker.token.ERC20Token` class instances.\n            approval_function: Approval function (i.e. approval mode).\n        \"\"\"\n        assert(isinstance(tokens, list))\n        assert(callable(approval_function))\n\n        for token in tokens:\n            approval_function(token, self.address, 'OasisDEX')\n\n    def past_make(self, number_of_past_blocks: int, event_filter: dict = None) -> List[LogMake]:\n        \"\"\"Synchronously retrieve past LogMake events.\n\n        `LogMake` events are emitted by the Oasis contract every time someone places an order.\n\n        Args:\n            number_of_past_blocks: Number of past Ethereum blocks to retrieve the events from.\n            event_filter: Filter which will be applied to returned events.\n\n        Returns:\n            List of past `LogMake` events represented as :py:class:`pymaker.oasis.LogMake` class.\n        \"\"\"\n        assert(isinstance(number_of_past_blocks, int))\n        assert(isinstance(event_filter, dict) or (event_filter is None))\n\n        return self._past_events(self._contract, 'LogMake', LogMake, number_of_past_blocks, event_filter)\n\n    def past_bump(self, number_of_past_blocks: int, event_filter: dict = None) -> List[LogBump]:\n        \"\"\"Synchronously retrieve past LogBump events.\n\n        `LogBump` events are emitted by the Oasis contract every time someone calls the `bump()` function.\n\n        Args:\n            number_of_past_blocks: Number of past Ethereum blocks to retrieve the events from.\n            event_filter: Filter which will be applied to returned events.\n\n        Returns:\n            List of past `LogBump` events represented as :py:class:`pymaker.oasis.LogBump` class.\n        \"\"\"\n        assert(isinstance(number_of_past_blocks, int))\n        assert(isinstance(event_filter, dict) or (event_filter is None))\n\n        return self._past_events(self._contract, 'LogBump', LogBump, number_of_past_blocks, event_filter)\n\n    def past_take(self, number_of_past_blocks: int, event_filter: dict = None) -> List[LogTake]:\n        \"\"\"Synchronously retrieve past LogTake events.\n\n        `LogTake` events are emitted by the Oasis contract every time someone takes an order.\n\n        Args:\n            number_of_past_blocks: Number of past Ethereum blocks to retrieve the events from.\n            event_filter: Filter which will be applied to returned events.\n\n        Returns:\n            List of past `LogTake` events represented as :py:class:`pymaker.oasis.LogTake` class.\n        \"\"\"\n        assert(isinstance(number_of_past_blocks, int))\n        assert(isinstance(event_filter, dict) or (event_filter is None))\n\n        return self._past_events(self._contract, 'LogTake', LogTake, number_of_past_blocks, event_filter)\n\n    def past_kill(self, number_of_past_blocks: int, event_filter: dict = None) -> List[LogKill]:\n        \"\"\"Synchronously retrieve past LogKill events.\n\n        `LogKill` events are emitted by the Oasis contract every time someone cancels an order.\n\n        Args:\n            number_of_past_blocks: Number of past Ethereum blocks to retrieve the events from.\n            event_filter: Filter which will be applied to returned events.\n\n        Returns:\n            List of past `LogKill` events represented as :py:class:`pymaker.oasis.LogKill` class.\n        \"\"\"\n        assert(isinstance(number_of_past_blocks, int))\n        assert(isinstance(event_filter, dict) or (event_filter is None))\n\n        return self._past_events(self._contract, 'LogKill', LogKill, number_of_past_blocks, event_filter)\n\n    def get_last_order_id(self) -> int:\n        \"\"\"Get the id of the last order created on the market.\n\n        Returns:\n            The id of the last order. Returns `0` if no orders have been created at all.\n        \"\"\"\n        return self._contract.functions.last_offer_id().call()\n\n    def get_order(self, order_id: int, block_ident: Optional[str] = None) -> Optional[Order]:\n        \"\"\"Get order details.\n\n        Args:\n            order_id: The id of the order to get the details of.\n            `block_ident`: Block identifier can either be a str, int or None and will fallback to 'latest' block.\n                           Used for historical retreival of orders.\n\n        Returns:\n            An instance of `Order` if the order is still active, or `None` if the order has been\n            either already completely taken or cancelled.\n        \"\"\"\n        assert(isinstance(order_id, int))\n\n        assert(isinstance(block_ident, str) or isinstance(block_ident, int) or (block_ident is None))\n        block_ident = 'latest' if block_ident is None else block_ident\n\n        array = self._contract.functions.offers(order_id).call(block_identifier=block_ident)\n        if array[5] == 0:\n            return None\n        else:\n            return Order(market=self, order_id=order_id, maker=Address(array[4]), pay_token=Address(array[1]),\n                         pay_amount=Wad(array[0]), buy_token=Address(array[3]), buy_amount=Wad(array[2]),\n                         timestamp=array[5])\n\n    def get_orders(self, pay_token: Address = None, buy_token: Address = None) -> List[Order]:\n        \"\"\"Get all active orders.\n\n        If both `pay_token` and `buy_token` are specified, orders will be filtered by these.\n        Either none or both of these parameters have to be specified.\n\n        Args:\n            `pay_token`: Address of the `pay_token` to filter the orders by.\n            `buy_token`: Address of the `buy_token` to filter the orders by.\n\n        Returns:\n            A list of `Order` objects representing all active orders on Oasis.\n        \"\"\"\n        assert((isinstance(pay_token, Address) and isinstance(buy_token, Address))\n               or (pay_token is None and buy_token is None))\n\n        orders = [self.get_order(order_id + 1) for order_id in range(self.get_last_order_id())]\n        orders = [order for order in orders if order is not None]\n\n        if pay_token is not None and buy_token is not None:\n            orders = list(filter(lambda order: order.pay_token == pay_token and order.buy_token == buy_token, orders))\n\n        return orders\n\n    def get_orders_by_maker(self, maker: Address) -> List[Order]:\n        \"\"\"Get all active orders created by `maker`.\n\n        Args:\n            maker: Address of the `maker` to filter the orders by.\n\n        Returns:\n            A list of `Order` objects representing all active orders belonging to this `maker`.\n        \"\"\"\n        assert(isinstance(maker, Address))\n\n        result = []\n        for order_id in range(self.get_last_order_id()):\n            # Query the order.\n            order = self.get_order(order_id + 1)\n            if order is None:\n                continue\n\n            # We are only interested in orders owned by `maker`. In case the order is not owned by `maker`,\n            # we add it to `_alien_orders[maker]` so the next time `get_orders_by_maker()` is called\n            # with the same parameter we will be able to rule out these orders straight away.\n            if order.maker != maker:\n                continue\n\n            result.append(order)\n\n        return result\n\n    def make(self, pay_token: Address, pay_amount: Wad, buy_token: Address, buy_amount: Wad) -> Transact:\n        \"\"\"Create a new order.\n\n        The `pay_amount` of `pay_token` token will be taken from you on order creation and deposited\n        in the market contract. Allowance needs to be set first - refer to the `approve()` method.\n\n        When complete, `receipt.result` will contain order_id of the new order.\n\n        Args:\n            pay_token: Address of the ERC20 token you want to put on sale.\n            pay_amount: Amount of the `pay_token` token you want to put on sale.\n            buy_token: Address of the ERC20 token you want to be paid with.\n            buy_amount: Amount of the `buy_token` you want to receive.\n\n        Returns:\n            A :py:class:`pymaker.Transact` instance, which can be used to trigger the transaction.\n        \"\"\"\n        assert(isinstance(pay_token, Address))\n        assert(isinstance(pay_amount, Wad))\n        assert(isinstance(buy_token, Address))\n        assert(isinstance(buy_amount, Wad))\n        assert(pay_amount > Wad(0))\n        assert(buy_amount > Wad(0))\n\n        return Transact(self, self.web3, self.abi, self.address, self._contract,\n                        'make', [pay_token.address, buy_token.address, pay_amount.value, buy_amount.value], None,\n                        self._make_order_id_result_function)\n\n    def bump(self, order_id: int) -> Transact:\n        \"\"\"Bumps an order.\n\n        Bumping an order generates a `LogBump` event, which can make the order reappear\n        in some front-ends relying on the events.\n\n        Args:\n            order_id: Id of the order you want to bump.\n\n        Returns:\n            A :py:class:`pymaker.Transact` instance, which can be used to trigger the transaction.\n        \"\"\"\n        assert(isinstance(order_id, int))\n\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'bump',\n                        [int_to_bytes32(order_id)])\n\n    def take(self, order_id: int, quantity: Wad) -> Transact:\n        \"\"\"Takes (buys) an order.\n\n        If `quantity` is equal to `pay_amount`, the whole order will be taken (bought) which will make it\n        disappear from the order book. If you want to buy a fraction of the order, set `quantity` to a number\n        lower than `pay_amount`.\n\n        Args:\n            order_id: Id of the order you want to take (buy).\n            quantity: Quantity of `pay_token` that you want to buy.\n\n        Returns:\n            A :py:class:`pymaker.Transact` instance, which can be used to trigger the transaction.\n        \"\"\"\n        assert(isinstance(order_id, int))\n        assert(isinstance(quantity, Wad))\n\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'take',\n                        [int_to_bytes32(order_id), quantity.value])\n\n    def kill(self, order_id: int) -> Transact:\n        \"\"\"Cancels an existing order.\n\n        Orders can be cancelled only by their owners.\n\n        Args:\n            order_id: Id of the order you want to cancel.\n\n        Returns:\n            A :py:class:`pymaker.Transact` instance, which can be used to trigger the transaction.\n        \"\"\"\n        assert(isinstance(order_id, int))\n\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'kill(bytes32)', [int_to_bytes32(order_id)])\n\n    @staticmethod\n    def _make_order_id_result_function(receipt):\n        return next(map(lambda log_make: log_make.order_id, LogMake.from_receipt(receipt)), None)\n\n    def __repr__(self):\n        return f\"SimpleMarket('{self.address}')\"\n\n\nclass MatchingMarket(SimpleMarket):\n    \"\"\"A client for a `MatchingMarket` contract.\n\n    You can find the source code of the `OasisDEX` contracts here:\n    <https://github.com/makerdao/maker-otc>.\n\n    Attributes:\n        web3: An instance of `Web` from `web3.py`.\n        address: Ethereum address of the `MatchingMarket` contract.\n        support_address: Ethereum address of the `MakerOtcSupportMethods` contract (optional).\n    \"\"\"\n\n    abi = Contract._load_abi(__name__, 'abi/MatchingMarket.abi')\n    bin = Contract._load_bin(__name__, 'abi/MatchingMarket.bin')\n\n    abi_support = Contract._load_abi(__name__, 'abi/MakerOtcSupportMethods.abi')\n\n    def __init__(self, web3: Web3, address: Address, support_address: Optional[Address] = None):\n        assert(isinstance(support_address, Address) or (support_address is None))\n\n        super(MatchingMarket, self).__init__(web3=web3, address=address)\n\n        self.support_address = support_address\n        self._support_contract = self._get_contract(web3, self.abi_support, self.support_address) \\\n            if self.support_address else None\n\n    @staticmethod\n    def deploy(web3: Web3, dust_token: Address, dust_limit: Wad, price_oracle: Address,\n               support_address: Optional[Address] = None):\n        \"\"\"Deploy a new instance of the `MatchingMarket` contract.\n\n        Args:\n            web3: An instance of `Web` from `web3.py`.\n            dust_token: Address of token serving as unit of measurement for dust_limit\n            dust_limit: The limit itself\n            price_oracle: Exposes getPriceFor method for price conversion\n            support_address: Ethereum address of the `MakerOtcSupportMethods` contract (optional).\n\n        Returns:\n            A `MatchingMarket` class instance.\n        \"\"\"\n\n        assert isinstance(dust_token, Address)\n        assert isinstance(dust_limit, Wad)\n        assert isinstance(price_oracle, Address)\n\n        return MatchingMarket(web3=web3, address=Contract._deploy(web3, MatchingMarket.abi, MatchingMarket.bin,\n                                                                  [dust_token.address,\n                                                                   dust_limit.value,\n                                                                   price_oracle.address]),\n                              support_address=support_address)\n\n    def add_token_pair_whitelist(self, base_token: Address, quote_token: Address) -> Transact:\n        \"\"\"Adds a token pair to the whitelist.\n\n        All newly created orders are checked against the whitelist.\n\n        Args:\n            base_token: Address of the ERC20 token.\n            quote_token: Address of the ERC20 token.\n\n        Returns:\n            A :py:class:`pymaker.Transact` instance, which can be used to trigger the transaction.\n        \"\"\"\n        assert(isinstance(base_token, Address))\n        assert(isinstance(quote_token, Address))\n\n        return Transact(self, self.web3, self.abi, self.address, self._contract,\n                        'addTokenPairWhitelist', [base_token.address, quote_token.address])\n\n    def get_orders(self, p_token: Token = None,  b_token: Token = None, block_ident: Optional[str] = None) -> List[Order]:\n        \"\"\"Get all active orders.\n\n        If both `p_token` and `b_token` are specified, orders will be filtered by these.\n        In case of the _MatchingMarket_ implementation, order enumeration will be much efficient\n        if these two parameters are supplied, as then orders can be fetched using `getBestOffer`\n        and a series of `getWorseOffer` calls. This approach will result in much lower number of calls\n        comparing to the naive 0..get_last_order_id approach, especially if the number of inactive orders\n        is very high.\n\n        Either none or both of these parameters have to be specified.\n\n        Args:\n            `p_token`: Token object (see `model.py`) of the `pay_token` to filter the orders by.\n            `b_token`: Token object (see `model.py`) of the `buy_token` to filter the orders by.\n            `block_ident`: Block identifier can either be a str, int or None and will fallback to 'latest' block.\n                           Used for historical retreival of orders.\n\n        Returns:\n            A list of `Order` objects representing all active orders on Oasis.\n        \"\"\"\n\n        assert((isinstance(p_token, Token) and isinstance(b_token, Token))\n               or ((p_token is None) and (b_token is None)))\n\n        assert(isinstance(block_ident, int) or isinstance(block_ident, str) or (block_ident is None))\n\n        block_ident = 'latest' if block_ident is None else block_ident\n\n        if (p_token is None) or (b_token is None):\n            pay_token = None\n            buy_token = None\n        else:\n            pay_token = p_token.address\n            buy_token = b_token.address\n\n        if pay_token is not None and buy_token is not None:\n            orders = []\n\n            if self._support_contract:\n                result = self._support_contract.functions.getOffers(self.address.address, pay_token.address, buy_token.address).call(block_identifier=block_ident)\n\n                while True:\n                    count = 0\n                    for i in range(0, 100):\n                        if result[3][i] != '0x0000000000000000000000000000000000000000':\n                            count += 1\n\n                            orders.append(Order(market=self,\n                                                order_id=result[0][i],\n                                                maker=Address(result[3][i]),\n                                                pay_token=pay_token,\n                                                pay_amount=p_token.normalize_amount(Wad(result[1][i])),\n                                                buy_token=buy_token,\n                                                buy_amount=b_token.normalize_amount(Wad(result[2][i])),\n                                                timestamp=result[4][i]))\n\n                    if count == 100:\n                        next_order_id = self._contract.functions.getWorseOffer(orders[-1].order_id).call(block_identifier=block_ident)\n                        result = self._support_contract.functions.getOffers(self.address.address, next_order_id).call(block_identifier=block_ident)\n\n                    else:\n                        break\n\n            else:\n                order_id = self._contract.functions.getBestOffer(pay_token.address, buy_token.address).call(block_identifier=block_ident)\n                while order_id != 0:\n                    order = self.get_order(order_id, block_ident)\n                    if order is not None:\n                        orders.append(order)\n\n                    order_id = self._contract.functions.getWorseOffer(order_id).call(block_identifier=block_ident)\n\n            return sorted(orders, key=lambda order: order.order_id)\n        else:\n            return super(MatchingMarket, self).get_orders(pay_token, buy_token)\n\n    def make(self, p_token: Token, pay_amount: Wad, b_token: Token, buy_amount: Wad, pos: int = None) -> Transact:\n        \"\"\"Create a new order.\n\n        The `have_amount` of `have_token` token will be taken from you on order creation and deposited\n        in the market contract. Allowance needs to be set first. Refer to the `approve()` method\n        in the `ERC20Token` class.\n\n        The `MatchingMarket` contract maintains an internal ordered linked list of orders, which allows the contract\n        to do automated matching. Client placing a new order can either let the contract find the correct\n        position in the linked list (by passing `0` as the `pos` argument of `make`) or calculate the position\n        itself and just pass the right value to the contract (this will happen if you omit the `pos`\n        argument of `make`). The latter should always use less gas. If the client decides not to calculate the\n        position or it does get it wrong and the number of open orders is high at the same time, the new order\n        may not even be placed at all as the attempt to calculate the position by the contract will likely fail\n        due to high gas usage.\n\n        When complete, `receipt.result` will contain order_id of the new order.\n\n        Args:\n            p_token: Token object (see `model.py`) of the ERC20 token you want to put on sale.\n            pay_amount: Amount of the `pay_token` token you want to put on sale.\n            b_token: Token object (see `model.py`) of the ERC20 token you want to be paid with.\n            buy_amount: Amount of the `buy_token` you want to receive.\n            pos: The position to insert the order at in the sorted list.\n                If `None`, the optimal position will automatically get calculated.\n\n        Returns:\n            A :py:class:`pymaker.Transact` instance, which can be used to trigger the transaction.\n        \"\"\"\n        assert(isinstance(p_token, Token))\n        assert(isinstance(pay_amount, Wad))\n        assert(isinstance(b_token, Token))\n        assert(isinstance(buy_amount, Wad))\n        assert(isinstance(pos, int) or (pos is None))\n        assert(pay_amount > Wad(0))\n        assert(buy_amount > Wad(0))\n\n        pay_token = p_token.address\n        buy_token = b_token.address\n\n\n        if pos is None:\n            pos = self.position(pay_amount=pay_amount,\n                                p_token=p_token,\n                                b_token=b_token,\n                                buy_amount=buy_amount)\n        else:\n            assert(pos >= 0)\n\n        return Transact(self, self.web3, self.abi, self.address, self._contract,\n                        'offer(uint256,address,uint256,address,uint256)',\n                        [pay_amount.value, pay_token.address, buy_amount.value, buy_token.address, pos], None,\n                        self._make_order_id_result_function)\n\n    def position(self, p_token: Token, pay_amount: Wad, b_token: Token, buy_amount: Wad) -> int:\n        \"\"\"Calculate the position (`pos`) new order should be inserted at to minimize gas costs.\n\n        The `MatchingMarket` contract maintains an internal ordered linked list of orders, which allows the contract\n        to do automated matching. Client placing a new order can either let the contract find the correct\n        position in the linked list (by passing `0` as the `pos` argument of `make`) or calculate the position\n        itself and just pass the right value to the contract (this will happen if you omit the `pos`\n        argument of `make`). The latter should always use less gas. If the client decides not to calculate the\n        position or it does get it wrong and the number of open orders is high at the same time, the new order\n        may not even be placed at all as the attempt to calculate the position by the contract will likely fail\n        due to high gas usage.\n\n        This method is responsible for calculating the correct insertion position. It is used internally\n        by `make` when `pos` argument is omitted (or is `None`).\n\n        Args:\n            p_token: Token object (see `model.py`) of the token you want to put on sale.\n            pay_amount: Amount of the `pay_token` token you want to put on sale.\n            b_token: Token object (see `model.py`) of the token you want to be paid with.\n            buy_amount: Amount of the `buy_token` you want to receive.\n\n        Returns:\n            The position (`pos`) new order should be inserted at.\n        \"\"\"\n        assert(isinstance(p_token, Token))\n        assert(isinstance(pay_amount, Wad))\n        assert(isinstance(b_token, Token))\n        assert(isinstance(buy_amount, Wad))\n\n        pay_token = p_token.address\n        buy_token = b_token.address\n\n        self.logger.debug(\"Enumerating orders for position calculation...\")\n\n        orders = filter(lambda order: order.pay_amount / order.buy_amount >= p_token.normalize_amount(pay_amount) / b_token.normalize_amount(buy_amount),\n                        self.get_orders(p_token, b_token))\n\n        self.logger.debug(\"Enumerating orders for position calculation finished\")\n\n        sorted_orders = sorted(orders, key=lambda o: o.pay_amount / o.buy_amount)\n        return sorted_orders[0].order_id if len(sorted_orders) > 0 else 0\n\n    def __repr__(self):\n        return f\"MatchingMarket('{self.address}')\"\n"
  },
  {
    "path": "pymaker/oracles.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2019 grandizzy\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nfrom web3 import Web3\n\nfrom pymaker import Contract, Address, Transact\nfrom pymaker.numeric import Wad\n\n\n# TODO: Complete implementation and unit test\nclass OSM(Contract):\n    \"\"\"A client for the `OSM` contract.\n\n    You can find the source code of the `OSM` contract here:\n    <https://github.com/makerdao/osm>.\n\n    Attributes:\n        web3: An instance of `Web` from `web3.py`.\n        address: Ethereum address of the `OSM` contract.\n    \"\"\"\n\n    abi = Contract._load_abi(__name__, 'abi/OSM.abi')\n    bin = Contract._load_bin(__name__, 'abi/OSM.bin')\n\n    def __init__(self, web3: Web3, address: Address):\n        assert (isinstance(web3, Web3))\n        assert (isinstance(address, Address))\n\n        self.web3 = web3\n        self.address = address\n        self._contract = self._get_contract(web3, self.abi, address)\n\n    def poke(self) -> Transact:\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'poke', [])\n\n    def peek(self) -> Wad:\n        return Wad(self._extract_price(3))\n\n    def peep(self) -> Wad:\n        return Wad(self._extract_price(4))\n\n    def zzz(self) -> int:\n        return self._contract.functions.zzz().call()\n\n    def _extract_price(self, storage_slot: int) -> int:\n        assert isinstance(storage_slot, int)\n        return Web3.toInt(self.web3.eth.getStorageAt(self.address.address, storage_slot)[16:])\n\n    def __repr__(self):\n        return f\"OSM('{self.address}')\"\n\n\nclass OldUniv2LpOSM(OSM):\n    \"\"\"A custom `OSM` contract for Uniswap LP tokens which used different storage slots, obsolete as of dss-1.7.0\n\n    You can find the source code of the `OSM` contract here:\n    <https://github.com/makerdao/univ2-lp-oracle>.\n    \"\"\"\n\n    def __init__(self, web3: Web3, address: Address):\n        super().__init__(web3, address)\n\n    def peek(self) -> Wad:\n        return Wad(self._extract_price(6))\n\n    def peep(self) -> Wad:\n        return Wad(self._extract_price(7))\n"
  },
  {
    "path": "pymaker/proxy.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2018,2019 bargst\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\nfrom typing import List, Optional\n\nfrom hexbytes import HexBytes\nfrom web3 import Web3\nfrom web3._utils.events import get_event_data\n\nfrom eth_abi.codec import ABICodec\nfrom eth_abi.registry import registry as default_registry\n\nfrom pymaker import Address, Contract, Transact, Receipt, Calldata\nfrom pymaker.util import hexstring_to_bytes\n\n\nclass DSProxyCache(Contract):\n    \"\"\"A client for the `DSProxyCache` contract.\n\n    Ref. <https://github.com/dapphub/ds-proxy/blob/master/src/proxy.sol#L120>\n    \"\"\"\n\n    abi = Contract._load_abi(__name__, 'abi/DSProxyCache.abi')\n    bin = Contract._load_bin(__name__, 'abi/DSProxyCache.bin')\n\n    def __init__(self, web3: Web3, address: Address):\n        assert (isinstance(web3, Web3))\n        assert (isinstance(address, Address))\n\n        self.web3 = web3\n        self.address = address\n        self._contract = self._get_contract(web3, self.abi, address)\n\n    @classmethod\n    def deploy(cls, web3: Web3):\n        return cls(web3=web3, address=Contract._deploy(web3, cls.abi, cls.bin, []))\n\n    def read(self, code: str) -> Optional[Address]:\n        assert (isinstance(code, str))\n\n        if code.startswith('0x'):\n            b32_code = hexstring_to_bytes(code)\n        else:\n            b32_code = hexstring_to_bytes('0x' + code)\n        address = Address(self._contract.functions.read(b32_code).call())\n\n        if address == Address('0x0000000000000000000000000000000000000000'):\n            return None\n        else:\n            return address\n\n    def write(self, code: str):\n        assert (isinstance(code, str))\n\n        if code.startswith('0x'):\n            b32_code = hexstring_to_bytes(code)\n        else:\n            b32_code = hexstring_to_bytes('0x' + code)\n\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'write', [b32_code])\n\n    def __repr__(self):\n        return f\"DSProxyCache('{self.address}')\"\n\n\nclass DSProxy(Contract):\n    \"\"\"A client for the `DSProxy` contract.\n\n    Ref. <https://github.com/dapphub/ds-proxy/blob/master/src/proxy.sol#L28>\n    \"\"\"\n\n    abi = Contract._load_abi(__name__, 'abi/DSProxy.abi')\n    bin = Contract._load_bin(__name__, 'abi/DSProxy.bin')\n\n    def __init__(self, web3: Web3, address: Address):\n        assert (isinstance(web3, Web3))\n        assert (isinstance(address, Address))\n\n        self.web3 = web3\n        self.address = address\n        self._contract = self._get_contract(web3, self.abi, address)\n\n    def authority(self) -> Address:\n        \"\"\"Return the current `authority` of a `DSAuth`-ed contract.\n\n        Returns:\n            The address of the current `authority`.\n        \"\"\"\n        return Address(self._contract.functions.authority().call())\n\n    def set_authority(self, address: Address) -> Transact:\n        \"\"\"Set the `authority` of a `DSAuth`-ed contract.\n\n        Args:\n            address: The address of the new `authority`.\n\n        Returns:\n            A :py:class:`pymaker.Transact` instance, which can be used to trigger the transaction.\n        \"\"\"\n        assert(isinstance(address, Address))\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'setAuthority', [address.address])\n\n    @classmethod\n    def deploy(cls, web3: Web3, cache: Address):\n        return cls(web3=web3, address=Contract._deploy(web3, cls.abi, cls.bin, [cache.address]))\n\n    def execute(self, code: str, calldata: Calldata) -> Transact:\n        assert (isinstance(code, str))\n        assert (isinstance(calldata, Calldata))\n\n        if code.startswith('0x'):\n            b32_code = hexstring_to_bytes(code)\n        else:\n            b32_code = hexstring_to_bytes('0x' + code)\n\n        return Transact(self, self.web3, self.abi, self.address, self._contract,\n                        'execute(bytes,bytes)', [b32_code, calldata.as_bytes()])\n\n    def call(self, code: str, calldata: Calldata) -> (Address, HexBytes):\n        assert (isinstance(code, str))\n        assert (isinstance(calldata, Calldata))\n\n        fn = self._contract.get_function_by_signature('execute(bytes,bytes)')\n        target, response = fn(code, calldata.value).call()\n\n        return Address(target), HexBytes(response)\n\n    def execute_at(self, address: Address, calldata: Calldata) -> Transact:\n        assert (isinstance(address, Address))\n        assert (isinstance(calldata, Calldata))\n\n        return Transact(self, self.web3, self.abi, self.address, self._contract,\n                        'execute(address,bytes)', [address.address, calldata.as_bytes()])\n\n    def call_at(self, address: Address, calldata: Calldata) -> Transact:\n        assert (isinstance(address, Address))\n        assert (isinstance(calldata, Calldata))\n\n        fn = self._contract.get_function_by_signature('execute(address,bytes)')\n        response = fn(address.address, calldata.value).call()\n\n        return HexBytes(response)\n\n    def set_cache(self, address: Address) -> Transact:\n        assert (isinstance(address, Address))\n\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'setCache', [address.address])\n\n    def cache(self) -> Address:\n        return Address(self._contract.functions.cache().call())\n\n    def __repr__(self):\n        return f\"DSProxy('{self.address}')\"\n\n\n# event Created(address indexed sender, address indexed owner, address proxy, address cache);\nclass LogCreated:\n    def __init__(self, log):\n        self.sender = Address(log['args']['sender'])\n        self.owner = Address(log['args']['owner'])\n        self.proxy = Address(log['args']['proxy'])\n        self.cache = Address(log['args']['cache'])\n        self.raw = log\n\n    @classmethod\n    def from_event(cls, event: dict):\n        assert (isinstance(event, dict))\n\n        topics = event.get('topics')\n        if topics and topics[0] == HexBytes('0x259b30ca39885c6d801a0b5dbc988640f3c25e2f37531fe138c5c5af8955d41b'):\n            log_created_abi = [abi for abi in DSProxyFactory.abi if abi.get('name') == 'Created'][0]\n            codec = ABICodec(default_registry)\n            event_data = get_event_data(codec, log_created_abi, event)\n\n            return LogCreated(event_data)\n        else:\n            raise Exception(f'[from_event] Invalid topic in {event}')\n\n    def __eq__(self, other):\n        assert (isinstance(other, LogCreated))\n        return self.__dict__ == other.__dict__\n\n\nclass DSProxyFactory(Contract):\n    \"\"\"A client for the `DSProxyFactory` contract.\n\n    Ref. <https://github.com/dapphub/ds-proxy/blob/master/src/proxy.sol#L90>\n    \"\"\"\n\n    abi = Contract._load_abi(__name__, 'abi/DSProxyFactory.abi')\n    bin = Contract._load_bin(__name__, 'abi/DSProxyFactory.bin')\n\n    def __init__(self, web3: Web3, address: Address):\n        assert (isinstance(web3, Web3))\n        assert (isinstance(address, Address))\n\n        self.web3 = web3\n        self.address = address\n        self._contract = self._get_contract(web3, self.abi, address)\n\n    @classmethod\n    def deploy(cls, web3: Web3):\n        return cls(web3=web3, address=Contract._deploy(web3, cls.abi, cls.bin, []))\n\n    def build(self) -> Transact:\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'build()', [])\n\n    def build_for(self, address: Address) -> Transact:\n        assert (isinstance(address, Address))\n\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'build(address)', [address.address])\n\n    def cache(self) -> Address:\n        return Address(self._contract.functions.cache().call())\n\n    def is_proxy(self, address: Address) -> bool:\n        assert (isinstance(address, Address))\n\n        return self._contract.functions.isProxy(address.address).call()\n\n    def past_build(self, number_of_past_blocks: int, event_filter: dict = None) -> List[LogCreated]:\n        \"\"\"Synchronously retrieve past LogCreated events.\n\n        `LogCreated` events are emitted every time someone build a proxy from the factory.\n\n        Args:\n            number_of_past_blocks: Number of past Ethereum blocks to retrieve the events from.\n            event_filter: Filter which will be applied to returned events.\n\n        Returns:\n            List of past `LogCreated` events represented as :py:class:`pymaker.proxy.LogCreated` class.\n        \"\"\"\n        assert isinstance(number_of_past_blocks, int)\n        assert isinstance(event_filter, dict) or (event_filter is None)\n\n        return self._past_events(self._contract, 'Created', LogCreated, number_of_past_blocks, event_filter)\n\n    @classmethod\n    def log_created(cls, receipt: Receipt) -> List[LogCreated]:\n        assert isinstance(receipt, Receipt)\n\n        events = []\n        for log in receipt.raw_receipt.logs:\n            try:\n                event = LogCreated.from_event(dict(log))\n                events.append(event)\n            except:\n                pass\n        return events\n\n    def __repr__(self):\n        return f\"DSProxyFactory('{self.address}')\"\n\n\nclass ProxyRegistry(Contract):\n    \"\"\"A client for the `ProxyRegistry` contract.\n\n    Ref. <https://github.com/makerdao/proxy-registry/blob/master/src/ProxyRegistry.sol>\n    \"\"\"\n\n    abi = Contract._load_abi(__name__, 'abi/ProxyRegistry.abi')\n    bin = Contract._load_bin(__name__, 'abi/ProxyRegistry.bin')\n\n    def __init__(self, web3: Web3, address: Address):\n        assert isinstance(web3, Web3)\n        assert isinstance(address, Address)\n\n        self.web3 = web3\n        self.address = address\n        self._contract = self._get_contract(web3, self.abi, address)\n\n    def build(self, owner: Address) -> Transact:\n        assert isinstance(owner, Address)\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'build(address)', [owner.address])\n\n    def proxies(self, owner: Address) -> Address:\n        assert isinstance(owner, Address)\n        return Address(self._contract.functions.proxies(owner.address).call())\n\n    def __repr__(self):\n        return f\"ProxyRegistry('{self.address}')\"\n\n\nclass DssProxyActionsDsr(Contract):\n    \"\"\"A client for the `DssProxyActionsDsr` contract.\n\n    Ref. <https://github.com/makerdao/dss-proxy-actions/blob/master/src/DssProxyActions.sol>\n    \"\"\"\n\n    abi = Contract._load_abi(__name__, 'abi/DssProxyActionsDsr.abi')\n    bin = Contract._load_bin(__name__, 'abi/DssProxyActionsDsr.bin')\n\n    def __init__(self, web3: Web3, address: Address):\n        assert isinstance(web3, Web3)\n        assert isinstance(address, Address)\n\n        self.web3 = web3\n        self.address = address\n        self._contract = self._get_contract(web3, self.abi, address)\n\n"
  },
  {
    "path": "pymaker/reloadable_config.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2020 MikeHathaway\n#\n# This is proprietary (closed source) software. Unauthorized copying,\n# distributing, downloading, sharing, conveying, modifying, or use, via any\n# medium or in any manner whatsoever are strictly prohibited.  No license or\n# any other rights are provided with this file.\n\nimport json\nimport _jsonnet\nimport logging\nimport os\nimport zlib\n\nfrom typing import Optional, List\n\n\nclass ReloadableConfig:\n    \"\"\"Reloadable JSON config file reader.\n\n    This reader will always read most up-to-date version of the config file from disk\n    on each call to `get_config()`. In addition to that, whenever the config file changes,\n    a log event is emitted.\n\n    Attributes:\n        filename: Filename of the configuration file.\n    \"\"\"\n\n    logger = logging.getLogger('reloadable-config')\n\n    def __init__(self, filename: str):\n        assert(isinstance(filename, str))\n\n        self.filename = filename\n        self._checksum = None\n        self._checksum_config = None\n        self._config = None\n        self._mtime = None\n        self._imported_paths_to_mtimes = {}\n\n    def _import_callback(self, paths: list):\n\n        def callback(path, file):\n            abs_path = os.path.join(os.path.dirname(self.filename), file)\n            paths.append(abs_path)\n\n            with open(abs_path) as file_obj:\n                return file, file_obj.read()\n\n        return callback\n\n    def _load_mtimes(self, imported_paths: List[str]) -> dict:\n        return {path: os.path.getmtime(path) for path in imported_paths}\n\n    def _mtimes_changed(self, imported_paths_to_mtimes: dict) -> bool:\n        try:\n            return any(os.path.getmtime(path) != mtime for path, mtime in imported_paths_to_mtimes.items())\n\n        except:\n            return True\n\n    def get_config(self):\n        \"\"\"Reads the JSON config file from disk and returns it as a Python object.\n\n        Returns:\n            Current configuration as a `dict` or `list` object.\n        \"\"\"\n\n        mtime = os.path.getmtime(self.filename)\n\n        # If the modification time has not changed since the last time we have read the file,\n        # we return the last content without opening and parsing it. It saves us around ~ 30ms.\n        #\n        # Ultimately something like `watchdog` (<https://pythonhosted.org/watchdog/index.html>)\n        # should be used to watch the filesystem changes asynchronously.\n        if self._config is not None and self._mtime is not None:\n            if mtime == self._mtime \\\n                    and not self._mtimes_changed(self._imported_paths_to_mtimes):\n                return self._config\n\n        with open(self.filename) as data_file:\n            content_file = data_file.read()\n            imported_paths = []\n\n            content_config = _jsonnet.evaluate_snippet(\"snippet\", content_file, ext_vars={},\n                                                       import_callback=self._import_callback(imported_paths))\n            result = None\n            try:\n                result = json.loads(content_config)\n            except ValueError as ex:\n                logging.error(f\"Failed to read config: {ex}\")\n                raise ex\n\n            # Report if file has been newly loaded or reloaded\n            checksum = zlib.crc32(content_file.encode('utf-8'))\n            checksum_config = zlib.crc32(content_config.encode('utf-8'))\n\n            if self._checksum is None:\n                self.logger.info(f\"Loaded configuration from '{self.filename}'\")\n                self.logger.debug(f\"Config file is: \" + json.dumps(result, indent=4))\n            elif self._checksum != checksum:\n                self.logger.info(f\"Reloaded configuration from '{self.filename}'\")\n                self.logger.debug(f\"Reloaded config file is: \" + json.dumps(result, indent=4))\n            elif self._imported_paths_to_mtimes != self._load_mtimes(imported_paths):\n                self.logger.info(f\"Reloaded configuration from '{self.filename}' (due to imported file changed)\")\n                self.logger.debug(f\"Reloaded config file is: \" + json.dumps(result, indent=4))\n            elif self._checksum_config != checksum_config:\n                self.logger.debug(f\"Parsed configuration from '{self.filename}'\")\n                self.logger.debug(f\"Parsed config file is: \" + json.dumps(result, indent=4))\n\n            self._checksum = checksum\n            self._checksum_config = checksum_config\n            self._config = result\n            self._mtime = mtime\n            self._imported_paths_to_mtimes = self._load_mtimes(imported_paths)\n\n            return result\n"
  },
  {
    "path": "pymaker/sai.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2017-2018 reverendus\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nfrom typing import Optional\n\nfrom web3 import Web3\n\nfrom pymaker import Address, Contract, Transact\nfrom pymaker.numeric import Wad, Ray\nfrom pymaker.token import ERC20Token\nfrom pymaker.util import int_to_bytes32\n\n\nclass Cup:\n    \"\"\"Represents details of a single cup managed by a `Tub`.\n\n    Notes:\n        `art` is denominated in internal debt units and should not be used directly, unless you really\n        know what you're doing and you know what `chi()` and `rho()` are.\n\n    Attributes:\n        cup_id: The identifier of the cup.\n        lad: Address of the owner of the cup.\n        art: The amount of outstanding debt (denominated in internal debt units).\n        ink: The amount of SKR collateral locked in the cup.\n    \"\"\"\n    def __init__(self, cup_id: int, lad: Address, ink: Wad, art: Wad):\n        assert(isinstance(cup_id, int))\n        assert(isinstance(lad, Address))\n        assert(isinstance(art, Wad))\n        assert(isinstance(ink, Wad))\n        self.cup_id = cup_id\n        self.lad = lad\n        self.art = art\n        self.ink = ink\n\n    def __repr__(self):\n        return f\"Cup(cup_id={self.cup_id}, lad={repr(self.lad)}, art={self.art}, ink={self.ink})\"\n\n\nclass Tub(Contract):\n    \"\"\"A client for the `Tub` contract.\n\n    SAI is a simple version of the diversely collateralized DAI stablecoin.\n\n    In this model there is one type of underlying collateral (called gems).\n    The SKR token represents claims on the system's excess gems, and is the\n    only admissible type of collateral.  Gems can be converted to/from SKR.\n\n    Any transfers of SAI or SKR are done using the normal ERC20 interface;\n    until settlement mode is triggered, SAI users should only need ERC20.\n    ``ERC20Token`` class may be used for it.\n\n    Attributes:\n        web3: An instance of `Web` from `web3.py`.\n        address: Ethereum address of the `Tub` contract.\n    \"\"\"\n\n    abi = Contract._load_abi(__name__, 'abi/SaiTub.abi')\n    bin = Contract._load_bin(__name__, 'abi/SaiTub.bin')\n\n    def __init__(self, web3: Web3, address: Address):\n        assert(isinstance(web3, Web3))\n        assert(isinstance(address, Address))\n\n        self.web3 = web3\n        self.address = address\n        self._contract = self._get_contract(web3, self.abi, address)\n\n    @staticmethod\n    def deploy(web3: Web3, sai: Address, sin: Address, skr: Address, gem: Address, gov: Address, pip: Address, pep: Address, vox: Address, pit: Address):\n        assert(isinstance(sai, Address))\n        assert(isinstance(sin, Address))\n        assert(isinstance(skr, Address))\n        assert(isinstance(gem, Address))\n        assert(isinstance(gov, Address))\n        assert(isinstance(pip, Address))\n        assert(isinstance(pep, Address))\n        assert(isinstance(vox, Address))\n        assert(isinstance(pit, Address))\n\n        return Tub(web3=web3, address=Contract._deploy(web3, Tub.abi, Tub.bin,\n                                                       [sai.address, sin.address, skr.address, gem.address, gov.address,\n                                                        pip.address, pep.address, vox.address, pit.address]))\n\n    def set_authority(self, address: Address) -> Transact:\n        assert(isinstance(address, Address))\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'setAuthority', [address.address])\n\n    def approve(self, approval_function):\n        \"\"\"Approve the `Tub` to access our GEM, SKR, SAI and GOV balances.\n\n        For available approval functions (i.e. approval modes) see `directly` and `via_tx_manager`\n        in `pymaker.approval`.\n\n        Args:\n            approval_function: Approval function (i.e. approval mode).\n        \"\"\"\n        assert(callable(approval_function))\n\n        approval_function(ERC20Token(web3=self.web3, address=self.gem()), self.address, 'Tub')\n        approval_function(ERC20Token(web3=self.web3, address=self.skr()), self.address, 'Tub')\n        approval_function(ERC20Token(web3=self.web3, address=self.sai()), self.address, 'Tub')\n        approval_function(ERC20Token(web3=self.web3, address=self.gov()), self.address, 'Tub')\n\n    def era(self) -> int:\n        \"\"\"Return the current `Tub` timestamp.\n\n        Returns:\n            Timestamp as a unix timestamp.\n        \"\"\"\n        return self._contract.functions.era().call()\n\n    def tap(self) -> Address:\n        \"\"\"Get the address of the `Tap` contract.\n\n        Returns:\n            The address of the `Tap` contract.\n        \"\"\"\n        return Address(self._contract.functions.tap().call())\n\n    def sai(self) -> Address:\n        \"\"\"Get the SAI token.\n\n        Returns:\n            The address of the SAI token.\n        \"\"\"\n        return Address(self._contract.functions.sai().call())\n\n    def sin(self) -> Address:\n        \"\"\"Get the SIN token.\n\n        Returns:\n            The address of the SIN token.\n        \"\"\"\n        return Address(self._contract.functions.sin().call())\n\n    def gov(self) -> Address:\n        \"\"\"Get the MKR token.\n\n        Returns:\n            The address of the MKR token.\n        \"\"\"\n        return Address(self._contract.functions.gov().call())\n\n    def vox(self) -> Address:\n        \"\"\"Get the address of the `Vox` contract.\n\n        Returns:\n            The address of the `Vox` contract.\n        \"\"\"\n        return Address(self._contract.functions.vox().call())\n\n    def pit(self) -> Address:\n        \"\"\"Get the governance vault.\n\n        Returns:\n            The address of the `DSVault` holding the governance tokens awaiting burn.\n        \"\"\"\n        return Address(self._contract.functions.pit().call())\n\n    def skr(self) -> Address:\n        \"\"\"Get the SKR token.\n\n        Returns:\n            The address of the SKR token.\n        \"\"\"\n        return Address(self._contract.functions.skr().call())\n\n    def gem(self) -> Address:\n        \"\"\"Get the collateral token (eg. W-ETH).\n\n        Returns:\n            The address of the collateral token.\n        \"\"\"\n        return Address(self._contract.functions.gem().call())\n\n    def pip(self) -> Address:\n        \"\"\"Get the reference (GEM) price feed.\n\n        Returns:\n            The address of the reference (GEM) price feed, which could be a `DSValue`, a `DSCache`, `Mednianizer` etc.\n        \"\"\"\n        return Address(self._contract.functions.pip().call())\n\n    def pep(self) -> Address:\n        \"\"\"Get the governance (MKR) price feed.\n\n        Returns:\n            The address of the governance (MKR) price feed, which could be a `DSValue`, a `DSCache`, `Mednianizer` etc.\n        \"\"\"\n        return Address(self._contract.functions.pep().call())\n\n    def axe(self) -> Ray:\n        \"\"\"Get the liquidation penalty.\n\n        Returns:\n            The liquidation penalty. `1.0` means no penalty. `1.2` means 20% penalty.\n        \"\"\"\n        return Ray(self._contract.functions.axe().call())\n\n    def cap(self) -> Wad:\n        \"\"\"Get the debt ceiling.\n\n        Returns:\n            The debt ceiling in SAI.\n        \"\"\"\n        return Wad(self._contract.functions.cap().call())\n\n    def mat(self) -> Ray:\n        \"\"\"Get the liquidation ratio.\n\n        Returns:\n            The liquidation ratio. `1.5` means the liquidation ratio is 150%.\n        \"\"\"\n        return Ray(self._contract.functions.mat().call())\n\n    def tax(self) -> Ray:\n        \"\"\"Get the stability fee.\n\n        Returns:\n            Per-second value of the stability fee. `1.0` means no stability fee.\n        \"\"\"\n        return Ray(self._contract.functions.tax().call())\n\n    def reg(self) -> int:\n        \"\"\"Get the Tub stage ('register').\n\n        Returns:\n            The current Tub stage (0=Usual, 1=Caged).\n        \"\"\"\n        return self._contract.functions.reg().call()\n\n    def fit(self) -> Ray:\n        \"\"\"Get the GEM per SKR settlement price.\n\n        Returns:\n            The GEM per SKR settlement (kill) price.\n        \"\"\"\n        return Ray(self._contract.functions.fit().call())\n\n    def rho(self) -> int:\n        \"\"\"Get the time of the last drip.\n\n        Returns:\n            The time of the last drip as a unix timestamp.\n        \"\"\"\n        return self._contract.functions.rho().call()\n\n    def tau(self) -> int:\n        \"\"\"Get the time of the last prod.\n\n        Returns:\n            The time of the last prod as a unix timestamp.\n        \"\"\"\n        return self._contractTip.functions.tau().call()\n\n    def chi(self) -> Ray:\n        \"\"\"Get the internal debt price.\n\n        Every invocation of this method calls `drip()` internally, so the value you receive is always up-to-date.\n        But as calling it doesn't result in an Ethereum transaction, the actual `_chi` value in the smart\n        contract storage does not get updated.\n\n        Returns:\n            The internal debt price in SAI.\n        \"\"\"\n        return Ray(self._contract.functions.chi().call())\n\n    def mold_axe(self, new_axe: Ray) -> Transact:\n        \"\"\"Update the liquidation penalty.\n\n        Args:\n            new_axe: The new value of the liquidation penalty (`axe`). `1.0` means no penalty. `1.2` means 20% penalty.\n\n        Returns:\n            A :py:class:`pymaker.Transact` instance, which can be used to trigger the transaction.\n        \"\"\"\n        assert isinstance(new_axe, Ray)\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'mold', [bytes('axe', 'utf-8'), new_axe.value])\n\n    def mold_cap(self, new_cap: Wad) -> Transact:\n        \"\"\"Update the debt ceiling.\n\n        Args:\n            new_cap: The new value of the debt ceiling (`cap`), in SAI.\n\n        Returns:\n            A :py:class:`pymaker.Transact` instance, which can be used to trigger the transaction.\n        \"\"\"\n        assert isinstance(new_cap, Wad)\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'mold', [bytes('cap', 'utf-8'), new_cap.value])\n\n    def mold_mat(self, new_mat: Ray) -> Transact:\n        \"\"\"Update the liquidation ratio.\n\n        Args:\n            new_mat: The new value of the liquidation ratio (`mat`). `1.5` means the liquidation ratio is 150%.\n\n        Returns:\n            A :py:class:`pymaker.Transact` instance, which can be used to trigger the transaction.\n        \"\"\"\n        assert isinstance(new_mat, Ray)\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'mold', [bytes('mat', 'utf-8'), new_mat.value])\n\n    def mold_tax(self, new_tax: Ray) -> Transact:\n        \"\"\"Update the stability fee.\n\n        Args:\n            new_tax: The new per-second value of the stability fee (`tax`). `1.0` means no stability fee.\n\n        Returns:\n            A :py:class:`pymaker.Transact` instance, which can be used to trigger the transaction.\n        \"\"\"\n        assert isinstance(new_tax, Ray)\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'mold', [bytes('tax', 'utf-8'), new_tax.value])\n\n    def mold_gap(self, new_gap: Wad) -> Transact:\n        \"\"\"Update the current spread (`gap`) for `join` and `exit`.\n\n        Args:\n            new_tax: The new value of the spread (`gap`). `1.0` means no spread, `1.01` means 1% spread.\n\n        Returns:\n            A :py:class:`pymaker.Transact` instance, which can be used to trigger the transaction.\n        \"\"\"\n        assert isinstance(new_gap, Wad)\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'mold', [bytes('gap', 'utf-8'), new_gap.value])\n\n    def drip(self) -> Transact:\n        \"\"\"Recalculate the internal debt price (`chi`).\n\n        Returns:\n            A :py:class:`pymaker.Transact` instance, which can be used to trigger the transaction.\n        \"\"\"\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'drip', [])\n\n    def prod(self) -> Transact:\n        \"\"\"Recalculate the accrued holder fee (`par`).\n\n        Returns:\n            A :py:class:`pymaker.Transact` instance, which can be used to trigger the transaction.\n        \"\"\"\n        return Transact(self, self.web3, self.abiTip, self.tip(), self._contractTip, 'prod', [])\n\n    def din(self) -> Wad:\n        \"\"\"Get the amount of total debt.\n\n        Returns:\n            The amount of total debt in SAI.\n        \"\"\"\n        return Wad(self._contract.functions.din().call())\n\n    def pie(self) -> Wad:\n        \"\"\"Get the amount of raw collateral.\n\n        Returns:\n            The amount of raw collateral in GEM.\n        \"\"\"\n        return Wad(self._contract.functions.pie().call())\n\n    def air(self) -> Wad:\n        \"\"\"Get the amount of backing collateral.\n\n        Returns:\n            The amount of backing collateral in SKR.\n        \"\"\"\n        return Wad(self._contract.functions.air().call())\n\n    def tag(self) -> Ray:\n        \"\"\"Get the reference price (REF per SKR).\n\n        The price is read from the price feed (`tip()`) every time this method gets called.\n        Its value is actually the value from the feed (REF per GEM) multiplied by `per()` (GEM per SKR).\n\n        Returns:\n            The reference price (REF per SKR).\n        \"\"\"\n        return Ray(self._contract.functions.tag().call())\n\n    def per(self) -> Ray:\n        \"\"\"Get the current average entry/exit price (GEM per SKR).\n\n        In order to get the price that will be actually used on `join()` or `exit()`, see\n        `ask()` and `bid()` respectively. The difference is due to the spread (`gap`).\n\n        Returns:\n            The current GEM per SKR price.\n        \"\"\"\n        return Ray(self._contract.functions.per().call())\n\n    def gap(self) -> Wad:\n        \"\"\"Get the current spread for `join` and `exit`.\n\n        Returns:\n            The current spread for `join` and `exit`. `1.0` means no spread, `1.01` means 1% spread.\n        \"\"\"\n        return Wad(self._contract.functions.gap().call())\n\n    def bid(self, amount: Wad) -> Wad:\n        \"\"\"Get the current `exit()`.\n\n        Returns:\n            The amount of GEM you will get for `amount` SKR in `join()`.\n        \"\"\"\n        assert(isinstance(amount, Wad))\n\n        return Wad(self._contract.functions.bid(amount.value).call())\n\n    def ask(self, amount: Wad) -> Wad:\n        \"\"\"Get the current `join()` price.\n\n        Returns:\n            The amount of GEM you will have to pay to get `amount` SKR fromm `join()`.\n        \"\"\"\n        assert(isinstance(amount, Wad))\n\n        return Wad(self._contract.functions.ask(amount.value).call())\n\n    def cupi(self) -> int:\n        \"\"\"Get the last cup id\n\n        Returns:\n            The id of the last cup created. Zero if no cups have been created so far.\n        \"\"\"\n        return self._contract.functions.cupi().call()\n\n    def cups(self, cup_id: int) -> Cup:\n        \"\"\"Get the cup details.\n\n        Args:\n            cup_id: Id of the cup to get the details of.\n\n        Returns:\n            Class encapsulating cup details.\n        \"\"\"\n        assert isinstance(cup_id, int)\n        array = self._contract.functions.cups(int_to_bytes32(cup_id)).call()\n        return Cup(cup_id, Address(array[0]), Wad(array[1]), Wad(array[2]))\n\n    def tab(self, cup_id: int) -> Wad:\n        \"\"\"Get the amount of debt in a cup.\n\n        Args:\n            cup_id: Id of the cup.\n\n        Returns:\n            Amount of debt in the cup, in SAI.\n        \"\"\"\n        assert isinstance(cup_id, int)\n        return Wad(self._contract.functions.tab(int_to_bytes32(cup_id)).call())\n\n    def ink(self, cup_id: int) -> Wad:\n        \"\"\"Get the amount of SKR collateral locked in a cup.\n\n        Args:\n            cup_id: Id of the cup.\n\n        Returns:\n            Amount of SKR collateral locked in the cup, in SKR.\n        \"\"\"\n        assert isinstance(cup_id, int)\n        return Wad(self._contract.functions.ink(int_to_bytes32(cup_id)).call())\n\n    def lad(self, cup_id: int) -> Address:\n        \"\"\"Get the owner of a cup.\n\n        Args:\n            cup_id: Id of the cup.\n\n        Returns:\n            Address of the owner of the cup.\n        \"\"\"\n        assert isinstance(cup_id, int)\n        return Address(self._contract.functions.lad(int_to_bytes32(cup_id)).call())\n\n    def safe(self, cup_id: int) -> bool:\n        \"\"\"Determine if a cup is safe.\n\n        Args:\n            cup_id: Id of the cup\n\n        Returns:\n            `True` if the cup is safe. `False` otherwise.\n        \"\"\"\n        assert isinstance(cup_id, int)\n        return self._contract.functions.safe(int_to_bytes32(cup_id)).call()\n\n    def join(self, amount_in_skr: Wad) -> Transact:\n        \"\"\"Buy SKR for GEMs.\n\n        Args:\n            amount_in_skr: The amount of SKRs to buy for GEM.\n\n        Returns:\n            A :py:class:`pymaker.Transact` instance, which can be used to trigger the transaction.\n        \"\"\"\n        assert isinstance(amount_in_skr, Wad)\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'join', [amount_in_skr.value])\n\n    def exit(self, amount_in_skr: Wad) -> Transact:\n        \"\"\"Sell SKR for GEMs.\n\n        Args:\n            amount_in_skr: The amount of SKR to sell for GEMs.\n\n        Returns:\n            A :py:class:`pymaker.Transact` instance, which can be used to trigger the transaction.\n        \"\"\"\n        assert isinstance(amount_in_skr, Wad)\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'exit', [amount_in_skr.value])\n\n    #TODO make it return the id of the newly created cup\n    def open(self) -> Transact:\n        \"\"\"Create a new cup.\n\n        Returns:\n            A :py:class:`pymaker.Transact` instance, which can be used to trigger the transaction.\n        \"\"\"\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'open', [])\n\n    def shut(self, cup_id: int) -> Transact:\n        \"\"\"Close a cup.\n\n        Involves calling `wipe()` and `free()` internally in order to clear all remaining SAI debt and free\n        all remaining SKR collateral.\n\n        Args:\n            cup_id: Id of the cup to close.\n\n        Returns:\n            A :py:class:`pymaker.Transact` instance, which can be used to trigger the transaction.\n        \"\"\"\n        assert isinstance(cup_id, int)\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'shut', [int_to_bytes32(cup_id)])\n\n    def lock(self, cup_id: int, amount_in_skr: Wad) -> Transact:\n        \"\"\"Post additional SKR collateral to a cup.\n\n        Args:\n            cup_id: Id of the cup to post the collateral into.\n            amount_in_skr: The amount of collateral to post, in SKR.\n\n        Returns:\n            A :py:class:`pymaker.Transact` instance, which can be used to trigger the transaction.\n        \"\"\"\n        assert isinstance(cup_id, int)\n        assert isinstance(amount_in_skr, Wad)\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'lock',\n                        [int_to_bytes32(cup_id), amount_in_skr.value])\n\n    def free(self, cup_id: int, amount_in_skr: Wad) -> Transact:\n        \"\"\"Remove excess SKR collateral from a cup.\n\n        Args:\n            cup_id: Id of the cup to remove the collateral from.\n            amount_in_skr: The amount of collateral to remove, in SKR.\n\n        Returns:\n            A :py:class:`pymaker.Transact` instance, which can be used to trigger the transaction.\n        \"\"\"\n        assert isinstance(cup_id, int)\n        assert isinstance(amount_in_skr, Wad)\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'free',\n                        [int_to_bytes32(cup_id), amount_in_skr.value])\n\n    def draw(self, cup_id: int, amount_in_sai: Wad) -> Transact:\n        \"\"\"Issue the specified amount of SAI stablecoins.\n\n        Args:\n            cup_id: Id of the cup to issue the SAI from.\n            amount_in_sai: The amount SAI to be issued.\n\n        Returns:\n            A :py:class:`pymaker.Transact` instance, which can be used to trigger the transaction.\n        \"\"\"\n        assert isinstance(cup_id, int)\n        assert isinstance(amount_in_sai, Wad)\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'draw',\n                        [int_to_bytes32(cup_id), amount_in_sai.value])\n\n    def wipe(self, cup_id: int, amount_in_sai: Wad) -> Transact:\n        \"\"\"Repay some portion of existing SAI debt.\n\n        Args:\n            cup_id: Id of the cup to repay the SAI to.\n            amount_in_sai: The amount SAI to be repaid.\n\n        Returns:\n            A :py:class:`pymaker.Transact` instance, which can be used to trigger the transaction.\n        \"\"\"\n        assert isinstance(cup_id, int)\n        assert isinstance(amount_in_sai, Wad)\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'wipe',\n                        [int_to_bytes32(cup_id), amount_in_sai.value])\n\n    def give(self, cup_id: int, new_lad: Address) -> Transact:\n        \"\"\"Transfer ownership of a cup.\n\n        Args:\n            cup_id: Id of the cup to transfer ownership of.\n            new_lad: New owner of the cup.\n\n        Returns:\n            A :py:class:`pymaker.Transact` instance, which can be used to trigger the transaction.\n        \"\"\"\n        assert isinstance(cup_id, int)\n        assert isinstance(new_lad, Address)\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'give',\n                        [int_to_bytes32(cup_id), new_lad.address])\n\n    def bite(self, cup_id: int) -> Transact:\n        \"\"\"Initiate liquidation of an undercollateralized cup.\n\n        Args:\n            cup_id: Id of the cup to liquidate.\n\n        Returns:\n            A :py:class:`pymaker.Transact` instance, which can be used to trigger the transaction.\n        \"\"\"\n        assert isinstance(cup_id, int)\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'bite', [int_to_bytes32(cup_id)])\n\n    def __eq__(self, other):\n        assert(isinstance(other, Tub))\n        return self.address == other.address\n\n    def __repr__(self):\n        return f\"Tub('{self.address}')\"\n\n\nclass Tap(Contract):\n    \"\"\"A client for the `Tap` contract.\n\n    Attributes:\n        web3: An instance of `Web` from `web3.py`.\n        address: Ethereum address of the `Tap` contract.\n    \"\"\"\n\n    abi = Contract._load_abi(__name__, 'abi/SaiTap.abi')\n    bin = Contract._load_bin(__name__, 'abi/SaiTap.bin')\n\n    def __init__(self, web3: Web3, address: Address):\n        assert(isinstance(web3, Web3))\n        assert(isinstance(address, Address))\n\n        self.web3 = web3\n        self.address = address\n        self._contract = self._get_contract(web3, self.abi, address)\n\n    @staticmethod\n    def deploy(web3: Web3, tub: Address):\n        assert(isinstance(tub, Address))\n        return Tap(web3=web3, address=Contract._deploy(web3, Tap.abi, Tap.bin, [tub.address]))\n\n    def set_authority(self, address: Address) -> Transact:\n        assert(isinstance(address, Address))\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'setAuthority', [address.address])\n\n    def approve(self, approval_function):\n        \"\"\"Approve the `Tap` to access our SAI, SKR and GEM balances.\n\n        For available approval functions (i.e. approval modes) see `directly` and `via_tx_manager`\n        in `pymaker.approval`.\n\n        Args:\n            approval_function: Approval function (i.e. approval mode).\n        \"\"\"\n        assert(callable(approval_function))\n\n        tub = Tub(web3=self.web3, address=self.tub())\n\n        approval_function(ERC20Token(web3=self.web3, address=self.sai()), self.address, 'Tap')\n        approval_function(ERC20Token(web3=self.web3, address=self.skr()), self.address, 'Tap')\n        approval_function(ERC20Token(web3=self.web3, address=tub.gem()), self.address, 'Tap')\n\n    def tub(self) -> Address:\n        \"\"\"Get the address of the `Tub` contract.\n\n        Returns:\n            The address of the `Tub` contract.\n        \"\"\"\n        return Address(self._contract.functions.tub().call())\n\n    def sai(self) -> Address:\n        \"\"\"Get the SAI token.\n\n        Returns:\n            The address of the SAI token.\n        \"\"\"\n        return Address(self._contract.functions.sai().call())\n\n    def sin(self) -> Address:\n        \"\"\"Get the SIN token.\n\n        Returns:\n            The address of the SIN token.\n        \"\"\"\n        return Address(self._contract.functions.sin().call())\n\n    def skr(self) -> Address:\n        \"\"\"Get the SKR token.\n\n        Returns:\n            The address of the SKR token.\n        \"\"\"\n        return Address(self._contract.functions.skr().call())\n\n    def woe(self) -> Wad:\n        \"\"\"Get the amount of bad debt.\n\n        Returns:\n            The amount of bad debt in SAI.\n        \"\"\"\n        return Wad(self._contract.functions.woe().call())\n\n    def fog(self) -> Wad:\n        \"\"\"Get the amount of SKR pending liquidation.\n\n        Returns:\n            The amount of SKR pending liquidation, in SKR.\n        \"\"\"\n        return Wad(self._contract.functions.fog().call())\n\n    #TODO beware that it doesn't call drip() underneath so if `tax`>1.0 we won't get an up-to-date value of joy()\n    def joy(self) -> Wad:\n        \"\"\"Get the amount of surplus SAI.\n\n        Surplus SAI can be processed using `boom()`.\n\n        Returns:\n            The amount of surplus SAI accumulated in the Tub.\n        \"\"\"\n        return Wad(self._contract.functions.joy().call())\n\n    def gap(self) -> Wad:\n        \"\"\"Get the current spread for `boom` and `bust`.\n\n        Returns:\n            The current spread for `boom` and `bust`. `1.0` means no spread, `1.01` means 1% spread.\n        \"\"\"\n        return Wad(self._contract.functions.gap().call())\n\n    def mold_gap(self, new_gap: Wad) -> Transact:\n        \"\"\"Update the current spread (`gap`) for `boom` and `bust`.\n\n        Args:\n            new_gap: The new value of the spread (`gap`). `1.0` means no spread, `1.01` means 1% spread.\n\n        Returns:\n            A :py:class:`pymaker.Transact` instance, which can be used to trigger the transaction.\n        \"\"\"\n        assert isinstance(new_gap, Wad)\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'mold', [bytes('gap', 'utf-8'), new_gap.value])\n\n    def s2s(self) -> Ray:\n        \"\"\"Get the current SKR per SAI rate (for `boom` and `bust`).\n\n        Returns:\n            The current SKR per SAI rate.\n        \"\"\"\n        return Ray(self._contract.functions.s2s().call())\n\n    def bid(self, amount_in_skr: Wad) -> Wad:\n        \"\"\"Get the current price of `amount_in_skr` SKR in SAI for `boom`.\n\n        Returns:\n            The amount in SAI which will be received from `boom` in return of\n                `amount_in_skr` SKR.\n        \"\"\"\n        return Wad(self._contract.functions.bid(amount_in_skr.value).call())\n\n    def ask(self, amount_in_skr: Wad) -> Wad:\n        \"\"\"Get the current price of `amount_in_skr` SKR in SAI for `bust`.\n\n        Returns:\n            The amount in SAI which will be consumed by `bust` if we want\n                to receive `amount_in_skr` SKR from it.\n        \"\"\"\n        return Wad(self._contract.functions.ask(amount_in_skr.value).call())\n\n    def boom(self, amount_in_skr: Wad) -> Transact:\n        \"\"\"Buy some amount of SAI to process `joy` (surplus).\n\n        Args:\n            amount_in_skr: The amount of SKR we want to send in order to receive SAI.\n\n        Returns:\n            A :py:class:`pymaker.Transact` instance, which can be used to trigger the transaction.\n        \"\"\"\n        assert isinstance(amount_in_skr, Wad)\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'boom', [amount_in_skr.value])\n\n    def bust(self, amount_in_skr: Wad) -> Transact:\n        \"\"\"Sell some amount of SAI to process `woe` (bad debt).\n\n        Args:\n            amount_in_skr: The amount of SKR we want to receive in exchange for our SAI.\n\n        Returns:\n            A :py:class:`pymaker.Transact` instance, which can be used to trigger the transaction.\n        \"\"\"\n        assert isinstance(amount_in_skr, Wad)\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'bust', [amount_in_skr.value])\n\n    def cash(self, amount_in_sai: Wad) -> Transact:\n        \"\"\"Exchange SAI to GEM after cage.\n\n        Args:\n            amount_in_sai: The amount of SAI to exchange to GEM.\n\n        Returns:\n            A :py:class:`pymaker.Transact` instance, which can be used to trigger the transaction.\n        \"\"\"\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'cash', [amount_in_sai.value])\n\n    def mock(self, amount_in_sai: Wad) -> Transact:\n        \"\"\"Exchange GEM to SAI after cage.\n\n        Args:\n            amount_in_sai: The amount of SAI to buy for GEM.\n\n        Returns:\n            A :py:class:`pymaker.Transact` instance, which can be used to trigger the transaction.\n        \"\"\"\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'mock', [amount_in_sai.value])\n\n    def __eq__(self, other):\n        assert(isinstance(other, Tap))\n        return self.address == other.address\n\n    def __repr__(self):\n        return f\"Tap('{self.address}')\"\n\n\nclass Top(Contract):\n    \"\"\"A client for the `Top` contract.\n\n    Attributes:\n        web3: An instance of `Web` from `web3.py`.\n        address: Ethereum address of the `Top` contract.\n    \"\"\"\n\n    abi = Contract._load_abi(__name__, 'abi/SaiTop.abi')\n    bin = Contract._load_bin(__name__, 'abi/SaiTop.bin')\n\n    def __init__(self, web3: Web3, address: Address):\n        assert(isinstance(web3, Web3))\n        assert(isinstance(address, Address))\n\n        self.web3 = web3\n        self.address = address\n        self._contract = self._get_contract(web3, self.abi, address)\n\n    @staticmethod\n    def deploy(web3: Web3, tub: Address, tap: Address):\n        assert(isinstance(tub, Address))\n        assert(isinstance(tap, Address))\n        return Top(web3=web3, address=Contract._deploy(web3, Top.abi, Top.bin, [tub.address, tap.address]))\n\n    def set_authority(self, address: Address) -> Transact:\n        assert(isinstance(address, Address))\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'setAuthority', [address.address])\n\n    def fix(self) -> Ray:\n        \"\"\"Get the GEM per SAI settlement price.\n\n        Returns:\n            The GEM per SAI settlement (kill) price.\n        \"\"\"\n        return Ray(self._contract.functions.fix().call())\n\n    def cage(self) -> Transact:\n        \"\"\"Force settlement of the system at a current price.\n\n        Returns:\n            A :py:class:`pymaker.Transact` instance, which can be used to trigger the transaction.\n        \"\"\"\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'cage', [])\n\n    # TODO vent\n\n    def __eq__(self, other):\n        assert(isinstance(other, Top))\n        return self.address == other.address\n\n    def __repr__(self):\n        return f\"Top('{self.address}')\"\n\n\nclass Vox(Contract):\n    \"\"\"A client for the `Vox` contract.\n\n    Attributes:\n        web3: An instance of `Web` from `web3.py`.\n        address: Ethereum address of the `Vox` contract.\n    \"\"\"\n\n    abi = Contract._load_abi(__name__, 'abi/SaiVox.abi')\n    bin = Contract._load_bin(__name__, 'abi/SaiVox.bin')\n\n    def __init__(self, web3: Web3, address: Address):\n        assert(isinstance(web3, Web3))\n        assert(isinstance(address, Address))\n\n        self.web3 = web3\n        self.address = address\n        self._contract = self._get_contract(web3, self.abi, address)\n\n    @staticmethod\n    def deploy(web3: Web3, per: Ray):\n        assert(isinstance(per, Ray))\n        return Vox(web3=web3, address=Contract._deploy(web3, Vox.abi, Vox.bin, [per.value]))\n\n    def set_authority(self, address: Address) -> Transact:\n        assert(isinstance(address, Address))\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'setAuthority', [address.address])\n\n    def era(self) -> int:\n        \"\"\"Return the current `Vox` timestamp.\n\n        Returns:\n            Timestamp as a unix timestamp.\n        \"\"\"\n        return self._contract.functions.era().call()\n\n    def par(self) -> Ray:\n        \"\"\"Get the accrued holder fee (REF per SAI).\n\n        Every invocation of this method calls `prod()` internally, so the value you receive is always up-to-date.\n        But as calling it doesn't result in an Ethereum transaction, the actual `_par` value in the smart\n        contract storage does not get updated.\n\n        Returns:\n            The accrued holder fee.\n        \"\"\"\n        return Ray(self._contract.functions.par().call())\n\n    def __eq__(self, other):\n        assert(isinstance(other, Vox))\n        return self.address == other.address\n\n    def __repr__(self):\n        return f\"Vox('{self.address}')\"\n"
  },
  {
    "path": "pymaker/shutdown.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2019 EdNoepel\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nimport logging\nfrom datetime import datetime\nfrom typing import Optional, List\n\nfrom web3 import Web3\n\nfrom pymaker import Address, Contract, Transact\nfrom pymaker.approval import directly, hope_directly\nfrom pymaker.dss import Ilk\nfrom pymaker.numeric import Wad, Ray, Rad\nfrom pymaker.token import DSToken, ERC20Token\n\n\nlogger = logging.getLogger()\n\n\nclass ShutdownModule(Contract):\n    \"\"\"A client for the `ESM` contract, which allows users to call `end.cage()` and thereby trigger a shutdown.\n\n    Ref. <https://github.com/makerdao/esm/blob/master/src/ESM.sol>\n\n    Attributes:\n      web3: An instance of `Web` from `web3.py`.\n      address: Ethereum address of the `ESM` contract.\"\"\"\n\n    abi = Contract._load_abi(__name__, 'abi/ESM.abi')\n    bin = Contract._load_bin(__name__, 'abi/ESM.bin')\n\n    def __init__(self, web3: Web3, address: Address):\n        assert isinstance(web3, Web3)\n        assert isinstance(address, Address)\n\n        self.web3 = web3\n        self.address = address\n        self._contract = self._get_contract(web3, self.abi, address)\n\n    def sum(self) -> Wad:\n        \"\"\"Total balance of MKR `join`ed to this contract\"\"\"\n        return Wad(self._contract.functions.Sum().call())\n\n    def sum_of(self, address: Address) -> Wad:\n        \"\"\"MKR `join`ed to this contract by a specific account\"\"\"\n        assert isinstance(address, Address)\n\n        return Wad(self._contract.functions.sum(address.address).call())\n\n    def min(self) -> Wad:\n        \"\"\"Minimum amount of MKR required to call `fire`\"\"\"\n        return Wad(self._contract.functions.min().call())\n\n    def join(self, value: Wad) -> Transact:\n        \"\"\"Before `fire` can be called, sufficient MKR must be `join`ed to this contract\"\"\"\n        assert isinstance(value, Wad)\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'join', [value.value])\n\n    def fire(self):\n        \"\"\"Calls `cage` on the `end` contract, initiating a shutdown.\"\"\"\n        logger.info(\"Calling fire to cage the end\")\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'fire', [])\n\n    def deny(self, address: Address):\n        \"\"\"Removes the Pause proxy's privileges from address\"\"\"\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'deny', [address.address])\n\n    def burn(self):\n        logger.info(\"Calling burn to burn all the joined MKR\")\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'burn', [])\n\n\nclass End(Contract):\n    \"\"\"A client for the `End` contract, used to orchestrate a shutdown.\n\n    Ref. <https://github.com/makerdao/dss/blob/master/src/end.sol>\n\n    Attributes:\n      web3: An instance of `Web` from `web3.py`.\n      address: Ethereum address of the `ESM` contract.\"\"\"\n\n    abi = Contract._load_abi(__name__, 'abi/End.abi')\n    bin = Contract._load_bin(__name__, 'abi/End.bin')\n\n    def __init__(self, web3: Web3, address: Address):\n        assert isinstance(web3, Web3)\n        assert isinstance(address, Address)\n\n        self.web3 = web3\n        self.address = address\n        self._contract = self._get_contract(web3, self.abi, address)\n\n    def live(self) -> bool:\n        \"\"\"False when caged, true when uncaged\"\"\"\n        return self._contract.functions.live().call() > 0\n\n    def when(self) -> datetime:\n        \"\"\"Time of cage\"\"\"\n        timestamp = self._contract.functions.when().call()\n        return datetime.utcfromtimestamp(timestamp)\n\n    def wait(self) -> int:\n        \"\"\"Processing cooldown length, in seconds\"\"\"\n        return int(self._contract.functions.wait().call())\n\n    def debt(self) -> Rad:\n        \"\"\"total outstanding dai following processing\"\"\"\n        return Rad(self._contract.functions.debt().call())\n\n    def tag(self, ilk: Ilk) -> Ray:\n        \"\"\"Cage price for the collateral\"\"\"\n        assert isinstance(ilk, Ilk)\n        return Ray(self._contract.functions.tag(ilk.toBytes()).call())\n\n    def gap(self, ilk: Ilk) -> Wad:\n        \"\"\"Collateral shortfall (difference of debt and collateral\"\"\"\n        assert isinstance(ilk, Ilk)\n        return Wad(self._contract.functions.gap(ilk.toBytes()).call())\n\n    def art(self, ilk: Ilk) -> Wad:\n        \"\"\"Total debt for the collateral\"\"\"\n        assert isinstance(ilk, Ilk)\n        return Wad(self._contract.functions.Art(ilk.toBytes()).call())\n\n    def fix(self, ilk: Ilk) -> Ray:\n        \"\"\"Final cash price for the collateral\"\"\"\n        assert isinstance(ilk, Ilk)\n        return Ray(self._contract.functions.fix(ilk.toBytes()).call())\n\n    def bag(self, address: Address) -> Wad:\n        \"\"\"Amount of Dai `pack`ed for retrieving collateral in return\"\"\"\n        assert isinstance(address, Address)\n        return Wad(self._contract.functions.bag(address.address).call())\n\n    def out(self, ilk: Ilk, address: Address) -> Wad:\n        assert isinstance(ilk, Ilk)\n        assert isinstance(address, Address)\n        return Wad(self._contract.functions.out(ilk.toBytes(), address.address).call())\n\n    def cage(self, ilk: Ilk) -> Transact:\n        \"\"\"Set the `cage` price for the collateral\"\"\"\n        assert isinstance(ilk, Ilk)\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'cage(bytes32)', [ilk.toBytes()])\n\n    def snip(self, ilk: Ilk, clip_id: int) -> Transact:\n        \"\"\"Cancel a clip auction and seize it's collateral\"\"\"\n        assert isinstance(ilk, Ilk)\n        assert isinstance(clip_id, int)\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'snip', [ilk.toBytes(), clip_id])\n\n    def skip(self, ilk: Ilk, flip_id: int) -> Transact:\n        \"\"\"Cancel a flip auction and seize it's collateral\"\"\"\n        assert isinstance(ilk, Ilk)\n        assert isinstance(flip_id, int)\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'skip', [ilk.toBytes(), flip_id])\n\n    def skim(self, ilk: Ilk, address: Address) -> Transact:\n        \"\"\"Cancels undercollateralized CDP debt to determine collateral shortfall\"\"\"\n        assert isinstance(ilk, Ilk)\n        assert isinstance(address, Address)\n        return Transact(self, self.web3, self.abi, self.address, self._contract,\n                        'skim', [ilk.toBytes(), address.address])\n\n    def free(self, ilk: Ilk) -> Transact:\n        \"\"\"Releases excess collateral after `skim`ming\"\"\"\n        assert isinstance(ilk, Ilk)\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'free', [ilk.toBytes()])\n\n    def thaw(self):\n        \"\"\"Fix the total outstanding supply of Dai\"\"\"\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'thaw', [])\n\n    def flow(self, ilk: Ilk) -> Transact:\n        \"\"\"Calculate the `fix`, the cash price for a given collateral\"\"\"\n        assert isinstance(ilk, Ilk)\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'flow', [ilk.toBytes()])\n\n    def pack(self, dai: Wad) -> Transact:\n        \"\"\"Deposit Dai into the `bag`, from which it cannot be withdrawn\"\"\"\n        assert isinstance(dai, Wad)\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'pack', [dai.value])\n\n    def cash(self, ilk: Ilk, dai: Wad):\n        \"\"\"Exchange an amount of dai (already `pack`ed in the `bag`) for collateral\"\"\"\n        assert isinstance(ilk, Ilk)\n        assert isinstance(dai, Wad)\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'cash', [ilk.toBytes(), dai.value])\n"
  },
  {
    "path": "pymaker/sign.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2017-2018 reverendus\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nimport logging\nimport time\nfrom typing import Tuple\n\nfrom eth_account.messages import defunct_hash_message\nfrom eth_utils import encode_hex\nfrom web3 import Web3\n\nfrom pymaker import Address\nfrom pymaker.keys import _registered_accounts\nfrom pymaker.util import bytes_to_hexstring\n\n\ndef eth_sign(message: bytes, web3: Web3, key=None, in_hexbytes=False, account=None):\n    assert(isinstance(message, bytes))\n    assert(isinstance(web3, Web3))\n\n    local_account = _registered_accounts.get((web3, Address(web3.eth.defaultAccount)))\n\n    if local_account or (account is not None):\n\n        if key is None:\n            pkey = local_account.privateKey\n        else:\n            pkey = key\n\n        start_time = time.time()\n        start_clock = time.process_time()\n        try:\n            if in_hexbytes:\n                message_hash = message\n            else:\n                message_hash = defunct_hash_message(primitive=message)\n            signature = web3.eth.account.signHash(message_hash, private_key=pkey).signature.hex()\n        finally:\n            end_time = time.time()\n            end_clock = time.process_time()\n\n        logging.debug(f\"Local signing took {end_time - start_time:.3f}s time, {end_clock - start_clock:.3f}s clock\")\n\n        return signature\n\n    else:\n        signature = bytes_to_hexstring(web3.manager.request_blocking(\n            \"eth_sign\", [web3.eth.defaultAccount, encode_hex(message)],\n        ))\n\n        # for `EthereumJS TestRPC/v2.2.1/ethereum-js`\n        if signature.endswith(\"00\"):\n            signature = signature[:-2] + \"1b\"\n\n        if signature.endswith(\"01\"):\n            signature = signature[:-2] + \"1c\"\n\n        return signature\n\n\ndef to_vrs(signature: str) -> Tuple[int, bytes, bytes]:\n    assert(isinstance(signature, str))\n    assert(signature.startswith(\"0x\"))\n\n    signature_hex = signature[2:]\n    r = bytes.fromhex(signature_hex[0:64])\n    s = bytes.fromhex(signature_hex[64:128])\n    v = ord(bytes.fromhex(signature_hex[128:130]))\n\n    return v, r, s\n"
  },
  {
    "path": "pymaker/tightly_packed.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2017-2018 reverendus\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nfrom eth_abi.encoding import encode_uint_256, BytesEncoder, AddressEncoder\n\nfrom pymaker import Address\n\n\ndef encode_address(address: Address) -> bytes:\n    return AddressEncoder().encode(address.address)[12:]\n\n\ndef encode_uint256(value: int) -> bytes:\n    return encode_uint_256.encode(value)\n\n\ndef encode_bytes(value: bytes) -> bytes:\n    return BytesEncoder().encode(value)\n"
  },
  {
    "path": "pymaker/token.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2017-2018 reverendus\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nimport json\n\nfrom web3 import Web3\n\nfrom pymaker import Contract, Address, Transact\nfrom pymaker.numeric import Wad\n\n\nclass ERC20Token(Contract):\n    \"\"\"A client for a standard ERC20 token contract.\n\n    Attributes:\n        web3: An instance of `Web` from `web3.py`.\n        address: Ethereum address of the ERC20 token.\n    \"\"\"\n\n    abi = Contract._load_abi(__name__, 'abi/ERC20Token.abi')\n    registry = {}\n\n    def __init__(self, web3: Web3, address: Address):\n        assert(isinstance(web3, Web3))\n        assert(isinstance(address, Address))\n\n        self.web3 = web3\n        self.address = address\n        self._contract = self._get_contract(web3, self.abi, address)\n\n    def name(self) -> str:\n        abi_with_string = json.loads(\"\"\"[{\"constant\":true,\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"}]\"\"\")\n        abi_with_bytes32 = json.loads(\"\"\"[{\"constant\":true,\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"}]\"\"\")\n\n        contract_with_string = self._get_contract(self.web3, abi_with_string, self.address)\n        contract_with_bytes32 = self._get_contract(self.web3, abi_with_bytes32, self.address)\n\n        try:\n            return contract_with_string.functions.name().call()\n        except:\n            return str(contract_with_bytes32.functions.name().call(), \"utf-8\").strip('\\x00')\n\n    def symbol(self) -> str:\n        abi_with_string = json.loads(\"\"\"[{\"constant\":true,\"inputs\":[],\"name\":\"symbol\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"}]\"\"\")\n        abi_with_bytes32 = json.loads(\"\"\"[{\"constant\":true,\"inputs\":[],\"name\":\"symbol\",\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"}]\"\"\")\n\n        contract_with_string = self._get_contract(self.web3, abi_with_string, self.address)\n        contract_with_bytes32 = self._get_contract(self.web3, abi_with_bytes32, self.address)\n\n        try:\n            return contract_with_string.functions.symbol().call()\n        except:\n            return str(contract_with_bytes32.functions.symbol().call(), \"utf-8\").strip('\\x00')\n\n    def total_supply(self) -> Wad:\n        \"\"\"Returns the total supply of the token.\n\n        Returns:\n            The total supply of the token.\n        \"\"\"\n        return Wad(self._contract.functions.totalSupply().call())\n\n    def balance_of(self, address: Address) -> Wad:\n        \"\"\"Returns the token balance of a given address.\n\n        Args:\n            address: The address to check the balance of.\n\n        Returns:\n            The token balance of the address specified.\n        \"\"\"\n        assert(isinstance(address, Address))\n\n        return Wad(self._contract.functions.balanceOf(address.address).call())\n\n    def balance_at_block(self, address: Address, block_identifier: int = 'latest') -> Wad:\n        \"\"\"Returns the token balance of a given address.\n\n        Args:\n            address: The address to check the balance of.\n            block_identifier: block at which to retrieve the balance\n\n        Returns:\n            The token balance of the address specified.\n        \"\"\"\n        assert(isinstance(address, Address))\n        assert(isinstance(block_identifier, int) or block_identifier == 'latest')\n\n        return Wad(self._contract.functions.balanceOf(address.address).call(block_identifier=block_identifier))\n\n    def allowance_of(self, address: Address, payee: Address) -> Wad:\n        \"\"\"Returns the current allowance of a specified `payee` (delegate account).\n\n        Allowance is an ERC20 concept allowing the `payee` (delegate account) to spend a fixed amount of tokens\n        on behalf of the token owner (`address`).\n\n        Args:\n            address: The address to check the allowance for (it's the address the tokens can be spent from).\n            payee: The address of the delegate account (it's the address that can spend the tokens).\n\n        Returns:\n            The allowance of the `payee` specified in regards to the `address`.\n        \"\"\"\n        assert(isinstance(address, Address))\n        assert(isinstance(payee, Address))\n\n        return Wad(self._contract.functions.allowance(address.address, payee.address).call())\n\n    def transfer(self, address: Address, value: Wad) -> Transact:\n        \"\"\"Transfers tokens to a specified address.\n\n        Args:\n            address: Destination address to transfer the tokens to.\n            value: The value of tokens to transfer.\n\n        Returns:\n            A :py:class:`pymaker.Transact` instance, which can be used to trigger the transaction.\n        \"\"\"\n        assert(isinstance(address, Address))\n        assert(isinstance(value, Wad))\n\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'transfer', [address.address, value.value])\n\n    def transfer_from(self, source_address: Address, destination_address: Address, value: Wad) -> Transact:\n        \"\"\"Transfers tokens to a specified address.\n\n        Args:\n            source_address: Source address to transfer the tokens from.\n            destination_address: Destination address to transfer the tokens to.\n            value: The value of tokens to transfer.\n\n        Returns:\n            A :py:class:`pymaker.Transact` instance, which can be used to trigger the transaction.\n        \"\"\"\n        assert(isinstance(source_address, Address))\n        assert(isinstance(destination_address, Address))\n        assert(isinstance(value, Wad))\n\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'transferFrom', [source_address.address,\n                                                                                                  destination_address.address,\n                                                                                                  value.value])\n\n    def approve(self, payee: Address, limit: Wad = Wad(2**256 - 1)) -> Transact:\n        \"\"\"Modifies the current allowance of a specified `payee` (delegate account).\n\n        Allowance is an ERC20 concept allowing the `payee` (delegate account) to spend a fixed amount of tokens\n        (`limit`) on behalf of the token owner.\n\n        If `limit` is omitted, a maximum possible value is granted.\n\n        Args:\n            payee: The address of the delegate account (it's the address that can spend the tokens).\n            limit: The value of the allowance i.e. the value of tokens that the `payee` (delegate account)\n                can spend on behalf of their owner.\n\n        Returns:\n            A :py:class:`pymaker.Transact` instance, which can be used to trigger the transaction.\n        \"\"\"\n        assert(isinstance(payee, Address))\n        assert(isinstance(limit, Wad))\n\n        return Transact(self, self.web3, self.abi, self.address, self._contract,\n                        'approve(address,uint256)', [payee.address, limit.value])\n\n    def __eq__(self, other):\n        return self.address == other.address\n\n    def __repr__(self):\n        return f\"ERC20Token('{self.address}')\"\n\n\nclass DSToken(ERC20Token):\n    \"\"\"A client for the `DSToken` contract.\n\n    You can find the source code of the `DSToken` contract here:\n    <https://github.com/dapphub/ds-token>.\n\n    Attributes:\n        web3: An instance of `Web` from `web3.py`.\n        address: Ethereum address of the `DSToken` contract.\n    \"\"\"\n\n    abi = Contract._load_abi(__name__, 'abi/DSToken.abi')\n    bin = Contract._load_bin(__name__, 'abi/DSToken.bin')\n\n    @staticmethod\n    def deploy(web3: Web3, symbol: str):\n        \"\"\"Deploy a new instance of the `DSToken` contract.\n\n        Args:\n            web3: An instance of `Web` from `web3.py`.\n            symbol: Symbol of the new token.\n\n        Returns:\n            A `DSToken` class instance.\n        \"\"\"\n        assert(isinstance(symbol, str))\n        return DSToken(web3=web3, address=Contract._deploy(web3, DSToken.abi, DSToken.bin, [bytes(symbol, \"utf-8\")]))\n\n    def authority(self) -> Address:\n        \"\"\"Return the current `authority` of a `DSAuth`-ed contract.\n\n        Returns:\n            The address of the current `authority`.\n        \"\"\"\n        return Address(self._contract.functions.authority().call())\n\n    def set_authority(self, address: Address) -> Transact:\n        \"\"\"Set the `authority` of a `DSAuth`-ed contract.\n\n        Args:\n            address: The address of the new `authority`.\n\n        Returns:\n            A :py:class:`pymaker.Transact` instance, which can be used to trigger the transaction.\n        \"\"\"\n        assert(isinstance(address, Address))\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'setAuthority', [address.address])\n\n    def mint(self, amount: Wad) -> Transact:\n        \"\"\"Increase the total supply of the token.\n\n        Args:\n            amount: The amount to increase the total supply by.\n\n        Returns:\n            A :py:class:`pymaker.Transact` instance, which can be used to trigger the transaction.\n        \"\"\"\n        assert(isinstance(amount, Wad))\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'mint(uint256)', [amount.value])\n\n    def mint_to(self, address: Address, amount: Wad) -> Transact:\n        \"\"\"Increase the total supply of the token.\n\n        Args:\n            address: The address to credit the new tokens to.\n            amount: The amount to increase the total supply by.\n\n        Returns:\n            A :py:class:`pymaker.Transact` instance, which can be used to trigger the transaction.\n        \"\"\"\n        assert(isinstance(amount, Wad))\n        assert(isinstance(address, Address))\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'mint(address,uint256)', [address.address,\n                                                                                                           amount.value])\n\n    def burn(self, amount: Wad) -> Transact:\n        \"\"\"Decrease the total supply of the token.\n\n        Args:\n            amount: The amount to decrease the total supply by.\n\n        Returns:\n            A :py:class:`pymaker.Transact` instance, which can be used to trigger the transaction.\n        \"\"\"\n        assert(isinstance(amount, Wad))\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'burn(uint256)', [amount.value])\n\n    def burn_from(self, address: Address, amount: Wad) -> Transact:\n        \"\"\"Decrease the total supply of the token.\n\n        Args:\n            address: The address to burn the tokens from.\n            amount: The amount to decrease the total supply by.\n\n        Returns:\n            A :py:class:`pymaker.Transact` instance, which can be used to trigger the transaction.\n        \"\"\"\n        assert(isinstance(amount, Wad))\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'burn(address,uint256)', [address.address,\n                                                                                                           amount.value])\n\n    def __repr__(self):\n        return f\"DSToken('{self.address}')\"\n\n\nclass DSEthToken(ERC20Token):\n    \"\"\"A client for the `DSEthToken` contract.\n\n    `DSEthToken`, also known as ETH Wrapper or W-ETH, is a contract into which you can deposit\n    raw ETH and then deal with it like with any other ERC20 token. In addition to the `deposit()`\n    and `withdraw()` methods, it implements the standard ERC20 token API.\n\n    You can find the source code of the `DSEthToken` contract here:\n    <https://github.com/dapphub/ds-eth-token>.\n\n    Attributes:\n        web3: An instance of `Web` from `web3.py`.\n        address: Ethereum address of the `DSEthToken` contract.\n    \"\"\"\n\n    abi = Contract._load_abi(__name__, 'abi/DSEthToken.abi')\n    bin = Contract._load_bin(__name__, 'abi/DSEthToken.bin')\n\n    @staticmethod\n    def deploy(web3: Web3):\n        \"\"\"Deploy a new instance of the `DSEthToken` contract.\n\n        Args:\n            web3: An instance of `Web` from `web3.py`.\n\n        Returns:\n            A `DSEthToken` class instance.\n        \"\"\"\n        return DSEthToken(web3=web3, address=Contract._deploy(web3, DSEthToken.abi, DSEthToken.bin, []))\n\n    def __init__(self, web3, address):\n        super().__init__(web3, address)\n        self._contract = self._get_contract(web3, self.abi, address)\n\n    def deposit(self, amount: Wad) -> Transact:\n        \"\"\"Deposits `amount` of raw ETH to `DSEthToken`.\n\n        Args:\n            amount: Amount of raw ETH to be deposited to `DSEthToken`.\n\n        Returns:\n            A :py:class:`pymaker.Transact` instance, which can be used to trigger the transaction.\n        \"\"\"\n        assert(isinstance(amount, Wad))\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'deposit', [], {'value': amount.value})\n\n    def withdraw(self, amount: Wad) -> Transact:\n        \"\"\"Withdraws `amount` of raw ETH from `DSEthToken`.\n\n        The withdrawn ETH will get transferred to the calling account.\n\n        Args:\n            amount: Amount of raw ETH to be withdrawn from `DSEthToken`.\n\n        Returns:\n            A :py:class:`pymaker.Transact` instance, which can be used to trigger the transaction.\n        \"\"\"\n        assert(isinstance(amount, Wad))\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'withdraw', [amount.value])\n\n    def __repr__(self):\n        return f\"DSEthToken('{self.address}')\"\n\n\nclass EthToken():\n    \"\"\"Basic ETH token.\n\n        Attributes:\n         web3: An instance of `Web` from `web3.py`.\n         address: Ethereum address of the original ETH token.\n    \"\"\"\n\n    def __init__(self, web3: Web3, address: Address):\n        assert(isinstance(web3, Web3))\n        assert(isinstance(address, Address))\n\n        self.web3 = web3\n        self.address = address\n\n    def balance_of(self, address):\n        \"\"\"Returns the ETH balance of a given Ethereum address.\n\n         Args:\n             address: The address to check the balance of.\n\n         Returns:\n             The ETH balance of the address specified.\n         \"\"\"\n        assert(isinstance(address, Address))\n\n        return Wad(self.web3.eth.getBalance(address.address))\n"
  },
  {
    "path": "pymaker/transactional.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2017-2018 reverendus\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nimport operator\nfrom functools import reduce\nfrom typing import List\n\nfrom web3 import Web3\n\nfrom pymaker import Contract, Address, Invocation, Transact\nfrom pymaker.token import ERC20Token\n\n\nclass TxManager(Contract):\n    \"\"\"A client for the `TxManager` contract.\n\n    `TxManager` allows to invoke multiple contract methods in one Ethereum transaction.\n    Each invocation is represented as an instance of the `Invocation` class, containing a\n    contract address and a calldata.\n\n    In addition to that, these invocations can use ERC20 token balances. In order to do that,\n    the entire allowance of each token involved is transferred from the caller to the `TxManager`\n    contract at the beginning of the transaction and all the remaining balances are returned\n    to the caller at the end of it. In order to use this feature, ERC20 token allowances\n    have to be granted to the `TxManager`.\n\n    You can find the source code of the `TxManager` contract here:\n    <https://github.com/makerdao/tx-manager>.\n\n    Attributes:\n        web3: An instance of `Web` from `web3.py`.\n        address: Ethereum address of the `TxManager` contract.\n    \"\"\"\n\n    abi = Contract._load_abi(__name__, 'abi/TxManager.abi')\n    bin = Contract._load_bin(__name__, 'abi/TxManager.bin')\n\n    def __init__(self, web3: Web3, address: Address):\n        assert(isinstance(web3, Web3))\n        assert(isinstance(address, Address))\n\n        self.web3 = web3\n        self.address = address\n        self._contract = self._get_contract(web3, self.abi, address)\n\n    @staticmethod\n    def deploy(web3: Web3):\n        return TxManager(web3=web3, address=Contract._deploy(web3, TxManager.abi, TxManager.bin, []))\n\n    def approve(self, tokens: List[ERC20Token], approval_function):\n        \"\"\"Approve the TxManager contract to fully access balances of specified tokens.\n\n        For available approval functions (i.e. approval modes) see `directly` and `via_tx_manager`\n        in `pymaker.approval`.\n\n        Args:\n            tokens: List of :py:class:`pymaker.token.ERC20Token` class instances.\n            approval_function: Approval function (i.e. approval mode).\n        \"\"\"\n        assert(isinstance(tokens, list))\n        assert(callable(approval_function))\n\n        for token in tokens:\n            approval_function(token, self.address, 'TxManager')\n\n    def owner(self) -> Address:\n        return Address(self._contract.functions.owner().call())\n\n    def execute(self, tokens: List[Address], invocations: List[Invocation]) -> Transact:\n        \"\"\"Executes multiple contract methods in one Ethereum transaction.\n\n        Args:\n            tokens: List of addresses of ERC20 token the invocations should be able to access.\n            invocations: A list of invocations (contract methods) to be executed.\n\n        Returns:\n            A :py:class:`pymaker.Transact` instance, which can be used to trigger the transaction.\n        \"\"\"\n        def token_addresses() -> list:\n            return list(map(lambda address: address.address, tokens))\n\n        def script() -> bytes:\n            return reduce(operator.add, map(lambda invocation: script_entry(invocation), invocations), bytes())\n\n        def script_entry(invocation: Invocation) -> bytes:\n            address = invocation.address.as_bytes()\n            calldata = invocation.calldata.as_bytes()\n            calldata_length = len(calldata).to_bytes(32, byteorder='big')\n            return address + calldata_length + calldata\n\n        assert(isinstance(tokens, list))\n        assert(isinstance(invocations, list))\n\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'execute', [token_addresses(), script()])\n\n    def __repr__(self):\n        return f\"TxManager('{self.address}')\"\n"
  },
  {
    "path": "pymaker/util.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2017-2018 reverendus\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nimport asyncio\nimport logging\nimport threading\n\nfrom web3 import Web3\n\nfrom pymaker.numeric import Wad\n\n\ndef chain(web3: Web3) -> str:\n    block_0 = web3.eth.getBlock(0)['hash']\n    if block_0 == \"0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3\":\n        return \"ethlive\"\n    elif block_0 == \"0xa3c565fc15c7478862d50ccd6561e3c06b24cc509bf388941c25ea985ce32cb9\":\n        return \"kovan\"\n    elif block_0 == \"0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d\":\n        return \"ropsten\"\n    elif block_0 == \"0x0cd786a2425d16f152c658316c423e6ce1181e15c3295826d7c9904cba9ce303\":\n        return \"morden\"\n    else:\n        return \"unknown\"\n\n\ndef http_response_summary(response) -> str:\n    text = response.text.replace('\\r', '').replace('\\n', '')[:2048]\n    return f\"{response.status_code} {response.reason} ({text})\"\n\n\n# CAUTION: Used by Transact class, this breaks applications running their own asyncio event loop.\ndef synchronize(futures) -> list:\n    if len(futures) > 0:\n        loop = asyncio.new_event_loop()\n        try:\n            return loop.run_until_complete(asyncio.gather(*futures, loop=loop))\n        finally:\n            loop.close()\n    else:\n        return []\n\n\ndef eth_balance(web3: Web3, address) -> Wad:\n    return Wad(web3.eth.getBalance(address.address))\n\n\ndef is_contract_at(web3: Web3, address):\n    code = web3.eth.getCode(address.address)\n    return (code is not None) and (code != \"0x\") and (code != \"0x0\") and (code != b\"\\x00\") and (code != b\"\")\n\n\ndef int_to_bytes32(value: int) -> bytes:\n    assert(isinstance(value, int))\n    return value.to_bytes(32, byteorder='big')\n\n\ndef bytes_to_int(value) -> int:\n    if isinstance(value, bytes) or isinstance(value, bytearray):\n        return int.from_bytes(value, byteorder='big')\n    elif isinstance(value, str):\n        b = bytearray()\n        b.extend(map(ord, value))\n        return int.from_bytes(b, byteorder='big')\n    else:\n        raise AssertionError\n\n\ndef bytes_to_hexstring(value) -> str:\n    if isinstance(value, bytes) or isinstance(value, bytearray):\n        return \"0x\" + \"\".join(map(lambda b: format(b, \"02x\"), value))\n    elif isinstance(value, str):\n        b = bytearray()\n        b.extend(map(ord, value))\n        return \"0x\" + \"\".join(map(lambda b: format(b, \"02x\"), b))\n    else:\n        raise AssertionError\n\n\ndef hexstring_to_bytes(value: str) -> bytes:\n    assert(isinstance(value, str))\n    assert(value.startswith(\"0x\"))\n    return Web3.toBytes(hexstr=value)\n\n\nclass AsyncCallback:\n    \"\"\"Decouples callback invocation from the web3.py filter.\n\n    Decouples callback invocation from the web3.py filter by executing the callback\n    in a dedicated thread. If we make web3.py trigger the callback directly, and the callback\n    execution takes more than 60 seconds, the `eth_getFilterChanges` call also will not\n    get called for 60 seconds and more which will make the filter expire in Parity side.\n    It's 60 seconds for Parity, this could be a different value for other nodes,\n    but the filter will eventually expire sooner or later anyway.\n\n    Invoking the callback logic in a separate thread allows the web3.py Filter thread\n    to keep calling `eth_getFilterChanges` regularly, so the filter stays active.\n\n    Attributes:\n        callback: The callback function to be invoked in a separate thread.\n    \"\"\"\n    def __init__(self, callback):\n        self.callback = callback\n        self.thread = None\n\n    def trigger(self, on_start=None, on_finish=None) -> bool:\n        \"\"\"Invokes the callback in a separate thread, unless one is already running.\n\n        If callback isn't currently running, invokes it in a separate thread and returns `True`.\n        If the previous callback invocation still hasn't finished, doesn't do anything\n        and returns `False`.\n\n        Arguments:\n            on_start: Optional method to be called before the actual callback. Can be `None`.\n            on_finish: Optional method to be called after the actual callback. Can be `None`.\n\n        Returns:\n            `True` if callback has been invoked, or if it invocation attempt failed.\n            `False` if the previous callback invocation still hasn't finished.\n        \"\"\"\n        if self.thread is None or not self.thread.is_alive():\n            def thread_target():\n                if on_start is not None:\n                    on_start()\n                self.callback()\n                if on_finish is not None:\n                    on_finish()\n\n            self.thread = threading.Thread(target=thread_target)\n\n            try:\n                self.thread.start()\n            except Exception as e:\n                self.thread = None\n\n                logging.critical(f\"Failed to start the async callback thread ({e})\")\n\n            return True\n        else:\n            return False\n\n    def wait(self):\n        \"\"\"Waits for the currently running callback to finish.\n\n        If the callback isn't running or hasn't even been invoked once, returns instantly.\"\"\"\n        if self.thread is not None:\n            self.thread.join()\n"
  },
  {
    "path": "pymaker/vault.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2017-2018 reverendus\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nfrom web3 import Web3\n\nfrom pymaker import Contract, Address, Transact\n\n\nclass DSVault(Contract):\n    \"\"\"A client for the `DSVault` contract.\n\n    You can find the source code of the `DSVault` contract here:\n    <https://github.com/dapphub/ds-vault>.\n\n    Attributes:\n        web3: An instance of `Web` from `web3.py`.\n        address: Ethereum address of the `DSVault` contract.\n    \"\"\"\n\n    abi = Contract._load_abi(__name__, 'abi/DSVault.abi')\n    bin = Contract._load_bin(__name__, 'abi/DSVault.bin')\n\n    def __init__(self, web3: Web3, address: Address):\n        assert(isinstance(web3, Web3))\n        assert(isinstance(address, Address))\n\n        self.web3 = web3\n        self.address = address\n        self._contract = self._get_contract(web3, self.abi, address)\n\n    @staticmethod\n    def deploy(web3: Web3):\n        \"\"\"Deploy a new instance of the `DSVault` contract.\n\n        Args:\n            web3: An instance of `Web` from `web3.py`.\n\n        Returns:\n            A `DSVault` class instance.\n        \"\"\"\n        return DSVault(web3=web3, address=Contract._deploy(web3, DSVault.abi, DSVault.bin, []))\n\n    def authority(self) -> Address:\n        \"\"\"Return the current `authority` of a `DSAuth`-ed contract.\n\n        Returns:\n            The address of the current `authority`.\n        \"\"\"\n        return Address(self._contract.functions.authority().call())\n\n    def set_authority(self, address: Address) -> Transact:\n        \"\"\"Set the `authority` of a `DSAuth`-ed contract.\n\n        Args:\n            address: The address of the new `authority`.\n\n        Returns:\n            A :py:class:`pymaker.Transact` instance, which can be used to trigger the transaction.\n        \"\"\"\n        assert(isinstance(address, Address))\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'setAuthority', [address.address])\n\n    def __repr__(self):\n        return f\"DSVault('{self.address}')\"\n"
  },
  {
    "path": "pymaker/zrx.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2017-2018 reverendus\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nimport array\nimport copy\nimport logging\nimport random\nfrom pprint import pformat\nfrom typing import List, Optional\n\nimport requests\nfrom hexbytes import HexBytes\nfrom web3 import Web3\nfrom web3._utils.events import get_event_data\n\nfrom eth_abi.codec import ABICodec\nfrom eth_abi.registry import registry as default_registry\n\nfrom pymaker import Contract, Address, Transact\nfrom pymaker.numeric import Wad\nfrom pymaker.sign import eth_sign, to_vrs\nfrom pymaker.token import ERC20Token\nfrom pymaker.util import bytes_to_hexstring, hexstring_to_bytes, http_response_summary\n\n\nclass Order:\n    def __init__(self, exchange, maker: Address, taker: Address, maker_fee: Wad, taker_fee: Wad, pay_token: Address,\n                 pay_amount: Wad, buy_token: Address, buy_amount: Wad, salt: int, fee_recipient: Address,\n                 expiration: int, exchange_contract_address: Address, ec_signature_r: Optional[str],\n                 ec_signature_s: Optional[str], ec_signature_v: Optional[int]):\n\n        assert(isinstance(maker, Address))\n        assert(isinstance(taker, Address))\n        assert(isinstance(maker_fee, Wad))\n        assert(isinstance(taker_fee, Wad))\n        assert(isinstance(pay_token, Address))\n        assert(isinstance(pay_amount, Wad))\n        assert(isinstance(buy_token, Address))\n        assert(isinstance(buy_amount, Wad))\n        assert(isinstance(salt, int))\n        assert(isinstance(fee_recipient, Address))\n        assert(isinstance(expiration, int))\n        assert(isinstance(exchange_contract_address, Address))\n        assert((isinstance(ec_signature_r, str) and isinstance(ec_signature_s, str) and isinstance(ec_signature_v, int))\n               or (ec_signature_r is None and ec_signature_s is None and ec_signature_v is None))\n\n        self._exchange = exchange\n        self.maker = maker\n        self.taker = taker\n        self.maker_fee = maker_fee\n        self.taker_fee = taker_fee\n        self.pay_token = pay_token\n        self.pay_amount = pay_amount\n        self.buy_token = buy_token\n        self.buy_amount = buy_amount\n        self.salt = salt\n        self.fee_recipient = fee_recipient\n        self.expiration = expiration\n        self.exchange_contract_address = exchange_contract_address\n        self.ec_signature_r = ec_signature_r\n        self.ec_signature_s = ec_signature_s\n        self.ec_signature_v = ec_signature_v\n\n    # this is not a proper 0x order_id, it's just so `OrderBookManager` can uniquely identify orders\n    @property\n    def order_id(self):\n        return hash(self)\n\n    @property\n    def sell_to_buy_price(self) -> Wad:\n        return self.pay_amount / self.buy_amount\n\n    @property\n    def buy_to_sell_price(self) -> Wad:\n        return self.buy_amount / self.pay_amount\n\n    @property\n    def remaining_buy_amount(self) -> Wad:\n        return Wad.max(self.buy_amount - self._exchange.get_unavailable_buy_amount(self), Wad(0))\n\n    @property\n    def remaining_sell_amount(self) -> Wad:\n        unavailable_buy_amount = self._exchange.get_unavailable_buy_amount(self)\n\n        if unavailable_buy_amount >= self.buy_amount:\n            return Wad(0)\n\n        else:\n            return Wad.max(self.pay_amount - (unavailable_buy_amount * self.pay_amount / self.buy_amount), Wad(0))\n\n    @staticmethod\n    def from_json(exchange, data: dict):\n        assert(isinstance(data, dict))\n\n        return Order(exchange=exchange,\n                     maker=Address(data['maker']),\n                     taker=Address(data['taker']),\n                     maker_fee=Wad(int(data['makerFee'])),\n                     taker_fee=Wad(int(data['takerFee'])),\n                     pay_token=Address(data['makerTokenAddress']),\n                     pay_amount=Wad(int(data['makerTokenAmount'])),\n                     buy_token=Address(data['takerTokenAddress']),\n                     buy_amount=Wad(int(data['takerTokenAmount'])),\n                     salt=int(data['salt']),\n                     fee_recipient=Address(data['feeRecipient']),\n                     expiration=int(data['expirationUnixTimestampSec']),\n                     exchange_contract_address=Address(data['exchangeContractAddress']),\n                     ec_signature_r=data['ecSignature']['r'] if 'ecSignature' in data else None,\n                     ec_signature_s=data['ecSignature']['s'] if 'ecSignature' in data else None,\n                     ec_signature_v=data['ecSignature']['v'] if 'ecSignature' in data else None)\n\n    def to_json_without_fees(self) -> dict:\n        return {\n            \"exchangeContractAddress\": self.exchange_contract_address.address.lower(),\n            \"maker\": self.maker.address.lower(),\n            \"taker\": self.taker.address.lower(),\n            \"makerTokenAddress\": self.pay_token.address.lower(),\n            \"takerTokenAddress\": self.buy_token.address.lower(),\n            \"makerTokenAmount\": str(self.pay_amount.value),\n            \"takerTokenAmount\": str(self.buy_amount.value),\n            \"expirationUnixTimestampSec\": str(self.expiration),\n            \"salt\": str(self.salt)\n        }\n\n    def to_json(self) -> dict:\n        return {\n            \"exchangeContractAddress\": self.exchange_contract_address.address.lower(),\n            \"maker\": self.maker.address.lower(),\n            \"taker\": self.taker.address.lower(),\n            \"makerTokenAddress\": self.pay_token.address.lower(),\n            \"takerTokenAddress\": self.buy_token.address.lower(),\n            \"feeRecipient\": self.fee_recipient.address.lower(),\n            \"makerTokenAmount\": str(self.pay_amount.value),\n            \"takerTokenAmount\": str(self.buy_amount.value),\n            \"makerFee\": str(self.maker_fee.value),\n            \"takerFee\": str(self.taker_fee.value),\n            \"expirationUnixTimestampSec\": str(self.expiration),\n            \"salt\": str(self.salt),\n            \"ecSignature\": {\n                \"r\": self.ec_signature_r,\n                \"s\": self.ec_signature_s,\n                \"v\": self.ec_signature_v\n            }\n        }\n\n    def __eq__(self, other):\n        assert(isinstance(other, Order))\n        return self.maker == other.maker and \\\n               self.taker == other.taker and \\\n               self.maker_fee == other.maker_fee and \\\n               self.taker_fee == other.taker_fee and \\\n               self.pay_token == other.pay_token and \\\n               self.pay_amount == other.pay_amount and \\\n               self.buy_token == other.buy_token and \\\n               self.buy_amount == other.buy_amount and \\\n               self.salt == other.salt and \\\n               self.fee_recipient == other.fee_recipient and \\\n               self.expiration == other.expiration and \\\n               self.exchange_contract_address == other.exchange_contract_address and \\\n               self.ec_signature_r == other.ec_signature_r and \\\n               self.ec_signature_s == other.ec_signature_s and \\\n               self.ec_signature_v == other.ec_signature_v\n\n    def __hash__(self):\n        return hash((self.maker,\n                     self.taker,\n                     self.maker_fee,\n                     self.taker_fee,\n                     self.pay_token,\n                     self.pay_amount,\n                     self.buy_token,\n                     self.buy_amount,\n                     self.salt,\n                     self.fee_recipient,\n                     self.expiration,\n                     self.exchange_contract_address,\n                     self.ec_signature_r,\n                     self.ec_signature_s,\n                     self.ec_signature_v))\n\n    def __str__(self):\n        return f\"('{self.buy_token}', '{self.buy_amount}',\" \\\n               f\" '{self.pay_token}', '{self.pay_amount}',\" \\\n               f\" '{self.exchange_contract_address}', '{self.salt}')\"\n\n    def __repr__(self):\n        return pformat(vars(self))\n\n\nclass LogCancel:\n    def __init__(self, log):\n        self.maker = Address(log['args']['maker'])\n        self.fee_recipient = Address(log['args']['feeRecipient'])\n        self.pay_token = Address(log['args']['makerToken'])\n        self.buy_token = Address(log['args']['takerToken'])\n        self.cancelled_pay_amount = Wad(int(log['args']['cancelledMakerTokenAmount']))\n        self.cancelled_buy_amount = Wad(int(log['args']['cancelledTakerTokenAmount']))\n        self.tokens = bytes_to_hexstring(log['args']['tokens'])\n        self.order_hash = bytes_to_hexstring(log['args']['orderHash'])\n        self.raw = log\n\n    def __repr__(self):\n        return pformat(vars(self))\n\n\nclass LogFill:\n    def __init__(self, log):\n        self.maker = Address(log['args']['maker'])\n        self.taker = Address(log['args']['taker'])\n        self.fee_recipient = Address(log['args']['feeRecipient'])\n        self.pay_token = Address(log['args']['makerToken'])\n        self.buy_token = Address(log['args']['takerToken'])\n        self.filled_pay_amount = Wad(int(log['args']['filledMakerTokenAmount']))\n        self.filled_buy_amount = Wad(int(log['args']['filledTakerTokenAmount']))\n        self.paid_maker_fee = Wad(int(log['args']['paidMakerFee']))\n        self.paid_taker_fee = Wad(int(log['args']['paidTakerFee']))\n        self.tokens = bytes_to_hexstring(log['args']['tokens'])\n        self.order_hash = bytes_to_hexstring(log['args']['orderHash'])\n        self.raw = log\n\n    @classmethod\n    def from_event(cls, event: dict):\n        assert(isinstance(event, dict))\n        codec = ABICodec(default_registry)\n        topics = event.get('topics')\n        if topics and topics[0] == HexBytes('0x0bcc4c97732e47d9946f229edb95f5b6323f601300e4690de719993f3c371129'):\n            log_fill_abi = [abi for abi in ZrxExchange.abi if abi.get('name') == 'LogFill'][0]\n            event_data = get_event_data(codec, log_fill_abi, event)\n\n            return LogFill(event_data)\n\n    def __eq__(self, other):\n        assert(isinstance(other, LogFill))\n        return self.__dict__ == other.__dict__\n\n    def __repr__(self):\n        return pformat(vars(self))\n\n\nclass ZrxExchange(Contract):\n    \"\"\"A client for the 0x exchange contract.\n\n    You can find the source code of the `0x` exchange contract here:\n    <https://etherscan.io/address/0x12459c951127e0c374ff9105dda097662a027093#code>.\n\n    Attributes:\n        web3: An instance of `Web` from `web3.py`.\n        address: Ethereum address of the _0x_ `Exchange` contract.\n    \"\"\"\n\n    abi = Contract._load_abi(__name__, 'abi/Exchange.abi')\n    bin = Contract._load_bin(__name__, 'abi/Exchange.bin')\n\n    _ZERO_ADDRESS = Address(\"0x0000000000000000000000000000000000000000\")\n\n    @staticmethod\n    def deploy(web3: Web3, zrx_token: Address, token_transfer_proxy: Address):\n        \"\"\"Deploy a new instance of the 0x `Exchange` contract.\n\n        Args:\n            web3: An instance of `Web` from `web3.py`.\n            zrx_token: The address of the ZRX token this exchange will use.\n            token_transfer_proxy: The address of the token transfer proxy this exchange will use.\n\n        Returns:\n            A `ZrxExchange` class instance.\n        \"\"\"\n        return ZrxExchange(web3=web3,\n                           address=Contract._deploy(web3, ZrxExchange.abi, ZrxExchange.bin, [\n                              zrx_token.address,\n                              token_transfer_proxy.address\n                          ]))\n\n    def __init__(self, web3: Web3, address: Address):\n        assert(isinstance(web3, Web3))\n        assert(isinstance(address, Address))\n\n        self.web3 = web3\n        self.address = address\n        self._contract = self._get_contract(web3, self.abi, address)\n\n    def zrx_token(self) -> Address:\n        \"\"\"Get the address of the ZRX token contract associated with this `Exchange` contract.\n\n        Returns:\n            The address of the `ZRX` token.\n        \"\"\"\n        return Address(self._contract.functions.ZRX_TOKEN_CONTRACT().call())\n\n    def token_transfer_proxy(self) -> Address:\n        \"\"\"Get the address of the `TokenTransferProxy` contract associated with this `Exchange` contract.\n\n        Returns:\n            The address of the `TokenTransferProxy` token.\n        \"\"\"\n        return Address(self._contract.functions.TOKEN_TRANSFER_PROXY_CONTRACT().call())\n\n    def approve(self, tokens: List[ERC20Token], approval_function):\n        \"\"\"Approve the 0x Exchange TokenTransferProxy contract to fully access balances of specified tokens.\n\n        In case of 0x, it's the TokenTransferProxy contract that actually gets the approvals,\n        not the 0x Exchange contract itself. In addition to the tokens specified as the `tokens`\n        parameter, the ZRX token always gets approved as well as without it the 0x Exchange\n        contract wouldn't be able to charge maker and taker fees.\n\n        For available approval functions (i.e. approval modes) see `directly` and `via_tx_manager`\n        in `pymaker.approval`.\n\n        Args:\n            tokens: List of :py:class:`pymaker.token.ERC20Token` class instances.\n            approval_function: Approval function (i.e. approval mode).\n        \"\"\"\n        assert(isinstance(tokens, list))\n        assert(callable(approval_function))\n\n        for token in tokens + [ERC20Token(web3=self.web3, address=self.zrx_token())]:\n            approval_function(token, self.token_transfer_proxy(), '0x Exchange contract')\n\n    def past_fill(self, number_of_past_blocks: int, event_filter: dict = None) -> List[LogFill]:\n        \"\"\"Synchronously retrieve past LogFill events.\n\n        `LogFill` events are emitted by the 0x contract every time someone fills an order.\n\n        Args:\n            number_of_past_blocks: Number of past Ethereum blocks to retrieve the events from.\n            event_filter: Filter which will be applied to returned events.\n\n        Returns:\n            List of past `LogFill` events represented as :py:class:`pymaker.zrx.LogFill` class.\n        \"\"\"\n        assert(isinstance(number_of_past_blocks, int))\n        assert(isinstance(event_filter, dict) or (event_filter is None))\n\n        return self._past_events(self._contract, 'LogFill', LogFill, number_of_past_blocks, event_filter)\n\n    def past_cancel(self, number_of_past_blocks: int, event_filter: dict = None) -> List[LogCancel]:\n        \"\"\"Synchronously retrieve past LogCancel events.\n\n        `LogCancel` events are emitted by the 0x contract every time someone cancels an order.\n\n        Args:\n            number_of_past_blocks: Number of past Ethereum blocks to retrieve the events from.\n            event_filter: Filter which will be applied to returned events.\n\n        Returns:\n            List of past `LogCancel` events represented as :py:class:`pymaker.zrx.LogCancel` class.\n        \"\"\"\n        assert(isinstance(number_of_past_blocks, int))\n        assert(isinstance(event_filter, dict) or (event_filter is None))\n\n        return self._past_events(self._contract, 'LogCancel', LogCancel, number_of_past_blocks, event_filter)\n\n    def create_order(self,\n                     pay_token: Address,\n                     pay_amount: Wad,\n                     buy_token: Address,\n                     buy_amount: Wad,\n                     expiration: int) -> Order:\n        \"\"\"Creates a new order.\n\n        The `maker_fee`, `taker_fee` and `fee_recipient` fields are by default set to zero.\n        Before signing the order and submitting it to the relayer, they may need to be\n        populated using the `calculate_fees()` method of the `ZrxRelayerApi` class.\n\n        Args:\n            pay_token: Address of the ERC20 token you want to put on sale.\n            pay_amount: Amount of the `pay_token` token you want to put on sale.\n            buy_token: Address of the ERC20 token you want to be paid with.\n            buy_amount: Amount of the `buy_token` you want to receive.\n            expiration: Unix timestamp (in seconds) when the order will expire.\n\n        Returns:\n            New order as an instance of the :py:class:`pymaker.zrx.Order` class.\n        \"\"\"\n        assert(isinstance(pay_token, Address))\n        assert(isinstance(pay_amount, Wad))\n        assert(isinstance(buy_token, Address))\n        assert(isinstance(buy_amount, Wad))\n        assert(isinstance(expiration, int))\n\n        return Order(exchange=self,\n                     maker=Address(self.web3.eth.defaultAccount),\n                     taker=self._ZERO_ADDRESS,\n                     maker_fee=Wad(0),\n                     taker_fee=Wad(0),\n                     pay_token=pay_token,\n                     pay_amount=pay_amount,\n                     buy_token=buy_token,\n                     buy_amount=buy_amount,\n                     salt=self.generate_salt(),\n                     fee_recipient=self._ZERO_ADDRESS,\n                     expiration=expiration,\n                     exchange_contract_address=self.address,\n                     ec_signature_r=None,\n                     ec_signature_s=None,\n                     ec_signature_v=None)\n\n    def get_order_hash(self, order: Order) -> str:\n        \"\"\"Calculates hash of an order.\n\n        Args:\n            order: Order you want to calculate the hash of.\n\n        Returns:\n            Order hash as a hex string starting with `0x`.\n        \"\"\"\n        assert(isinstance(order, Order))\n\n        # the hash depends on the exchange contract address as well\n        assert(order.exchange_contract_address == self.address)\n\n        result = self._contract.functions.getOrderHash(self._order_addresses(order), self._order_values(order)).call()\n        return bytes_to_hexstring(result)\n\n    def get_unavailable_buy_amount(self, order: Order) -> Wad:\n        \"\"\"Return the order amount which was either taken or cancelled.\n\n        Args:\n            order: Order you want to get the unavailable amount of.\n\n        Returns:\n            The unavailable amount of the order (i.e. the amount which was either taken or cancelled),\n            expressed in terms of the `buy_token` token.\n        \"\"\"\n        assert(isinstance(order, Order))\n\n        return Wad(self._contract.functions.getUnavailableTakerTokenAmount(\n            hexstring_to_bytes(self.get_order_hash(order))).call())\n\n    def sign_order(self, order: Order) -> Order:\n        \"\"\"Signs an order so it can be submitted to the relayer.\n\n        Order will be signed by the `web3.eth.defaultAccount` account.\n\n        Args:\n            order: Order you want to sign.\n\n        Returns:\n            Signed order. Copy of the order passed as a parameter with the `ec_signature_r`, `ec_signature_s`\n            and `ec_signature_v` fields filled with signature values.\n        \"\"\"\n        assert(isinstance(order, Order))\n\n        signature = eth_sign(hexstring_to_bytes(self.get_order_hash(order)), self.web3)\n        v, r, s = to_vrs(signature)\n\n        signed_order = copy.copy(order)\n        signed_order.ec_signature_r = bytes_to_hexstring(r)\n        signed_order.ec_signature_s = bytes_to_hexstring(s)\n        signed_order.ec_signature_v = v\n        return signed_order\n\n    def fill_order(self, order: Order, fill_buy_amount: Wad) -> Transact:\n        \"\"\"Fills an order.\n\n        Args:\n            order: The order to be filled.\n            fill_buy_amount: The amount (in terms of `buy_token` of the original order) to be filled.\n\n        Returns:\n            A :py:class:`pymaker.Transact` instance, which can be used to trigger the transaction.\n        \"\"\"\n        assert(isinstance(order, Order))\n        assert(isinstance(fill_buy_amount, Wad))\n\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'fillOrder',\n                        [self._order_addresses(order), self._order_values(order), fill_buy_amount.value,\n                         True, order.ec_signature_v,\n                         hexstring_to_bytes(order.ec_signature_r),\n                         hexstring_to_bytes(order.ec_signature_s)])\n\n    def cancel_order(self, order: Order) -> Transact:\n        \"\"\"Cancels an order.\n\n        Args:\n            order: Order you want to cancel.\n\n        Returns:\n            A :py:class:`pymaker.Transact` instance, which can be used to trigger the transaction.\n        \"\"\"\n        assert(isinstance(order, Order))\n\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'cancelOrder',\n                        [self._order_addresses(order), self._order_values(order), order.buy_amount.value])\n\n    @staticmethod\n    def _order_values(order):\n        return [order.pay_amount.value,\n                order.buy_amount.value,\n                order.maker_fee.value,\n                order.taker_fee.value,\n                order.expiration,\n                order.salt]\n\n    @staticmethod\n    def _order_addresses(order):\n        return [order.maker.address,\n                order.taker.address,\n                order.pay_token.address,\n                order.buy_token.address,\n                order.fee_recipient.address]\n\n    @staticmethod\n    def generate_salt() -> int:\n        return random.randint(1, 2**256 - 1)\n\n    def __repr__(self):\n        return f\"ZrxExchange('{self.address}')\"\n\n\nclass ZrxRelayerApi:\n    \"\"\"A client for the Standard 0x Relayer API V0.\n\n    <https://github.com/0xProject/standard-relayer-api>\n\n    Attributes:\n        exchange: The 0x Exchange contract.\n        api_server: Base URL of the Standard Relayer API server.\n    \"\"\"\n    logger = logging.getLogger()\n    timeout = 15.5\n\n    def __init__(self, exchange: ZrxExchange, api_server: str):\n        assert(isinstance(exchange, ZrxExchange))\n        assert(isinstance(api_server, str))\n\n        self.exchange = exchange\n        self.api_server = api_server\n\n    def get_orders(self, pay_token: Address, buy_token: Address, per_page: int = 100) -> List[Order]:\n        \"\"\"Returns active orders filtered by token pair (one side).\n\n        In order to get them, issues a `/v0/orders` call to the Standard Relayer API.\n\n        Args:\n            per_page: Maximum number of orders to be downloaded per page. 0x Standard Relayer API\n                limitation is 100, but some relayers can handle more so that's why this parameter\n                is exposed.\n\n        Returns:\n            Orders, as a list of instances of the :py:class:`pymaker.zrx.Order` class.\n        \"\"\"\n        assert(isinstance(pay_token, Address))\n        assert(isinstance(buy_token, Address))\n\n        url = f\"{self.api_server}/v0/orders?\" \\\n              f\"exchangeContractAddress={str(self.exchange.address.address).lower()}&\" \\\n              f\"makerTokenAddress={str(pay_token.address).lower()}&\" \\\n              f\"takerTokenAddress={str(buy_token.address).lower()}&\" \\\n              f\"per_page={per_page}\"\n\n        response = requests.get(url, timeout=self.timeout)\n        if not response.ok:\n            raise Exception(f\"Failed to fetch 0x orders from the relayer: {http_response_summary(response)}\")\n\n        return list(map(lambda item: Order.from_json(self.exchange, item), response.json()))\n\n    def get_orders_by_maker(self, maker: Address, per_page: int = 100) -> List[Order]:\n        \"\"\"Returns all active orders created by `maker`.\n\n        In order to get them, issues a `/v0/orders` call to the Standard Relayer API.\n\n        Args:\n            maker: Address of the `maker` to filter the orders by.\n            per_page: Maximum number of orders to be downloaded per page. 0x Standard Relayer API\n                limitation is 100, but some relayers can handle more so that's why this parameter\n                is exposed.\n\n        Returns:\n            Active orders created by `maker`, as a list of instances of the :py:class:`pymaker.zrx.Order` class.\n        \"\"\"\n        assert(isinstance(maker, Address))\n\n        url = f\"{self.api_server}/v0/orders?\" \\\n              f\"exchangeContractAddress={str(self.exchange.address.address).lower()}&\" \\\n              f\"maker={str(maker.address).lower()}&\" \\\n              f\"per_page={per_page}\"\n\n        response = requests.get(url, timeout=self.timeout)\n        if not response.ok:\n            raise Exception(f\"Failed to fetch 0x orders from the relayer: {http_response_summary(response)}\")\n\n        return list(map(lambda item: Order.from_json(self.exchange, item), response.json()))\n\n    def calculate_fees(self, order: Order) -> Order:\n        \"\"\"Takes and order and returns the same order with proper relayer fees.\n\n        Issues a call to the `/v0/fees` endpoint of the Standard Relayer API, as a result of it\n        new order is returned being the copy of the original one with the `maker_fee`, `taker_fee`\n        and `fee_recipient` fields filled in according to the relayer.\n\n        Relayers will very likely reject orders submitted if proper fees are not set first.\n        The standard approach is to call `calculate_fees()` first and then call `submit_order()`\n        passing the order received from `calculate_fees()` as parameter.\n\n        Args:\n            order: Order which should have fees calculated. The values of `maker_fee`, `taker_fee`\n                and `fee_recipient` are irrelevant and may as well be zeros as they will be overwritten\n                by this method anyway.\n\n        Returns:\n            Copy of the order received as a parameter with the `maker_fee`, `taker_fee` and `fee_recipient`\n            fields updated according to the relayer.\n        \"\"\"\n        assert(isinstance(order, Order))\n\n        response = requests.post(f\"{self.api_server}/v0/fees\", json=order.to_json_without_fees(), timeout=self.timeout)\n        if response.status_code == 200:\n            data = response.json()\n\n            order_with_fees = copy.copy(order)\n            order_with_fees.maker_fee = Wad(int(data['makerFee']))\n            order_with_fees.taker_fee = Wad(int(data['takerFee']))\n            order_with_fees.fee_recipient = Address(data['feeRecipient'])\n            return order_with_fees\n        else:\n            raise Exception(f\"Failed to fetch fees for 0x order: {http_response_summary(response)}\")\n\n    def submit_order(self, order: Order) -> bool:\n        \"\"\"Submits the order to the relayer.\n\n        Posts the order to the `/v0/order` endpoint of the Standard Relayer API\n\n        Args:\n            order: Order to be submitted.\n\n        Return:\n            `True` if order submission was successful. `False` otherwise.\n        \"\"\"\n        assert(isinstance(order, Order))\n\n        response = requests.post(f\"{self.api_server}/v0/order\", json=order.to_json(), timeout=self.timeout)\n        if response.status_code in [200, 201]:\n            self.logger.info(f\"Placed 0x order: {order}\")\n            return True\n        else:\n            self.logger.warning(f\"Failed to place 0x order: {http_response_summary(response)}\")\n            return False\n\n    def __repr__(self):\n        return f\"ZrxRelayerApi()\"\n"
  },
  {
    "path": "pymaker/zrxv2.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2017-2018 reverendus\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nimport array\nimport copy\nimport logging\nimport random\nimport time\nfrom pprint import pformat\nfrom typing import List, Optional, Tuple\n\nimport requests\nfrom eth_abi import encode_single, encode_abi, decode_single\nfrom hexbytes import HexBytes\nfrom web3 import Web3\nfrom web3._utils.events import get_event_data\n\nfrom eth_abi.codec import ABICodec\nfrom eth_abi.registry import registry as default_registry\n\nfrom pymaker import Contract, Address, Transact\nfrom pymaker.numeric import Wad\nfrom pymaker.sign import eth_sign, to_vrs\nfrom pymaker.token import ERC20Token\nfrom pymaker.util import bytes_to_hexstring, hexstring_to_bytes, http_response_summary\n\n\nclass Asset:\n    @staticmethod\n    def deserialize(asset: str):\n        assert(isinstance(asset, str))\n\n        if ERC20Asset.ID.upper() == asset[0:10].upper():\n            return ERC20Asset(token_address=Address(\"0x\" + asset[-40:]))\n\n        else:\n            return UnknownAsset(asset=asset)\n\n    def serialize(self) -> str:\n        raise Exception(\"serialize() not implemented\")\n\n    def __repr__(self):\n        return pformat(vars(self))\n\n\nclass ERC20Asset(Asset):\n    ID = \"0xf47261b0\"\n\n    def __init__(self, token_address: Address):\n        assert(isinstance(token_address, Address))\n\n        self.token_address = token_address\n\n    def serialize(self) -> str:\n        return self.ID + self.token_address.address[2:].zfill(64).lower()\n\n    def __hash__(self):\n        return hash(self.token_address)\n\n    def __eq__(self, other):\n        return isinstance(other, ERC20Asset) and self.token_address == other.token_address\n\n\nclass UnknownAsset(Asset):\n    def __init__(self, asset: str):\n        assert(isinstance(asset, str))\n\n        self.asset = asset\n\n    def serialize(self) -> str:\n        return self.asset\n\n    def __hash__(self):\n        return hash(self.asset)\n\n    def __eq__(self, other):\n        return isinstance(other, UnknownAsset) and self.asset == other.asset\n\n\nclass Order:\n    def __init__(self, exchange, sender: Address, maker: Address, taker: Address, maker_fee: Wad, taker_fee: Wad,\n                 pay_asset: Asset, pay_amount: Wad, buy_asset: Asset, buy_amount: Wad, salt: int, fee_recipient: Address,\n                 expiration: int, exchange_contract_address: Address, signature: Optional[str]):\n\n        assert(isinstance(sender, Address))\n        assert(isinstance(maker, Address))\n        assert(isinstance(taker, Address))\n        assert(isinstance(maker_fee, Wad))\n        assert(isinstance(taker_fee, Wad))\n        assert(isinstance(pay_asset, Asset))\n        assert(isinstance(pay_amount, Wad))\n        assert(isinstance(buy_asset, Asset))\n        assert(isinstance(buy_amount, Wad))\n        assert(isinstance(salt, int))\n        assert(isinstance(fee_recipient, Address))\n        assert(isinstance(expiration, int))\n        assert(isinstance(exchange_contract_address, Address))\n        assert(isinstance(signature, str) or (signature is None))\n\n        self._exchange = exchange\n        self.sender = sender\n        self.maker = maker\n        self.taker = taker\n        self.maker_fee = maker_fee\n        self.taker_fee = taker_fee\n        self.pay_asset = pay_asset\n        self.pay_amount = pay_amount\n        self.buy_asset = buy_asset\n        self.buy_amount = buy_amount\n        self.salt = salt\n        self.fee_recipient = fee_recipient\n        self.expiration = expiration\n        self.exchange_contract_address = exchange_contract_address\n        self.signature = signature\n\n    # this is not a proper 0x order_id, it's just so `OrderBookManager` can uniquely identify orders\n    @property\n    def order_id(self):\n        return hash(self)\n\n    @property\n    def sell_to_buy_price(self) -> Wad:\n        return self.pay_amount / self.buy_amount\n\n    @property\n    def buy_to_sell_price(self) -> Wad:\n        return self.buy_amount / self.pay_amount\n\n    @property\n    def remaining_buy_amount(self) -> Wad:\n        return Wad.max(self.buy_amount - self._exchange.get_unavailable_buy_amount(self), Wad(0))\n\n    @property\n    def remaining_sell_amount(self) -> Wad:\n        unavailable_buy_amount = self._exchange.get_unavailable_buy_amount(self)\n\n        if unavailable_buy_amount >= self.buy_amount:\n            return Wad(0)\n\n        else:\n            return Wad.max(self.pay_amount - (unavailable_buy_amount * self.pay_amount / self.buy_amount), Wad(0))\n\n    @staticmethod\n    def from_json(exchange, data: dict):\n        assert(isinstance(data, dict))\n\n        return Order(exchange=exchange,\n                     sender=Address(data['senderAddress']),\n                     maker=Address(data['makerAddress']),\n                     taker=Address(data['takerAddress']),\n                     maker_fee=Wad(int(data['makerFee'])),\n                     taker_fee=Wad(int(data['takerFee'])),\n                     pay_asset=Asset.deserialize(str(data['makerAssetData'])),\n                     pay_amount=Wad(int(data['makerAssetAmount'])),\n                     buy_asset=Asset.deserialize(str(data['takerAssetData'])),\n                     buy_amount=Wad(int(data['takerAssetAmount'])),\n                     salt=int(data['salt']),\n                     fee_recipient=Address(data['feeRecipientAddress']),\n                     expiration=int(data['expirationTimeSeconds']),\n                     exchange_contract_address=Address(data['exchangeAddress']),\n                     signature=data['signature'] if 'signature' in data else None)\n\n    def to_json_without_fees(self) -> dict:\n        return {\n            \"exchangeAddress\": self.exchange_contract_address.address.lower(),\n            \"makerAddress\": self.maker.address.lower(),\n            \"takerAddress\": self.taker.address.lower(),\n            \"makerAssetData\": self.pay_asset.serialize(),\n            \"takerAssetData\": self.buy_asset.serialize(),\n            \"makerAssetAmount\": str(self.pay_amount.value),\n            \"takerAssetAmount\": str(self.buy_amount.value),\n            \"expirationTimeSeconds\": str(self.expiration)\n        }\n\n    def to_json(self) -> dict:\n        return {\n            \"exchangeAddress\": self.exchange_contract_address.address.lower(),\n            \"senderAddress\": self.sender.address.lower(),\n            \"makerAddress\": self.maker.address.lower(),\n            \"takerAddress\": self.taker.address.lower(),\n            \"makerAssetData\": self.pay_asset.serialize(),\n            \"takerAssetData\": self.buy_asset.serialize(),\n            \"makerAssetAmount\": str(self.pay_amount.value),\n            \"takerAssetAmount\": str(self.buy_amount.value),\n            \"feeRecipientAddress\": self.fee_recipient.address.lower(),\n            \"makerFee\": str(self.maker_fee.value),\n            \"takerFee\": str(self.taker_fee.value),\n            \"expirationTimeSeconds\": str(self.expiration),\n            \"salt\": str(self.salt),\n            \"signature\": self.signature\n        }\n\n    def __eq__(self, other):\n        assert(isinstance(other, Order))\n        return self.sender == other.sender and \\\n               self.maker == other.maker and \\\n               self.taker == other.taker and \\\n               self.maker_fee == other.maker_fee and \\\n               self.taker_fee == other.taker_fee and \\\n               self.pay_asset == other.pay_asset and \\\n               self.pay_amount == other.pay_amount and \\\n               self.buy_asset == other.buy_asset and \\\n               self.buy_amount == other.buy_amount and \\\n               self.salt == other.salt and \\\n               self.fee_recipient == other.fee_recipient and \\\n               self.expiration == other.expiration and \\\n               self.exchange_contract_address == other.exchange_contract_address and \\\n               self.signature == other.signature\n\n    def __hash__(self):\n        return hash((self.sender,\n                     self.maker,\n                     self.taker,\n                     self.maker_fee,\n                     self.taker_fee,\n                     self.pay_asset,\n                     self.pay_amount,\n                     self.buy_asset,\n                     self.buy_amount,\n                     self.salt,\n                     self.fee_recipient,\n                     self.expiration,\n                     self.exchange_contract_address,\n                     self.signature))\n\n    def __str__(self):\n        return f\"('{self.buy_asset}', '{self.buy_amount}',\" \\\n               f\" '{self.pay_asset}', '{self.pay_amount}',\" \\\n               f\" '{self.exchange_contract_address}', '{self.salt}')\"\n\n    def __repr__(self):\n        return pformat(vars(self))\n\n\nclass LogCancel:\n    def __init__(self, log):\n        self.maker = Address(log['args']['makerAddress'])\n        self.fee_recipient = Address(log['args']['feeRecipientAddress'])\n        self.sender = Address(log['args']['senderAddress'])\n        self.pay_asset = Asset.deserialize(bytes_to_hexstring(log['args']['makerAssetData']))\n        self.buy_asset = Asset.deserialize(bytes_to_hexstring(log['args']['takerAssetData']))\n        self.order_hash = bytes_to_hexstring(log['args']['orderHash'])\n        self.raw = log\n\n    def __repr__(self):\n        return pformat(vars(self))\n\n\nclass LogFill:\n    def __init__(self, log):\n        self.sender = Address(log['args']['senderAddress'])\n        self.maker = Address(log['args']['makerAddress'])\n        self.taker = Address(log['args']['takerAddress'])\n        self.fee_recipient = Address(log['args']['feeRecipientAddress'])\n        self.pay_asset = Asset.deserialize(bytes_to_hexstring(log['args']['makerAssetData']))\n        self.buy_asset = Asset.deserialize(bytes_to_hexstring(log['args']['takerAssetData']))\n        self.filled_pay_amount = Wad(int(log['args']['makerAssetFilledAmount']))\n        self.filled_buy_amount = Wad(int(log['args']['takerAssetFilledAmount']))\n        self.paid_maker_fee = Wad(int(log['args']['makerFeePaid']))\n        self.paid_taker_fee = Wad(int(log['args']['takerFeePaid']))\n        self.order_hash = bytes_to_hexstring(log['args']['orderHash'])\n        self.raw = log\n\n    @classmethod\n    def from_event(cls, event: dict):\n        assert(isinstance(event, dict))\n\n        topics = event.get('topics')\n        if topics and topics[0] == HexBytes('0x0bcc4c97732e47d9946f229edb95f5b6323f601300e4690de719993f3c371129'):\n            log_fill_abi = [abi for abi in ZrxExchangeV2.abi if abi.get('name') == 'Fill'][0]\n            codec = ABICodec(default_registry)\n            event_data = get_event_data(codec, log_fill_abi, event)\n\n            return LogFill(event_data)\n\n        else:\n            return None\n\n    def __eq__(self, other):\n        assert(isinstance(other, LogFill))\n        return self.__dict__ == other.__dict__\n\n    def __repr__(self):\n        return pformat(vars(self))\n\n\nclass ZrxExchangeV2(Contract):\n    \"\"\"A client for the 0x V2 exchange contract.\n\n    You can find the `0x V2` exchange contract here:\n    <https://etherscan.io/address/0x4f833a24e1f95d70f028921e27040ca56e09ab0b>.\n\n    Attributes:\n        web3: An instance of `Web` from `web3.py`.\n        address: Ethereum address of the _0x_ `Exchange` contract.\n    \"\"\"\n\n    abi = Contract._load_abi(__name__, 'abi/ExchangeV2.abi')\n    bin = Contract._load_bin(__name__, 'abi/ExchangeV2.bin')\n\n    _ZERO_ADDRESS = Address(\"0x0000000000000000000000000000000000000000\")\n\n    ORDER_INFO_TYPE = '(address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes)'\n\n    @staticmethod\n    def deploy(web3: Web3, zrx_asset: str):\n        \"\"\"Deploy a new instance of the 0x `Exchange` contract.\n\n        Args:\n            web3: An instance of `Web` from `web3.py`.\n            zrx_token: The address of the ZRX token this exchange will use.\n\n        Returns:\n            A `ZrxExchange` class instance.\n        \"\"\"\n        return ZrxExchangeV2(web3=web3,\n                           address=Contract._deploy(web3, ZrxExchangeV2.abi, ZrxExchangeV2.bin, []))\n\n    def __init__(self, web3: Web3, address: Address):\n        assert(isinstance(web3, Web3))\n        assert(isinstance(address, Address))\n\n        self.web3 = web3\n        self.address = address\n        self._contract = self._get_contract(web3, self.abi, address)\n\n    def zrx_asset(self) -> str:\n        \"\"\"Get the asset data of the ZRX token contract associated with this `ExchangeV2` contract.\n\n        Returns:\n            The asset data of the `ZRX` token.\n        \"\"\"\n        return str(bytes_to_hexstring(self._contract.functions.ZRX_ASSET_DATA().call()))\n\n    def zrx_token(self) -> Address:\n        \"\"\"Get the address of the ZRX token contract associated with this `ExchangeV2` contract.\n\n        Returns:\n            The address of the `ZRX` token.\n        \"\"\"\n        return Address(\"0x\" + self.zrx_asset()[-40:])\n\n    def asset_transfer_proxy(self, proxy_id: str) -> Address:\n        \"\"\"Get the address of the `ERC20Proxy` contract associated with this `Exchange` contract.\n\n        Returns:\n            The address of the `ERC20Proxy` token.\n        \"\"\"\n        assert(isinstance(proxy_id, str))\n\n        return Address(self._contract.functions.getAssetProxy(hexstring_to_bytes(proxy_id)).call())\n\n    def approve(self, tokens: List[ERC20Token], approval_function):\n        \"\"\"Approve the 0x ERC20Proxy contract to fully access balances of specified tokens.\n\n        In case of 0x V2, it's the ERC20Proxy contract that actually gets the approvals,\n        not the 0x Exchange contract itself. In addition to the tokens specified as the `tokens`\n        parameter, the ZRX token always gets approved as well as without it the 0x Exchange\n        contract wouldn't be able to charge maker and taker fees.\n\n        For available approval functions (i.e. approval modes) see `directly` and `via_tx_manager`\n        in `pymaker.approval`.\n\n        Args:\n            tokens: List of :py:class:`pymaker.token.ERC20Token` class instances.\n            approval_function: Approval function (i.e. approval mode).\n        \"\"\"\n        assert(isinstance(tokens, list))\n        assert(callable(approval_function))\n\n        for token in tokens:  # TODO  + [ERC20Token(web3=self.web3, address=self.zrx_token())]\n            approval_function(token, self.asset_transfer_proxy(ERC20Asset.ID), '0x ERC20Proxy contract')\n\n    def past_fill(self, number_of_past_blocks: int, event_filter: dict = None) -> List[LogFill]:\n        \"\"\"Synchronously retrieve past LogFill events.\n\n        `LogFill` events are emitted by the 0x contract every time someone fills an order.\n\n        Args:\n            number_of_past_blocks: Number of past Ethereum blocks to retrieve the events from.\n            event_filter: Filter which will be applied to returned events.\n\n        Returns:\n            List of past `LogFill` events represented as :py:class:`pymaker.zrx.LogFill` class.\n        \"\"\"\n        assert(isinstance(number_of_past_blocks, int))\n        assert(isinstance(event_filter, dict) or (event_filter is None))\n\n        return self._past_events(self._contract, 'Fill', LogFill, number_of_past_blocks, event_filter)\n\n    def past_cancel(self, number_of_past_blocks: int, event_filter: dict = None) -> List[LogCancel]:\n        \"\"\"Synchronously retrieve past LogCancel events.\n\n        `LogCancel` events are emitted by the 0x contract every time someone cancels an order.\n\n        Args:\n            number_of_past_blocks: Number of past Ethereum blocks to retrieve the events from.\n            event_filter: Filter which will be applied to returned events.\n\n        Returns:\n            List of past `LogCancel` events represented as :py:class:`pymaker.zrx.LogCancel` class.\n        \"\"\"\n        assert(isinstance(number_of_past_blocks, int))\n        assert(isinstance(event_filter, dict) or (event_filter is None))\n\n        return self._past_events(self._contract, 'Cancel', LogCancel, number_of_past_blocks, event_filter)\n\n    def create_order(self,\n                     pay_asset: Asset,\n                     pay_amount: Wad,\n                     buy_asset: Asset,\n                     buy_amount: Wad,\n                     expiration: int) -> Order:\n        \"\"\"Creates a new order.\n\n        The `maker_fee`, `taker_fee` and `fee_recipient` fields are by default set to zero.\n        Before signing the order and submitting it to the relayer, they may need to be\n        populated using the `calculate_fees()` method of the `ZrxRelayerApi` class.\n\n        Args:\n            pay_asset: The asset you want to put on sale.\n            pay_amount: Amount of the `pay_asset` token you want to put on sale.\n            buy_asset: The asset you want to be paid with.\n            buy_amount: Amount of the `buy_asset` you want to receive.\n            expiration: Unix timestamp (in seconds) when the order will expire.\n\n        Returns:\n            New order as an instance of the :py:class:`pymaker.zrx.Order` class.\n        \"\"\"\n        assert(isinstance(pay_asset, Asset))\n        assert(isinstance(pay_amount, Wad))\n        assert(isinstance(buy_asset, Asset))\n        assert(isinstance(buy_amount, Wad))\n        assert(isinstance(expiration, int))\n\n        return Order(exchange=self,\n                     sender=self._ZERO_ADDRESS,\n                     maker=Address(self.web3.eth.defaultAccount),\n                     taker=self._ZERO_ADDRESS,\n                     maker_fee=Wad(0),\n                     taker_fee=Wad(0),\n                     pay_asset=pay_asset,\n                     pay_amount=pay_amount,\n                     buy_asset=buy_asset,\n                     buy_amount=buy_amount,\n                     salt=self.generate_salt(),\n                     fee_recipient=self._ZERO_ADDRESS,\n                     expiration=expiration,\n                     exchange_contract_address=self.address,\n                     signature=None)\n\n    def _get_order_info(self, order):\n        assert(isinstance(order, Order))\n\n        method_signature = self.web3.keccak(text=f\"getOrderInfo({self.ORDER_INFO_TYPE})\")[0:4]\n        method_parameters = encode_single(f\"({self.ORDER_INFO_TYPE})\", [self._order_tuple(order)])\n\n        request = bytes_to_hexstring(method_signature + method_parameters)\n        response = self.web3.eth.call({'to': self.address.address, 'data': request})\n        response_decoded = decode_single(\"((uint8,bytes32,uint256))\", response)\n\n        return response_decoded\n\n    def get_order_hash(self, order: Order) -> str:\n        \"\"\"Calculates hash of an order.\n\n        Args:\n            order: Order you want to calculate the hash of.\n\n        Returns:\n            Order hash as a hex string starting with `0x`.\n        \"\"\"\n        assert(isinstance(order, Order))\n\n        # the hash depends on the exchange contract address as well\n        assert(order.exchange_contract_address == self.address)\n\n        return bytes_to_hexstring(self._get_order_info(order)[0][1])\n\n    def get_unavailable_buy_amount(self, order: Order) -> Wad:\n        \"\"\"Return the order amount which was either taken or cancelled.\n\n        Args:\n            order: Order you want to get the unavailable amount of.\n\n        Returns:\n            The unavailable amount of the order (i.e. the amount which was either taken or cancelled),\n            expressed in terms of the `buy_token` token.\n        \"\"\"\n        assert(isinstance(order, Order))\n\n        order_info = self._get_order_info(order)[0]\n\n        if order_info[0] in [0,         # INVALID,                     // Default value\n                             1,         # INVALID_MAKER_ASSET_AMOUNT,  // Order does not have a valid maker asset amount\n                             2,         # INVALID_TAKER_ASSET_AMOUNT,  // Order does not have a valid taker asset amount\n                             4,         # EXPIRED,                     // Order has already expired\n                             5,         # FULLY_FILLED,                // Order is fully filled\n                             6]:        # CANCELLED                    // Order has been cancelled\n            return order.buy_amount\n\n        else:\n            return Wad(order_info[2])\n\n    def sign_order(self, order: Order) -> Order:\n        \"\"\"Signs an order so it can be submitted to the relayer.\n\n        Order will be signed by the `web3.eth.defaultAccount` account.\n\n        Args:\n            order: Order you want to sign.\n\n        Returns:\n            Signed order. Copy of the order passed as a parameter with the `signature` field filled with signature.\n        \"\"\"\n        assert(isinstance(order, Order))\n\n        signature = eth_sign(hexstring_to_bytes(self.get_order_hash(order)), self.web3)\n        v, r, s = to_vrs(signature)\n\n        signed_order = copy.copy(order)\n        signed_order.signature = bytes_to_hexstring(bytes([v])) + \\\n                                 bytes_to_hexstring(r)[2:] + \\\n                                 bytes_to_hexstring(s)[2:] + \\\n                                 \"03\"  # EthSign\n        return signed_order\n\n    def fill_order(self, order: Order, fill_buy_amount: Wad) -> Transact:\n        \"\"\"Fills an order.\n\n        Args:\n            order: The order to be filled.\n            fill_buy_amount: The amount (in terms of `buy_token` of the original order) to be filled.\n\n        Returns:\n            A :py:class:`pymaker.Transact` instance, which can be used to trigger the transaction.\n        \"\"\"\n        assert(isinstance(order, Order))\n        assert(isinstance(fill_buy_amount, Wad))\n\n        method_signature = self.web3.keccak(text=f\"fillOrder({self.ORDER_INFO_TYPE},uint256,bytes)\")[0:4]\n        method_parameters = encode_single(f\"({self.ORDER_INFO_TYPE},uint256,bytes)\", [self._order_tuple(order),\n                                                                                      fill_buy_amount.value,\n                                                                                      hexstring_to_bytes(order.signature)])\n\n        request = bytes_to_hexstring(method_signature + method_parameters)\n\n        return Transact(self, self.web3, self.abi, self.address, self._contract, None,\n                        [request])\n\n    def cancel_order(self, order: Order) -> Transact:\n        \"\"\"Cancels an order.\n\n        Args:\n            order: Order you want to cancel.\n\n        Returns:\n            A :py:class:`pymaker.Transact` instance, which can be used to trigger the transaction.\n        \"\"\"\n        assert(isinstance(order, Order))\n\n        method_signature = self.web3.keccak(text=f\"cancelOrder({self.ORDER_INFO_TYPE})\")[0:4]\n        method_parameters = encode_single(f\"({self.ORDER_INFO_TYPE})\", [self._order_tuple(order)])\n\n        request = bytes_to_hexstring(method_signature + method_parameters)\n\n        return Transact(self, self.web3, self.abi, self.address, self._contract, None,\n                        [request])\n\n    @staticmethod\n    def _order_tuple(order):\n        return (order.maker.address,\n                order.taker.address,\n                order.fee_recipient.address,\n                order.sender.address,\n                order.pay_amount.value,\n                order.buy_amount.value,\n                order.maker_fee.value,\n                order.taker_fee.value,\n                order.expiration,\n                order.salt,\n                hexstring_to_bytes(order.pay_asset.serialize()),\n                hexstring_to_bytes(order.buy_asset.serialize()))\n\n    @staticmethod\n    def generate_salt() -> int:\n        return int(time.time() * 1000)\n\n    def __repr__(self):\n        return f\"ZrxExchangeV2('{self.address}')\"\n\n\nclass ZrxRelayerApiV2:\n    \"\"\"A client for the Standard 0x Relayer API V2.\n\n    <https://github.com/0xProject/standard-relayer-api/blob/master/http/v2.md>\n\n    Attributes:\n        exchange: The 0x Exchange V2 contract.\n        api_server: Base URL of the Standard Relayer API server.\n    \"\"\"\n    logger = logging.getLogger()\n    timeout = 15.5\n\n    def __init__(self, exchange: ZrxExchangeV2, api_server: str):\n        assert(isinstance(exchange, ZrxExchangeV2))\n        assert(isinstance(api_server, str))\n\n        self.exchange = exchange\n        self.api_server = api_server\n\n    def get_book(self, pay_token: Address, buy_token: Address, depth: int = 100) -> Tuple[List[Order], List[Order]]:\n        assert(isinstance(pay_token, Address))\n        assert(isinstance(buy_token, Address))\n        assert(isinstance(depth, int))\n\n        params = {\"baseAssetData\": ERC20Asset(pay_token).serialize(),\n                  \"quoteAssetData\": ERC20Asset(buy_token).serialize(),\n                  \"perPage\": depth}\n\n        response = requests.get(f\"{self.api_server}/v2/orderbook\", params, timeout=self.timeout)\n        if not response.ok:\n            raise Exception(f\"Failed to fetch 0x orderbook from the relayer: {http_response_summary(response)}\")\n\n        data = response.json()\n\n        return list(map(lambda item: Order.from_json(self.exchange, item['order']), data['asks']['records'])), \\\n               list(map(lambda item: Order.from_json(self.exchange, item['order']), data['bids']['records']))\n\n    def get_orders(self, pay_token: Address, buy_token: Address, per_page: int = 100) -> List[Order]:\n        \"\"\"Returns active orders filtered by token pair (one side).\n\n        In order to get them, issues a `/v2/orders` call to the Standard Relayer API.\n\n        Args:\n            per_page: Maximum number of orders to be downloaded per page. 0x Standard Relayer API\n                limitation is 100, but some relayers can handle more so that's why this parameter\n                is exposed.\n\n        Returns:\n            Orders, as a list of instances of the :py:class:`pymaker.zrx.Order` class.\n        \"\"\"\n        assert(isinstance(pay_token, Address))\n        assert(isinstance(buy_token, Address))\n\n        params = { \"exchangeAddress\": self.exchange.address.address.lower(),\n                   \"makerAssetData\": ERC20Asset(pay_token).serialize(),\n                   \"takerAssetData\": ERC20Asset(buy_token).serialize(),\n                   \"per_page\": per_page,\n                 }\n\n        response = requests.get(f\"{self.api_server}/v2/orders\", params=params, timeout=self.timeout)\n        if not response.ok:\n            raise Exception(f\"Failed to fetch 0x orders from the relayer: {http_response_summary(response)}\")\n\n        data = response.json()\n        if 'records' in data:\n            return list(map(lambda item: Order.from_json(self.exchange, item['order']), data['records']))\n        else:\n            return []\n\n    def get_order(self, order_hash: str) -> Order:\n        assert(isinstance(order_hash, str))\n\n        response = requests.get(f\"{self.api_server}/v2/order/{order_hash}\", timeout=self.timeout)\n        if not response.ok:\n            raise Exception(f\"Failed to 0x order from the relayer: {http_response_summary(response)}\")\n\n        return Order.from_json(self.exchange, response.json()['order'])\n\n    def get_orders_by_maker(self, maker: Address, per_page: int = 100) -> List[Order]:\n        \"\"\"Returns all active orders created by `maker`.\n\n        In order to get them, issues a `/v2/orders` call to the Standard Relayer API.\n\n        Args:\n            maker: Address of the `maker` to filter the orders by.\n            per_page: Maximum number of orders to be downloaded per page. 0x Standard Relayer API\n                limitation is 100, but some relayers can handle more so that's why this parameter\n                is exposed.\n\n        Returns:\n            Active orders created by `maker`, as a list of instances of the :py:class:`pymaker.zrx.Order` class.\n        \"\"\"\n        assert(isinstance(maker, Address))\n\n        params = { \"exchangeAddress\": self.exchange.address.address.lower(),\n                   \"makerAddress\": str(maker).lower(),\n                   \"per_page\": per_page,\n                 }\n\n        response = requests.get(f\"{self.api_server}/v2/orders\", params=params, timeout=self.timeout)\n        if not response.ok:\n            raise Exception(f\"Failed to fetch 0x orders from the relayer: {http_response_summary(response)}\")\n\n        data = response.json()\n        if 'records' in data:\n            return list(map(lambda item: Order.from_json(self.exchange, item['order']), data['records']))\n        else:\n            return []\n\n    def configure_order(self, order: Order) -> Order:\n        \"\"\"Takes a partial order and  receive information required to complete the order:\n\n           senderAddress, feeRecipientAddress, makerFee, takerFee\n\n        Issues a call to the `/v2/order_config` endpoint of the Standard Relayer API V2, as a result\n        of it new order is returned being the copy of the original one with the `senderAddress`,\n        `maker_fee`, `taker_fee` and `fee_recipient` fields filled in according to the relayer.\n\n        Relayers will very likely reject orders submitted if proper fields are not set first.\n        The standard approach is to call `configure_order()` first and then call `submit_order()`\n        passing the order received from `configure_order()` as parameter.\n\n        Args:\n            order: Order which should be configured. The values of `senderAddress`, `maker_fee`, `taker_fee`\n                and `fee_recipient` could be overwritten by this method.\n\n        Returns:\n            Copy of the order received as a parameter with the `senderAddress`, `maker_fee`, `taker_fee`\n            and `fee_recipient` fields updated according to the relayer.\n        \"\"\"\n        assert(isinstance(order, Order))\n\n        response = requests.get(f\"{self.api_server}/v2/order_config\", params=order.to_json_without_fees(), timeout=self.timeout)\n        if response.status_code == 200:\n            data = response.json()\n            #{\"senderAddress\":\"0xc8924d8cd9a758a4150afe7cc7030effaff1aecc\",\"feeRecipientAddress\":\"0xc8924d8cd9a758a4150afe7cc7030effaff1aecc\",\"makerFee\":\"0\",\"takerFee\":\"0\"}\n\n            configured_order = copy.copy(order)\n            configured_order.sender = Address(data['senderAddress'])\n            configured_order.maker_fee = Wad(int(data['makerFee']))\n            configured_order.taker_fee = Wad(int(data['takerFee']))\n            configured_order.fee_recipient = Address(data['feeRecipientAddress'])\n            return configured_order\n        else:\n            raise Exception(f\"Failed to configure order with 0x SRAv2: {http_response_summary(response)}\")\n\n    def submit_order(self, order: Order) -> bool:\n        \"\"\"Submits the order to the relayer.\n\n        Posts the order to the `/v2/order` endpoint of the Standard Relayer API\n\n        Args:\n            order: Order to be submitted.\n\n        Return:\n            `True` if order submission was successful. `False` otherwise.\n        \"\"\"\n        assert(isinstance(order, Order))\n\n        response = requests.post(f\"{self.api_server}/v2/order\", json=order.to_json(), timeout=self.timeout)\n        if response.status_code in [200, 201]:\n            self.logger.info(f\"Placed 0x order: {order}\")\n            return True\n        else:\n            self.logger.warning(f\"Failed to place 0x order: {http_response_summary(response)}\")\n            return False\n\n    def __repr__(self):\n        return f\"ZrxRelayerApiV2()\"\n"
  },
  {
    "path": "requirements-dev.txt",
    "content": "attrs == 19.1.0\ncodecov == 2.0.9\nmock == 2.0.0\npytest == 3.3.0\npytest-asyncio == 0.9.0\npytest-cov == 2.5.1\npytest-mock == 1.6.3\npytest-timeout == 1.2.1\nasynctest == 0.13.0\nSphinx == 1.6.2\nzipp == 3.4.1 \n"
  },
  {
    "path": "requirements.txt",
    "content": "pytz == 2017.3\nweb3 == 5.12.0\nrequests == 2.22.0\neth-keys<0.3.0,>=0.2.1\njsonnet == 0.9.5\n"
  },
  {
    "path": "setup.py",
    "content": "\"\"\"A setuptools based setup module.\n\nSee:\nhttps://packaging.python.org/guides/distributing-packages-using-setuptools/\nhttps://github.com/pypa/sampleproject\nhttps://github.com/pypa/sampleproject/blob/master/setup.py\n\"\"\"\n\n# Always prefer setuptools over distutils\nfrom setuptools import setup, find_packages\nfrom os import path\n\nhere = path.abspath(path.dirname(__file__))\n\n# Get the long description from the README file\nwith open(path.join(here, 'README.md'), encoding='utf-8') as f:\n    long_description = f.read()\n\n# Read requirements.txt\nwith open(path.join(here, 'requirements.txt'), encoding='utf-8') as f:\n    requirements = f.read().split('\\n')\n\n# Arguments marked as \"Required\" below must be included for upload to PyPI.\n# Fields marked as \"Optional\" may be commented out.\n\nsetup(\n    name='pymaker',\n\n    # Versions should comply with PEP 440:\n    # https://www.python.org/dev/peps/pep-0440/\n    #\n    # For a discussion on single-sourcing the version across setup.py and the\n    # project code, see\n    # https://packaging.python.org/en/latest/single_source_version.html\n    version='1.1.3',  # Required\n    description='Python API for Maker contracts',\n    license='COPYING',\n    long_description=long_description,\n    long_description_content_type='text/markdown',\n    url='https://github.com/makerdao/pymaker',\n    author='MakerDAO',\n    packages=find_packages(include=['pymaker', 'pymaker.*']),  # Required\n    package_data={'pymaker': ['abi/*', '../config/*']},\n    include_package_data=True,\n    python_requires='~=3.6',\n\n    # This field lists other packages that your project depends on to run.\n    # Any package you put here will be installed by pip when your project is\n    # installed, so they must be valid existing projects.\n    #\n    # For an analysis of \"install_requires\" vs pip's requirements files see:\n    # https://packaging.python.org/en/latest/requirements.html\n    install_requires=requirements\n)\n"
  },
  {
    "path": "test-dss.sh",
    "content": "#!/bin/bash\n\n# Pull the docker image\ndocker pull makerdao/testchain-pymaker:unit-testing\n\n# Remove existing container if tests not gracefully stopped\ndocker-compose down\n\n# Start ganache\ndocker-compose up -d ganache\n\n# Start parity and wait to initialize\ndocker-compose up -d parity\nsleep 2\n\n# Run the tests\npy.test --cov=pymaker --cov-report=term --cov-append tests/test_auctions.py tests/test_cdpmanager.py \\\n tests/test_dsrmanager.py tests/test_dss.py tests/test_savings.py tests/test_shutdown.py $@\nTEST_RESULT=$?\n\n# Cleanup\ndocker-compose down\n\nexit $TEST_RESULT\n"
  },
  {
    "path": "test.sh",
    "content": "#!/bin/bash\n\n# Pull the docker image\ndocker pull makerdao/testchain-pymaker:unit-testing\n\n# Remove existing container if tests not gracefully stopped\ndocker-compose down\n\n# Start ganache\ndocker-compose up -d ganache\n\n# Start parity and wait to initialize\ndocker-compose up -d parity\nsleep 2\n\n# Run the tests\npy.test --cov=pymaker --cov-report=term --cov-append tests/ $@\nTEST_RESULT=$?\n\n# Cleanup\ndocker-compose down\n\nexit $TEST_RESULT\n"
  },
  {
    "path": "tests/__init__.py",
    "content": ""
  },
  {
    "path": "tests/abi/DaiMock.abi",
    "content": "[{\"constant\":true,\"inputs\":[],\"name\":\"vat\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"\",\"type\":\"address\"},{\"name\":\"\",\"type\":\"address\"}],\"name\":\"can\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"guy\",\"type\":\"address\"}],\"name\":\"hope\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"src\",\"type\":\"address\"},{\"name\":\"dst\",\"type\":\"address\"},{\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"move\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"guy\",\"type\":\"address\"}],\"name\":\"nope\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"urn\",\"type\":\"bytes32\"},{\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"join\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"guy\",\"type\":\"address\"},{\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"exit\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"dai\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"name\":\"vat_\",\"type\":\"address\"},{\"name\":\"dai_\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"}]"
  },
  {
    "path": "tests/abi/DaiMock.sol",
    "content": "pragma solidity ^0.4.24;\n\n// Fusion between a DaiJoin and a DaiMove\n\ncontract GemLike {\n    function transferFrom(address,address,uint) public returns (bool);\n    function mint(address,uint) public;\n    function burn(address,uint) public;\n}\n\ncontract VatLike {\n    function slip(bytes32,bytes32,int) public;\n    function move(bytes32,bytes32,int) public;\n    function flux(bytes32,bytes32,bytes32,int) public;\n}\n\ncontract DaiMock {\n    VatLike public vat;\n    GemLike public dai;\n    constructor(address vat_, address dai_) public {\n        vat = VatLike(vat_);\n        dai = GemLike(dai_);\n    }\n    uint constant ONE = 10 ** 27;\n    function mul(uint x, uint y) internal pure returns (int z) {\n        z = int(x * y);\n        require(int(z) >= 0);\n        require(y == 0 || uint(z) / y == x);\n    }\n    mapping(address => mapping (address => bool)) public can;\n    function hope(address guy) public { can[msg.sender][guy] = true; }\n    function nope(address guy) public { can[msg.sender][guy] = false; }\n    function move(address src, address dst, uint wad) public {\n        require(src == msg.sender || can[src][msg.sender]);\n        vat.move(bytes32(src), bytes32(dst), mul(ONE, wad));\n    }\n    function join(bytes32 urn, uint wad) public {\n        vat.move(bytes32(address(this)), urn, mul(ONE, wad));\n        dai.burn(msg.sender, wad);\n    }\n    function exit(address guy, uint wad) public {\n        vat.move(bytes32(msg.sender), bytes32(address(this)), mul(ONE, wad));\n        dai.mint(guy, wad);\n    }\n}\n"
  },
  {
    "path": "tests/abi/GemMock.abi",
    "content": "[{\"constant\":true,\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"stop\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"guy\",\"type\":\"address\"},{\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"owner_\",\"type\":\"address\"}],\"name\":\"setOwner\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"totalSupply\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"src\",\"type\":\"address\"},{\"name\":\"dst\",\"type\":\"address\"},{\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"guy\",\"type\":\"address\"},{\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"mint\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"burn\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"src\",\"type\":\"address\"},{\"name\":\"guy\",\"type\":\"address\"}],\"name\":\"can\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"name_\",\"type\":\"bytes32\"}],\"name\":\"setName\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"src\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"stopped\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"authority_\",\"type\":\"address\"}],\"name\":\"setAuthority\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"symbol\",\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"guy\",\"type\":\"address\"},{\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"burn\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"mint\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"guy\",\"type\":\"address\"}],\"name\":\"hope\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"dst\",\"type\":\"address\"},{\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"transfer\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"dst\",\"type\":\"address\"},{\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"push\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"src\",\"type\":\"address\"},{\"name\":\"dst\",\"type\":\"address\"},{\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"move\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"start\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"authority\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"guy\",\"type\":\"bytes32\"},{\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"push\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"guy\",\"type\":\"address\"}],\"name\":\"approve\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"guy\",\"type\":\"address\"}],\"name\":\"nope\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"src\",\"type\":\"address\"},{\"name\":\"guy\",\"type\":\"address\"}],\"name\":\"allowance\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"src\",\"type\":\"address\"},{\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"pull\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"name\":\"symbol_\",\"type\":\"bytes32\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"guy\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"Mint\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"guy\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"Burn\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"authority\",\"type\":\"address\"}],\"name\":\"LogSetAuthority\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"LogSetOwner\",\"type\":\"event\"},{\"anonymous\":true,\"inputs\":[{\"indexed\":true,\"name\":\"sig\",\"type\":\"bytes4\"},{\"indexed\":true,\"name\":\"guy\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"foo\",\"type\":\"bytes32\"},{\"indexed\":true,\"name\":\"bar\",\"type\":\"bytes32\"},{\"indexed\":false,\"name\":\"wad\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"fax\",\"type\":\"bytes\"}],\"name\":\"LogNote\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"src\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"guy\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"Approval\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"src\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"dst\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"Transfer\",\"type\":\"event\"}]"
  },
  {
    "path": "tests/abi/GemMock.sol",
    "content": "pragma solidity ^0.4.24;\n\nimport \"ds-token/token.sol\";\n\ncontract GemMock is DSToken('') {\n\n    constructor(bytes32 symbol_) public {\n        symbol = symbol_;\n    }\n\n    function can(address src, address guy) public view returns (bool) {\n        if (allowance(src, guy) > 0) {\n            return true;\n        }\n\n        return false;\n    }\n\n    function push(bytes32 guy, uint wad) public { push(address(guy), wad); }\n    function hope(address guy) public { approve(guy); }\n    function nope(address guy) public { approve(guy, 0); }\n}\n"
  },
  {
    "path": "tests/abi/OasisMockPriceOracle.abi",
    "content": "[{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"getPriceFor\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_price\",\"type\":\"uint256\"}],\"name\":\"setPrice\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]\n"
  },
  {
    "path": "tests/abi/OasisMockPriceOracle.sol",
    "content": "pragma solidity ^0.5.12;\n\ncontract OasisMockPriceOracle {\n    uint256 price;\n    function setPrice(address, uint256 _price) public {\n        price = _price;\n    }\n\n    function getPriceFor(address, address, uint256) public view returns (uint256) {\n        return price;\n    }\n}"
  },
  {
    "path": "tests/accounts/0_0x9596c16d7bf9323265c2f2e22f43e6c80eb3d943.json",
    "content": "{\"version\":3,\"id\":\"375c1ad3-b203-4e32-b32a-dd5cad819a4c\",\"address\":\"9596c16d7bf9323265c2f2e22f43e6c80eb3d943\",\"crypto\":{\"ciphertext\":\"4a24fa91bd5c9652ca0a3e9b03c8376d1c4cc1beda2641ea5e2642519b1614c6\",\"cipherparams\":{\"iv\":\"39a62791ce1c0e80a3b8a159b1d1c2dd\"},\"cipher\":\"aes-128-ctr\",\"kdf\":\"scrypt\",\"kdfparams\":{\"dklen\":32,\"salt\":\"e1e7167139d333de7897971f90f6af69c3befe0a02604775c55d109e82659156\",\"n\":262144,\"r\":8,\"p\":1},\"mac\":\"945369f4bf11492703ff7793038ab45f7f20a07f559be0b3721f159e8152d00e\"}}"
  },
  {
    "path": "tests/accounts/1_0xe415482ca06eeb684ad3f758c2129fca4b1eb1f4.json",
    "content": "{\"version\":3,\"id\":\"d2275fb9-533f-4c63-b970-9767bb45d38d\",\"address\":\"e415482ca06eeb684ad3f758c2129fca4b1eb1f4\",\"crypto\":{\"ciphertext\":\"9807f801eae14580b0641c6474e26e2ac2876f3903d0705965d2c5c5f1cfc4ab\",\"cipherparams\":{\"iv\":\"ede22e7e1e3f74560328258df631f7d7\"},\"cipher\":\"aes-128-ctr\",\"kdf\":\"scrypt\",\"kdfparams\":{\"dklen\":32,\"salt\":\"9cdab7c0c88539b75946b03a0a3e3d637faf42cf0e99d92540d269b0bc358fa0\",\"n\":262144,\"r\":8,\"p\":1},\"mac\":\"38c3c786a8b8d61d5169ae854b18c9e4968b6fb596028a71a78fab05477bf889\"}}"
  },
  {
    "path": "tests/accounts/2_0x270b0e8d873e858abd698a000b0da0b94e21d84c.json",
    "content": "{\"version\":3,\"id\":\"3088cf66-27a4-4da4-a5de-bb03fdfc1751\",\"address\":\"270b0e8d873e858abd698a000b0da0b94e21d84c\",\"crypto\":{\"ciphertext\":\"92da68991a680c24e806ce49bb3034c63c0597ef0e13b669c5d2bf3c4f8d4d03\",\"cipherparams\":{\"iv\":\"758f2d23967c5d2a2b7e816c259647d5\"},\"cipher\":\"aes-128-ctr\",\"kdf\":\"scrypt\",\"kdfparams\":{\"dklen\":32,\"salt\":\"e5a6c8b70e5df30da1e53c075b243872c2cc404038a4ac76208aa481a629e377\",\"n\":262144,\"r\":8,\"p\":1},\"mac\":\"c8316865a833729218c55fac5d709d3d2bfe37c893724c8fa879e9ef3abe0171\"}}"
  },
  {
    "path": "tests/accounts/3_0x812e87be5d4198fca55cb52fa60cb46620617474.json",
    "content": "{\"version\":3,\"id\":\"c0870e2f-9f3b-48b6-a670-cd056d3c6931\",\"address\":\"812e87be5d4198fca55cb52fa60cb46620617474\",\"crypto\":{\"ciphertext\":\"03a7f102806b520d17847f6dd75cd0b357fce4cd3097be0f3e40e93e55021b6d\",\"cipherparams\":{\"iv\":\"fc81b85e0efded67ae9d6a90283f1886\"},\"cipher\":\"aes-128-ctr\",\"kdf\":\"scrypt\",\"kdfparams\":{\"dklen\":32,\"salt\":\"d7e85edd551f7d3fd836dfbd94430c78c2484e51eb5d1d314b3bba21a59a8694\",\"n\":262144,\"r\":8,\"p\":1},\"mac\":\"6bc6b12ac5e9d2e61069aa76bda242fb2b07f08d07ccfc1437b318f5aaf61dd6\"}}"
  },
  {
    "path": "tests/accounts/4_0x13314e21cd6d343ceb857073f3f6d9368919d1ef.json",
    "content": "{\"version\":3,\"id\":\"f9455bbb-482b-46f7-9e29-503838c386be\",\"address\":\"13314e21cd6d343ceb857073f3f6d9368919d1ef\",\"crypto\":{\"ciphertext\":\"e16d398307f5cfc1331db001f1a9b0392eae1bb80299c3b7cf24fc56b3a24755\",\"cipherparams\":{\"iv\":\"488f5f4c06909ccec258ff3aa9a40caf\"},\"cipher\":\"aes-128-ctr\",\"kdf\":\"scrypt\",\"kdfparams\":{\"dklen\":32,\"salt\":\"f054d8267209ef8e28ef30c3ed3bbb5291ae8738efd0448c7f8993f4c0505388\",\"n\":262144,\"r\":8,\"p\":1},\"mac\":\"cf3a13bc8e6b6a4794f3890e350b972afe782fe092df7b99e443a34d18ad8ce8\"}}"
  },
  {
    "path": "tests/accounts/5_0x176087fea5c41fc370fabbd850521bc4451690ca.json",
    "content": "{\"version\":3,\"id\":\"aaf50a37-d4d1-41ef-ad91-ac5e20a0f5b4\",\"address\":\"176087fea5c41fc370fabbd850521bc4451690ca\",\"crypto\":{\"ciphertext\":\"e9429a2801c7a16ad339bd748fc352639e7380c72fa9bea984b2955664c4350d\",\"cipherparams\":{\"iv\":\"5457ac288d8340c984f753e8f21f3bb5\"},\"cipher\":\"aes-128-ctr\",\"kdf\":\"scrypt\",\"kdfparams\":{\"dklen\":32,\"salt\":\"5eb1234e8d3ad07a3989c33f68378d7cffd314cee79f988d76623fe083f251f0\",\"n\":262144,\"r\":8,\"p\":1},\"mac\":\"9470221ed35c56d8798fe238e276ab731b2f14f7671f69fcca16dfd9ccc8bccd\"}}"
  },
  {
    "path": "tests/accounts/pass",
    "content": "12345678"
  },
  {
    "path": "tests/config/keys/UnlimitedChain/key.json",
    "content": "{\"id\":\"032b64c7-38c6-dfb4-714a-25eacd2daecc\",\"version\":3,\"crypto\":{\"cipher\":\"aes-128-ctr\",\"cipherparams\":{\"iv\":\"38f902a7336bf5160c427f499a6ded5c\"},\"ciphertext\":\"afcc67f82dd5327181a75cbd33319301575577f42aa42083f4f6620dcbb6fa60\",\"kdf\":\"pbkdf2\",\"kdfparams\":{\"c\":10240,\"dklen\":32,\"prf\":\"hmac-sha256\",\"salt\":\"c7b2c641170cfb25a06c60e51d08bdf4e11d51acdfd65aaf832e2d7b13470812\"},\"mac\":\"dca205a615725a79073baec0e015a7a5c1d51c72d861081f2c26296abe7cec09\"},\"address\":\"00a329c0648769a73afac7f9381e08fb43dbea72\",\"name\":\"Development Account\",\"meta\":\"{\\\"description\\\":\\\"Never use this account outside of development chain!\\\",\\\"passwordHint\\\":\\\"Password is empty string\\\"}\"}"
  },
  {
    "path": "tests/config/keys/UnlimitedChain/key1.json",
    "content": "{\"id\":\"09068027-f04d-f8fb-79d9-9df6f03ac604\",\"version\":3,\"crypto\":{\"cipher\":\"aes-128-ctr\",\"cipherparams\":{\"iv\":\"4ac591b30b76366cf71de779fd32b5fe\"},\"ciphertext\":\"6eabb1137e388761f879d85f1f48ff48252ef657ba723e274b04c8b86c676f29\",\"kdf\":\"pbkdf2\",\"kdfparams\":{\"c\":10240,\"dklen\":32,\"prf\":\"hmac-sha256\",\"salt\":\"e16ce22f791ffa5b8e9da1236e6c943e7988f82ba63a540892e12b5d874241bb\"},\"mac\":\"b81515fe8c2ccc669f35b859128bf431c100f6eeeb853f942c73bb7fba9db44c\"},\"address\":\"6c626f45e3b7ae5a3998478753634790fd0e82ee\",\"name\":\"\",\"meta\":\"{}\"}"
  },
  {
    "path": "tests/config/keys/UnlimitedChain/key2.json",
    "content": "{\"id\":\"6e7c69aa-e8d3-e381-ed85-36a08df7cfbe\",\"version\":3,\"crypto\":{\"cipher\":\"aes-128-ctr\",\"cipherparams\":{\"iv\":\"e19eee7d928a8d7df75bc58fd31ff05a\"},\"ciphertext\":\"b6f9bd7de52fbf59677de26a81b8a4b03bbf4eb0f849e126eae4a36b0ac5a676\",\"kdf\":\"pbkdf2\",\"kdfparams\":{\"c\":10240,\"dklen\":32,\"prf\":\"hmac-sha256\",\"salt\":\"e9580e3282bcf4bf517d1eb78bf86b41e41bd9777f43afa517b2283c5a52ed9c\"},\"mac\":\"906d301903aae013fd98b184f153118a42af29e043865b2845e846eb5c5b4678\"},\"address\":\"50ff810797f75f6bfbf2227442e0c961a8562f4c\",\"name\":\"\",\"meta\":\"{}\"}"
  },
  {
    "path": "tests/config/keys/UnlimitedChain/key3.json",
    "content": "{\"id\":\"ecf98a52-7a41-0b18-a39a-e57ddc7a41d9\",\"version\":3,\"crypto\":{\"cipher\":\"aes-128-ctr\",\"cipherparams\":{\"iv\":\"c40c8be2726d367cdc885f59a1158488\"},\"ciphertext\":\"0ce8de6e23d2ebdb4ac559af6270e92a01668f11a20868caee672200d7f80c0c\",\"kdf\":\"pbkdf2\",\"kdfparams\":{\"c\":10240,\"dklen\":32,\"prf\":\"hmac-sha256\",\"salt\":\"b69e7ff92bdb5e10963221ae4379f5a4bfc1e3856001e5a78cf26c1aaeea0d54\"},\"mac\":\"c067ac59f17e52586de59a9f4542c11149d84825e9b027f7145455124784337b\"},\"address\":\"5beb2d3aa2333a524703af18310acff462c04723\",\"name\":\"\",\"meta\":\"{}\"}"
  },
  {
    "path": "tests/config/keys/UnlimitedChain/key4.json",
    "content": "{\"id\":\"d2bed4ab-c313-7430-be81-8a0d04bfc9aa\",\"version\":3,\"crypto\":{\"cipher\":\"aes-128-ctr\",\"cipherparams\":{\"iv\":\"b7b2722e5e15d41ab83d0350fd72d6f6\"},\"ciphertext\":\"7dc899d78c010324b8600a729078d894517c28b5a4fef85e60d0d95c91d51a75\",\"kdf\":\"pbkdf2\",\"kdfparams\":{\"c\":10240,\"dklen\":32,\"prf\":\"hmac-sha256\",\"salt\":\"fa15aef30d031c59d28794c1aec05797d47da1a5a0661d1a8e68841a613881b5\"},\"mac\":\"3f8c47c68a3c2428c44674e770dd1c16b47a968a2ec6f5f59901cdbbe994acb0\"},\"address\":\"57da1b8f38a5ecf91e9fee8a047df0f0a88716a1\",\"name\":\"\",\"meta\":\"{}\"}"
  },
  {
    "path": "tests/config/parity-dev-constantinopole.json",
    "content": "{\n  \"name\": \"UnlimitedChain\",\n  \"engine\": {\n    \"instantSeal\": {\n      \"params\": {}\n    }\n  },\n  \"params\": {\n    \"gasLimitBoundDivisor\": \"0x0400\",\n    \"accountStartNonce\": \"0x0\",\n    \"maximumExtraDataSize\": \"0x20\",\n    \"minGasLimit\": \"0x1388\",\n    \"networkID\" : \"0x11\",\n    \"registrar\" : \"0x0000000000000000000000000000000000001337\",\n    \"eip150Transition\": \"0x0\",\n    \"eip160Transition\": \"0x0\",\n    \"eip161abcTransition\": \"0x0\",\n    \"eip161dTransition\": \"0x0\",\n    \"eip155Transition\": \"0x0\",\n    \"eip98Transition\": \"0x7fffffffffffff\",\n    \"maxCodeSize\": \"0xFFFFFF\",\n    \"maxCodeSizeTransition\": \"0x0\",\n    \"eip140Transition\": \"0x0\",\n    \"eip211Transition\": \"0x0\",\n    \"eip214Transition\": \"0x0\",\n    \"eip658Transition\": \"0x0\",\n    \"eip145Transition\": \"0x0\",\n    \"eip1014Transition\": \"0x0\",\n    \"eip1052Transition\": \"0x0\",\n    \"wasmActivationTransition\": \"0x0\"\n  },\n  \"genesis\": {\n    \"seal\": {\n      \"generic\": \"0x0\"\n    },\n    \"difficulty\": \"0x20000\",\n    \"author\": \"0x0000000000000000000000000000000000000000\",\n    \"timestamp\": \"0x00\",\n    \"parentHash\": \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n    \"extraData\": \"0x\",\n    \"gasLimit\": \"0xFFFFFF\"\n  },\n  \"accounts\": {\n    \"0000000000000000000000000000000000000001\": { \"balance\": \"1\", \"builtin\": { \"name\": \"ecrecover\", \"pricing\": { \"linear\": { \"base\": 3000, \"word\": 0 } } } },\n    \"0000000000000000000000000000000000000002\": { \"balance\": \"1\", \"builtin\": { \"name\": \"sha256\", \"pricing\": { \"linear\": { \"base\": 60, \"word\": 12 } } } },\n    \"0000000000000000000000000000000000000003\": { \"balance\": \"1\", \"builtin\": { \"name\": \"ripemd160\", \"pricing\": { \"linear\": { \"base\": 600, \"word\": 120 } } } },\n    \"0000000000000000000000000000000000000004\": { \"balance\": \"1\", \"builtin\": { \"name\": \"identity\", \"pricing\": { \"linear\": { \"base\": 15, \"word\": 3 } } } },\n    \"0000000000000000000000000000000000000005\": { \"balance\": \"1\", \"builtin\": { \"name\": \"modexp\", \"activate_at\": 0, \"pricing\": { \"modexp\": { \"divisor\": 20 } } } },\n    \"0000000000000000000000000000000000000006\": { \"balance\": \"1\", \"builtin\": { \"name\": \"alt_bn128_add\", \"activate_at\": 0, \"pricing\": { \"linear\": { \"base\": 500, \"word\": 0 } } } },\n    \"0000000000000000000000000000000000000007\": { \"balance\": \"1\", \"builtin\": { \"name\": \"alt_bn128_mul\", \"activate_at\": 0, \"pricing\": { \"linear\": { \"base\": 40000, \"word\": 0 } } } },\n    \"0000000000000000000000000000000000000008\": { \"balance\": \"1\", \"builtin\": { \"name\": \"alt_bn128_pairing\", \"activate_at\": 0, \"pricing\": { \"alt_bn128_pairing\": { \"base\": 100000, \"pair\": 80000 } } } },\n    \"0000000000000000000000000000000000001337\": { \"balance\": \"1\", \"constructor\": \"0x606060405233600060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550670de0b6b3a764000060035534610000575b612904806100666000396000f3006060604052361561013c576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306b2ff471461014157806313af40351461018c57806319362a28146101bf5780633f3935d114610248578063432ced04146102b75780634f39ca59146102eb5780636795dbcd1461032457806369fe0e2d146103c857806379ce9fac146103fd5780638da5cb5b1461045557806390b97fc1146104a457806392698814146105245780639890220b1461055d578063ac4e73f914610584578063ac72c12014610612578063c3a358251461064b578063ddca3f43146106c3578063deb931a2146106e6578063df57b74214610747578063e30bd740146107a8578063eadf976014610862578063ef5454d6146108e7578063f25eb5c114610975578063f6d339e414610984575b610000565b3461000057610172600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610a1f565b604051808215151515815260200191505060405180910390f35b34610000576101bd600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610a81565b005b346100005761022e60048080356000191690602001909190803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509190803560001916906020019091905050610ba2565b604051808215151515815260200191505060405180910390f35b346100005761029d600480803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091905050610dc9565b604051808215151515815260200191505060405180910390f35b6102d1600480803560001916906020019091905050611035565b604051808215151515815260200191505060405180910390f35b346100005761030a60048080356000191690602001909190505061115f565b604051808215151515815260200191505060405180910390f35b346100005761038660048080356000191690602001909190803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091905050611378565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34610000576103e3600480803590602001909190505061140d565b604051808215151515815260200191505060405180910390f35b346100005761043b60048080356000191690602001909190803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506114b4565b604051808215151515815260200191505060405180910390f35b34610000576104626115fb565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b346100005761050660048080356000191690602001909190803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091905050611621565b60405180826000191660001916815260200191505060405180910390f35b34610000576105436004808035600019169060200190919050506116b2565b604051808215151515815260200191505060405180910390f35b346100005761056a611715565b604051808215151515815260200191505060405180910390f35b34610000576105f8600480803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050611824565b604051808215151515815260200191505060405180910390f35b3461000057610631600480803560001916906020019091905050611d8b565b604051808215151515815260200191505060405180910390f35b34610000576106ad60048080356000191690602001909190803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091905050611dee565b6040518082815260200191505060405180910390f35b34610000576106d0611e83565b6040518082815260200191505060405180910390f35b3461000057610705600480803560001916906020019091905050611e89565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3461000057610766600480803560001916906020019091905050611ed2565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34610000576107d9600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050611f1b565b6040518080602001828103825283818151815260200191508051906020019080838360008314610828575b80518252602083111561082857602082019150602081019050602083039250610804565b505050905090810190601f1680156108545780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34610000576108cd60048080356000191690602001909190803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509190803590602001909190505061200c565b604051808215151515815260200191505060405180910390f35b346100005761095b600480803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050612236565b604051808215151515815260200191505060405180910390f35b3461000057610982612425565b005b3461000057610a0560048080356000191690602001909190803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050612698565b604051808215151515815260200191505060405180910390f35b60006000600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020805460018160011615610100020316600290049050141590505b919050565b600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141515610add57610b9f565b8073ffffffffffffffffffffffffffffffffffffffff16600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f70aea8d848e8a90fb7661b227dc522eb6395c3dac71b63cb59edd5c9899b236460405180905060405180910390a380600060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505b5b50565b6000833373ffffffffffffffffffffffffffffffffffffffff1660016000836000191660001916815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141515610c1d57610dc1565b82600160008760001916600019168152602001908152602001600020600201856040518082805190602001908083835b60208310610c705780518252602082019150602081019050602083039250610c4d565b6001836020036101000a03801982511681845116808217855250505050505090500191505090815260200160405180910390208160001916905550836040518082805190602001908083835b60208310610cdf5780518252602082019150602081019050602083039250610cbc565b6001836020036101000a038019825116818451168082178552505050505050905001915050604051809103902085600019167fb829c3e412537bbe794c048ccb9e4605bb4aaaa8e4d4c15c1a6e0c2adc1716ea866040518080602001828103825283818151815260200191508051906020019080838360008314610d82575b805182526020831115610d8257602082019150602081019050602083039250610d5e565b505050905090810190601f168015610dae5780820380516001836020036101000a031916815260200191505b509250505060405180910390a3600191505b5b509392505050565b6000813373ffffffffffffffffffffffffffffffffffffffff1660016000836040518082805190602001908083835b60208310610e1b5780518252602082019150602081019050602083039250610df8565b6001836020036101000a03801982511681845116808217855250505050505090500191505060405180910390206000191660001916815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141515610ea45761102f565b82600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000209080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10610f2d57805160ff1916838001178555610f5b565b82800160010185558215610f5b579182015b82811115610f5a578251825591602001919060010190610f3f565b5b509050610f8091905b80821115610f7c576000816000905550600101610f64565b5090565b50503373ffffffffffffffffffffffffffffffffffffffff16836040518082805190602001908083835b60208310610fcd5780518252602082019150602081019050602083039250610faa565b6001836020036101000a03801982511681845116808217855250505050505090500191505060405180910390207f098ae8581bb8bd9af1beaf7f2e9f51f31a8e5a8bfada4e303a645d71d9c9192060405180905060405180910390a3600191505b5b50919050565b600081600060016000836000191660001916815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614151561109b57611159565b6003543410156110aa57611158565b3360016000856000191660001916815260200190815260200160002060000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055503373ffffffffffffffffffffffffffffffffffffffff1683600019167f4963513eca575aba66fdcd25f267aae85958fe6fb97e75fa25d783f1a091a22160405180905060405180910390a3600191505b5b5b50919050565b6000813373ffffffffffffffffffffffffffffffffffffffff1660016000836000191660001916815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415156111da57611372565b6002600060016000866000191660001916815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020805460018160011615610100020316600290046000825580601f1061127c57506112b3565b601f0160209004906000526020600020908101906112b291905b808211156112ae576000816000905550600101611296565b5090565b5b5060016000846000191660001916815260200190815260200160002060006000820160006101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690556001820160006101000a81549073ffffffffffffffffffffffffffffffffffffffff021916905550503373ffffffffffffffffffffffffffffffffffffffff1683600019167fef1961b4d2909dc23643b309bfe5c3e5646842d98c3a58517037ef3871185af360405180905060405180910390a3600191505b5b50919050565b6000600160008460001916600019168152602001908152602001600020600201826040518082805190602001908083835b602083106113cc57805182526020820191506020810190506020830392506113a9565b6001836020036101000a0380198251168184511680821785525050505050509050019150509081526020016040518091039020546001900490505b92915050565b6000600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561146b576114af565b816003819055507f6bbc57480a46553fa4d156ce702beef5f3ad66303b0ed1a5d4cb44966c6584c3826040518082815260200191505060405180910390a1600190505b5b919050565b6000823373ffffffffffffffffffffffffffffffffffffffff1660016000836000191660001916815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614151561152f576115f4565b8260016000866000191660001916815260200190815260200160002060000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1685600019167f7b97c62130aa09acbbcbf7482630e756592496f1759eaf702f469cf64dfb779460405180905060405180910390a4600191505b5b5092915050565b600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000600160008460001916600019168152602001908152602001600020600201826040518082805190602001908083835b602083106116755780518252602082019150602081019050602083039250611652565b6001836020036101000a03801982511681845116808217855250505050505090500191505090815260200160405180910390205490505b92915050565b6000600060016000846000191660001916815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141590505b919050565b6000600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561177357611821565b7fdef931299fe61d176f949118058530c1f3f539dcb6950b4e372c9b835c33ca073073ffffffffffffffffffffffffffffffffffffffff16316040518082815260200191505060405180910390a13373ffffffffffffffffffffffffffffffffffffffff166108fc3073ffffffffffffffffffffffffffffffffffffffff16319081150290604051809050600060405180830381858888f19350505050151561181b57610000565b600190505b5b90565b60006000836040518082805190602001908083835b6020831061185c5780518252602082019150602081019050602083039250611839565b6001836020036101000a03801982511681845116808217855250505050505090500191505060405180910390203373ffffffffffffffffffffffffffffffffffffffff1660016000836000191660001916815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614151561190157611d83565b846040518082805190602001908083835b602083106119355780518252602082019150602081019050602083039250611912565b6001836020036101000a03801982511681845116808217855250505050505090500191505060405180910390209150600060016000846000191660001916815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614158015611ab4575081600019166002600060016000866000191660001916815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206040518082805460018160011615610100020316600290048015611aa15780601f10611a7f576101008083540402835291820191611aa1565b820191906000526020600020905b815481529060010190602001808311611a8d575b5050915050604051809103902060001916145b15611c79576002600060016000856000191660001916815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020805460018160011615610100020316600290046000825580601f10611b5b5750611b92565b601f016020900490600052602060002090810190611b9191905b80821115611b8d576000816000905550600101611b75565b5090565b5b5060016000836000191660001916815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16856040518082805190602001908083835b60208310611c1c5780518252602082019150602081019050602083039250611bf9565b6001836020036101000a03801982511681845116808217855250505050505090500191505060405180910390207f12491ad95fd945e444d88a894ffad3c21959880a4dcd8af99d4ae4ffc71d4abd60405180905060405180910390a35b8360016000846000191660001916815260200190815260200160002060010160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508373ffffffffffffffffffffffffffffffffffffffff16856040518082805190602001908083835b60208310611d215780518252602082019150602081019050602083039250611cfe565b6001836020036101000a03801982511681845116808217855250505050505090500191505060405180910390207f728435a0031f6a04538fcdd24922a7e06bc7bc945db03e83d22122d1bc5f28df60405180905060405180910390a3600192505b5b505092915050565b6000600060016000846000191660001916815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141590505b919050565b6000600160008460001916600019168152602001908152602001600020600201826040518082805190602001908083835b60208310611e425780518252602082019150602081019050602083039250611e1f565b6001836020036101000a0380198251168184511680821785525050505050509050019150509081526020016040518091039020546001900490505b92915050565b60035481565b600060016000836000191660001916815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690505b919050565b600060016000836000191660001916815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690505b919050565b6020604051908101604052806000815250600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015611fff5780601f10611fd457610100808354040283529160200191611fff565b820191906000526020600020905b815481529060010190602001808311611fe257829003601f168201915b505050505090505b919050565b6000833373ffffffffffffffffffffffffffffffffffffffff1660016000836000191660001916815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415156120875761222e565b82600102600160008760001916600019168152602001908152602001600020600201856040518082805190602001908083835b602083106120dd57805182526020820191506020810190506020830392506120ba565b6001836020036101000a03801982511681845116808217855250505050505090500191505090815260200160405180910390208160001916905550836040518082805190602001908083835b6020831061214c5780518252602082019150602081019050602083039250612129565b6001836020036101000a038019825116818451168082178552505050505050905001915050604051809103902085600019167fb829c3e412537bbe794c048ccb9e4605bb4aaaa8e4d4c15c1a6e0c2adc1716ea8660405180806020018281038252838181518152602001915080519060200190808383600083146121ef575b8051825260208311156121ef576020820191506020810190506020830392506121cb565b505050905090810190601f16801561221b5780820380516001836020036101000a031916815260200191505b509250505060405180910390a3600191505b5b509392505050565b6000600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415156122945761241f565b82600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000209080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061231d57805160ff191683800117855561234b565b8280016001018555821561234b579182015b8281111561234a57825182559160200191906001019061232f565b5b50905061237091905b8082111561236c576000816000905550600101612354565b5090565b50508173ffffffffffffffffffffffffffffffffffffffff16836040518082805190602001908083835b602083106123bd578051825260208201915060208101905060208303925061239a565b6001836020036101000a03801982511681845116808217855250505050505090500191505060405180910390207f098ae8581bb8bd9af1beaf7f2e9f51f31a8e5a8bfada4e303a645d71d9c9192060405180905060405180910390a3600190505b5b92915050565b3373ffffffffffffffffffffffffffffffffffffffff16600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060405180828054600181600116156101000203166002900480156124d65780601f106124b45761010080835404028352918201916124d6565b820191906000526020600020905b8154815290600101906020018083116124c2575b505091505060405180910390207f12491ad95fd945e444d88a894ffad3c21959880a4dcd8af99d4ae4ffc71d4abd60405180905060405180910390a360016000600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060405180828054600181600116156101000203166002900480156125b05780601f1061258e5761010080835404028352918201916125b0565b820191906000526020600020905b81548152906001019060200180831161259c575b505091505060405180910390206000191660001916815260200190815260200160002060010160006101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020805460018160011615610100020316600290046000825580601f1061265d5750612694565b601f01602090049060005260206000209081019061269391905b8082111561268f576000816000905550600101612677565b5090565b5b505b565b6000833373ffffffffffffffffffffffffffffffffffffffff1660016000836000191660001916815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141515612713576128d0565b8273ffffffffffffffffffffffffffffffffffffffff16600102600160008760001916600019168152602001908152602001600020600201856040518082805190602001908083835b6020831061277f578051825260208201915060208101905060208303925061275c565b6001836020036101000a03801982511681845116808217855250505050505090500191505090815260200160405180910390208160001916905550836040518082805190602001908083835b602083106127ee57805182526020820191506020810190506020830392506127cb565b6001836020036101000a038019825116818451168082178552505050505050905001915050604051809103902085600019167fb829c3e412537bbe794c048ccb9e4605bb4aaaa8e4d4c15c1a6e0c2adc1716ea866040518080602001828103825283818151815260200191508051906020019080838360008314612891575b8051825260208311156128915760208201915060208101905060208303925061286d565b505050905090810190601f1680156128bd5780820380516001836020036101000a031916815260200191505b509250505060405180910390a3600191505b5b5093925050505600a165627a7a7230582066b2da4773a0f1d81efe071c66b51c46868a871661efd18c0f629353ff4c1f9b0029\" },\n    \"00a329c0648769a73afac7f9381e08fb43dbea72\": { \"balance\": \"1606938044258990275541962092341162602522202993782792835301376\" },\n    \"6c626f45e3b7ae5a3998478753634790fd0e82ee\": { \"balance\": \"1606938044258990275541962092341162602522202993782792835301376\" },\n    \"50ff810797f75f6bfbf2227442e0c961a8562f4c\": { \"balance\": \"1606938044258990275541962092341162602522202993782792835301376\" },\n    \"5beb2d3aa2333a524703af18310acff462c04723\": { \"balance\": \"1606938044258990275541962092341162602522202993782792835301376\" },\n    \"57da1b8f38a5ecf91e9fee8a047df0f0a88716a1\": { \"balance\": \"1606938044258990275541962092341162602522202993782792835301376\" }\n  }\n}\n"
  },
  {
    "path": "tests/conftest.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2017-2019 reverendus, EdNoepel\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nimport logging\nimport pytest\n\nfrom web3 import Web3, HTTPProvider\n\nfrom pymaker import Address, web3_via_http\nfrom pymaker.auctions import Flipper, Flapper, Flopper\nfrom pymaker.deployment import Deployment, DssDeployment\nfrom pymaker.dss import Vat, Vow, Cat, Dog, Jug, Pot\nfrom pymaker.keys import register_keys\n\n\n@pytest.fixture(scope='session')\ndef new_deployment() -> Deployment:\n    return Deployment()\n\n\n@pytest.fixture()\ndef deployment(new_deployment: Deployment) -> Deployment:\n    new_deployment.reset()\n    return new_deployment\n\n\n@pytest.fixture(scope=\"session\")\ndef web3() -> Web3:\n    # for local dockerized parity testchain\n    web3 = web3_via_http(\"http://0.0.0.0:8545\")\n    web3.eth.defaultAccount = \"0x50FF810797f75f6bfbf2227442e0c961a8562F4C\"\n    register_keys(web3,\n                  [\"key_file=tests/config/keys/UnlimitedChain/key1.json,pass_file=/dev/null\",\n                   \"key_file=tests/config/keys/UnlimitedChain/key2.json,pass_file=/dev/null\",\n                   \"key_file=tests/config/keys/UnlimitedChain/key3.json,pass_file=/dev/null\",\n                   \"key_file=tests/config/keys/UnlimitedChain/key4.json,pass_file=/dev/null\",\n                   \"key_file=tests/config/keys/UnlimitedChain/key.json,pass_file=/dev/null\"])\n\n    # reduce logspew\n    logging.getLogger(\"web3\").setLevel(logging.INFO)\n    logging.getLogger(\"urllib3\").setLevel(logging.INFO)\n    logging.getLogger(\"asyncio\").setLevel(logging.INFO)\n\n    assert len(web3.eth.accounts) > 3\n    return web3\n\n\n@pytest.fixture(scope=\"session\")\ndef our_address(web3) -> Address:\n    return Address(web3.eth.accounts[0])\n\n\n@pytest.fixture(scope=\"session\")\ndef other_address(web3) -> Address:\n    return Address(web3.eth.accounts[1])\n\n\n@pytest.fixture(scope=\"session\")\ndef deployment_address(web3) -> Address:\n    # FIXME: Unsure why it isn't added to web3.eth.accounts list\n    return Address(\"0x00a329c0648769A73afAc7F9381E08FB43dBEA72\")\n\n\n@pytest.fixture(scope=\"session\")\ndef mcd(web3) -> DssDeployment:\n    # for local dockerized parity testchain\n    deployment = DssDeployment.from_node(web3=web3)\n    validate_contracts_loaded(deployment)\n    initialize_collaterals(deployment)\n    return deployment\n\n\ndef validate_contracts_loaded(deployment: DssDeployment):\n    assert isinstance(deployment.vat, Vat)\n    assert deployment.vat.address is not None\n    assert isinstance(deployment.vow, Vow)\n    assert deployment.vow.address is not None\n    assert isinstance(deployment.cat, Cat)\n    assert deployment.cat.address is not None\n    assert isinstance(deployment.dog, Dog)\n    assert deployment.dog.address is not None\n    assert isinstance(deployment.jug, Jug)\n    assert deployment.jug.address is not None\n    assert isinstance(deployment.flapper, Flapper)\n    assert deployment.flapper.address is not None\n    assert isinstance(deployment.flopper, Flopper)\n    assert deployment.flopper.address is not None\n    assert isinstance(deployment.pot, Pot)\n    assert deployment.pot.address is not None\n\n\ndef initialize_collaterals(deployment: DssDeployment):\n    for collateral in deployment.collaterals.values():\n        if collateral.clipper:\n            collateral.clipper.upchost().transact(from_address=deployment_address(deployment.web3))\n"
  },
  {
    "path": "tests/dss_token.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2017-2018 reverendus\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\nfrom web3 import Web3\n\nfrom pymaker import Contract, Address, Transact\nfrom pymaker.dss import Urn, Ilk\nfrom pymaker.numeric import Wad\n\n\nclass GemMock(Contract):\n    \"\"\"A client for `GemMock` contract.\n    \"\"\"\n\n    abi = Contract._load_abi(__name__, 'abi/GemMock.abi')\n    bin = Contract._load_bin(__name__, 'abi/GemMock.bin')\n\n    def __init__(self, web3: Web3, address: Address):\n        assert(isinstance(web3, Web3))\n        assert(isinstance(address, Address))\n\n        self.web3 = web3\n        self.address = address\n        self._contract = self._get_contract(web3, self.abi, address)\n\n    @staticmethod\n    def deploy(web3: Web3, vat: Address, ilk: Ilk, gem: Address):\n        assert isinstance(web3, Web3)\n        assert isinstance(vat, Address)\n        assert isinstance(ilk, Ilk)\n        assert isinstance(gem, Address)\n\n        return GemMock(web3=web3, address=Contract._deploy(web3, GemMock.abi, GemMock.bin, [vat.address,\n                                                                                            ilk.toBytes(),\n                                                                                            gem.address]))\n    def ilk(self):\n        return Ilk.fromBytes(self._contract.functions.ilk().call())\n\n    def join(self, urn: Urn, value: Wad) -> Transact:\n        assert(isinstance(urn, Urn))\n        assert(isinstance(value, Wad))\n\n        return Transact(self, self.web3, self.abi, self.address, self._contract,\n                        'join', [urn.toBytes() , value.value])\n\n    def hope(self, guy: Address) -> Transact:\n        assert(isinstance(guy, Address))\n\n        return Transact(self, self.web3, self.abi, self.address, self._contract,\n                        'hope', [guy.address])\n\n    def __eq__(self, other):\n        return self.address == other.address\n\n    def __repr__(self):\n        return f\"GemMock('{self.address}')\"\n"
  },
  {
    "path": "tests/helpers.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2017-2018 reverendus\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nimport time\nfrom unittest.mock import Mock\n\nfrom web3 import Web3\n\n\ndef is_hashable(v):\n    \"\"\"Determine whether `v` can be hashed.\"\"\"\n    try:\n        hash(v)\n    except TypeError:\n        return False\n    return True\n\n\ndef wait_until_mock_called(mock: Mock):\n    while not mock.called:\n        pass\n    return mock.call_args[0]\n\n\ndef time_travel_by(web3: Web3, seconds: int):\n    assert(isinstance(web3, Web3))\n    assert(isinstance(seconds, int))\n\n    if \"Parity\" in web3.clientVersion:\n        print(f\"time travel unsupported by parity; waiting {seconds} seconds\")\n        time.sleep(seconds)\n        # force a block mining to have a correct timestamp in latest block\n        web3.eth.sendTransaction({'from': web3.eth.accounts[0], 'to': web3.eth.accounts[1], 'value': 1})\n    else:\n        web3.manager.request_blocking(\"evm_increaseTime\", [seconds])\n        # force a block mining to have a correct timestamp in latest block\n        web3.manager.request_blocking(\"evm_mine\", [])\n\n\ndef snapshot(web3: Web3):\n    assert(isinstance(web3, Web3))\n\n    return web3.manager.request_blocking(\"evm_snapshot\", [])\n\n\ndef reset(web3: Web3, snap_id):\n    assert(isinstance(web3, Web3))\n\n    return web3.manager.request_blocking(\"evm_revert\", [snap_id])\n"
  },
  {
    "path": "tests/manual_test_async_tx.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2020 EdNoepel\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nimport asyncio\nimport logging\nimport os\nimport sys\nimport threading\nimport time\n\nfrom pymaker import Address, web3_via_http\nfrom pymaker.deployment import DssDeployment\nfrom pymaker.gas import FixedGasPrice, GeometricGasPrice\nfrom pymaker.keys import register_keys\nfrom pymaker.numeric import Wad\n\nlogging.basicConfig(format='%(asctime)-15s %(levelname)-8s %(message)s', level=logging.DEBUG)\n# reduce logspew\nlogging.getLogger('urllib3').setLevel(logging.INFO)\nlogging.getLogger(\"web3\").setLevel(logging.INFO)\nlogging.getLogger(\"asyncio\").setLevel(logging.INFO)\nlogging.getLogger(\"requests\").setLevel(logging.INFO)\n\npool_size = int(sys.argv[3]) if len(sys.argv) > 3 else 10\nweb3 = web3_via_http(endpoint_uri=os.environ['ETH_RPC_URL'], http_pool_size=pool_size)\nweb3.eth.defaultAccount = sys.argv[1]   # ex: 0x0000000000000000000000000000000aBcdef123\nregister_keys(web3, [sys.argv[2]])      # ex: key_file=~keys/default-account.json,pass_file=~keys/default-account.pass\n\nmcd = DssDeployment.from_node(web3)\nour_address = Address(web3.eth.defaultAccount)\nweth = DssDeployment.from_node(web3).collaterals['ETH-A'].gem\n\nGWEI = 1000000000\nslow_gas = GeometricGasPrice(initial_price=int(15 * GWEI), every_secs=42, max_price=200 * GWEI)\nfast_gas = GeometricGasPrice(initial_price=int(30 * GWEI), every_secs=42, max_price=200 * GWEI)\n\n\nclass TestApp:\n    def main(self):\n        self.test_replacement()\n        self.test_simultaneous()\n        self.shutdown()\n\n    def test_replacement(self):\n        first_tx = weth.deposit(Wad(4))\n        logging.info(f\"Submitting first TX with gas price deliberately too low\")\n        self._run_future(first_tx.transact_async(gas_price=slow_gas))\n        time.sleep(0.5)\n\n        second_tx = weth.deposit(Wad(6))\n        logging.info(f\"Replacing first TX with legitimate gas price\")\n        second_tx.transact(replace=first_tx, gas_price=fast_gas)\n\n        assert first_tx.replaced\n\n    def test_simultaneous(self):\n        self._run_future(weth.deposit(Wad(1)).transact_async(gas_price=fast_gas))\n        self._run_future(weth.deposit(Wad(3)).transact_async(gas_price=fast_gas))\n        self._run_future(weth.deposit(Wad(5)).transact_async(gas_price=fast_gas))\n        self._run_future(weth.deposit(Wad(7)).transact_async(gas_price=fast_gas))\n        time.sleep(33)\n\n    def shutdown(self):\n        balance = weth.balance_of(our_address)\n        if Wad(0) < balance < Wad(100):  # this account's tiny WETH balance came from this test\n            logging.info(f\"Unwrapping {balance} WETH\")\n            assert weth.withdraw(balance).transact(gas_price=fast_gas)\n        elif balance >= Wad(22):  # user already had a balance, so unwrap what a successful test would have consumed\n            logging.info(f\"Unwrapping 12 WETH\")\n            assert weth.withdraw(Wad(22)).transact(gas_price=fast_gas)\n\n    @staticmethod\n    def _run_future(future):\n        def worker():\n            loop = asyncio.new_event_loop()\n            asyncio.set_event_loop(loop)\n            try:\n                asyncio.get_event_loop().run_until_complete(future)\n            finally:\n                loop.close()\n\n        thread = threading.Thread(target=worker, daemon=True)\n        thread.start()\n\n\nif __name__ == '__main__':\n    TestApp().main()\n"
  },
  {
    "path": "tests/manual_test_cdpmanager.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2020 ith-harvey\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n\nimport sys\nimport os\nfrom web3 import Web3, HTTPProvider\n\nfrom pymaker import Address\nfrom pymaker.deployment import DssDeployment\nfrom pymaker.keys import register_keys\nfrom pymaker.numeric import Wad\nfrom pymaker.dsr import Dsr\n\nendpoint_uri = f\"{os.environ['SERVER_ETH_RPC_HOST']}:{os.environ['SERVER_ETH_RPC_PORT']}\"\nweb3 = Web3(HTTPProvider(endpoint_uri=endpoint_uri,\n                         request_kwargs={\"timeout\": 10}))\nweb3.eth.defaultAccount = sys.argv[1]   # ex: 0x0000000000000000000000000000000aBcdef123\nregister_keys(web3, [sys.argv[2]])      # ex: key_file=~keys/default-account.json,pass_file=~keys/default-account.pass\ncdpid = int(sys.argv[3])\n\nmcd = DssDeployment.from_network(web3, \"kovan\")\nour_address = Address(web3.eth.defaultAccount)\ndsr_client = Dsr(mcd, our_address)\nprint(our_address)\n\nprint(f\"Default account: {our_address.address}\")\nif dsr_client.has_proxy():\n    proxy = dsr_client.get_proxy()\n    print(f\"{our_address} has a DS-Proxy - {proxy.address.address}, test will continue\")\n\n    print(f\"Urn of Cdp ID {cdpid} - {mcd.cdp_manager.urn(cdpid)}\")\n    print(f\"Owner of CDP ID {cdpid} - {mcd.cdp_manager.owns(cdpid)}\")\n    print(f\"List of CDP IDs next to and previous to {cdpid} - {mcd.cdp_manager.list(cdpid)}\")\n    print(f\"Ilk of CDP ID {cdpid} - {mcd.cdp_manager.ilk(cdpid)}\")\n\n    print(f\"First of Cdp ID for account {proxy.address.address} - {mcd.cdp_manager.first(proxy.address)}\")\n    print(f\"Last of Cdp ID for account {proxy.address.address} - {mcd.cdp_manager.last(proxy.address)}\")\n    print(f\"Number of all CDPs created via DS-Cdp-Manager contract {proxy.address.address} - {mcd.cdp_manager.count(proxy.address)}\")\n\nelse:\n    print(f\"{our_address} does not have a DS-Proxy. Please create a cdp on kovan via Oasis.app (to create a proxy) to perform this test\")\n\n\n"
  },
  {
    "path": "tests/manual_test_dsr.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2019 grandizzy\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n\nimport sys\nfrom web3 import Web3, HTTPProvider\n\nfrom pymaker import Address\nfrom pymaker.deployment import DssDeployment\nfrom pymaker.keys import register_keys\nfrom pymaker.numeric import Wad\nfrom pymaker.dsr import Dsr\n\n\nweb3 = Web3(HTTPProvider(endpoint_uri=\"http://0.0.0.0:8545\",\n                         request_kwargs={\"timeout\": 10}))\nweb3.eth.defaultAccount = sys.argv[1]   # ex: 0x0000000000000000000000000000000aBcdef123\nregister_keys(web3, [sys.argv[2]])      # ex: key_file=~keys/default-account.json,pass_file=~keys/default-account.pass\n\nmcd = DssDeployment.from_network(web3, \"kovan\")\nour_address = Address(web3.eth.defaultAccount)\nprint(our_address)\n\ndsr_client = Dsr(mcd, our_address)\n\nprint(f\"Chi: {dsr_client.chi()}\")\nprint(f\"Total DAI: {dsr_client.get_total_dai()}\")\nprint(f\"DSR: {dsr_client.dsr()}\")\n\nproxy = dsr_client.get_proxy()\nprint(f\"Has Proxy: {dsr_client.has_proxy()}\")\n\nif not dsr_client.has_proxy():\n    dsr_client.build_proxy().transact()\n\nproxy = dsr_client.get_proxy()\nprint(f\"Proxy address: {proxy.address.address}\")\n\nprint(f\"Balance: {dsr_client.get_balance(proxy.address)}\")\n\n# approve proxy to use 10 DAI from account\ndsr_client.mcd.dai.approve(proxy.address, Wad.from_number(10)).transact()\n\ndsr_client.join(Wad.from_number(2.2), proxy).transact()\nprint(f\"Balance: {dsr_client.get_balance(proxy.address)}\")\n\ndsr_client.exit(Wad.from_number(1.01), proxy).transact()\nprint(f\"Balance: {dsr_client.get_balance(proxy.address)}\")\n\ndsr_client.exit_all(proxy).transact()\nprint(f\"Balance: {dsr_client.get_balance(proxy.address)}\")\n\n"
  },
  {
    "path": "tests/manual_test_goerli.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2020-2021 EdNoepel\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nimport logging\nimport os\nimport sys\nfrom web3 import Web3, HTTPProvider\n\nfrom pymaker import Address, eth_transfer, web3_via_http\nfrom pymaker.gas import GeometricGasPrice\nfrom pymaker.lifecycle import Lifecycle\nfrom pymaker.keys import register_keys\nfrom pymaker.numeric import Wad\nfrom pymaker.token import EthToken\n\nlogging.basicConfig(format='%(asctime)-15s %(levelname)-8s %(message)s', level=logging.DEBUG)\n# reduce logspew\nlogging.getLogger('urllib3').setLevel(logging.INFO)\nlogging.getLogger(\"web3\").setLevel(logging.INFO)\nlogging.getLogger(\"asyncio\").setLevel(logging.INFO)\nlogging.getLogger(\"requests\").setLevel(logging.INFO)\n\nendpoint_uri = sys.argv[1]\nweb3 = web3_via_http(endpoint_uri, timeout=10)\nprint(web3.clientVersion)\n\n\"\"\"\nArgument:           Reqd?   Example:\nEthereum node URI   yes     https://localhost:8545\nEthereum address    no      0x0000000000000000000000000000000aBcdef123\nPrivate key         no      key_file=~keys/default-account.json,pass_file=~keys/default-account.pass\nGas price (GWEI)    no      9\n\"\"\"\n\n\nif len(sys.argv) > 3:\n    web3.eth.defaultAccount = sys.argv[2]\n    register_keys(web3, [sys.argv[3]])\n    our_address = Address(web3.eth.defaultAccount)\n    run_transactions = True\nelif len(sys.argv) > 2:\n    our_address = Address(sys.argv[2])\n    run_transactions = False\nelse:\n    our_address = None\n    run_transactions = False\n\ngas_price = None if len(sys.argv) <= 4 else \\\n    GeometricGasPrice(initial_price=int(float(sys.argv[4]) * GeometricGasPrice.GWEI),\n                      every_secs=15,\n                      max_price=100 * GeometricGasPrice.GWEI)\n\neth = EthToken(web3, Address.zero())\n\n\nclass TestApp:\n    def main(self):\n        with Lifecycle(web3) as lifecycle:\n            lifecycle.on_block(self.on_block)\n\n    def on_block(self):\n        block = web3.eth.blockNumber\n        logging.info(f\"Found block; web3.eth.blockNumber={block}\")\n\n        if run_transactions and block % 3 == 0:\n            # dummy transaction: send 0 ETH to ourself\n            eth_transfer(web3=web3, to=our_address, amount=Wad(0)).transact(\n                from_address=our_address, gas=21000, gas_price=gas_price)\n\n        if our_address:\n            logging.info(f\"Eth balance is {eth.balance_of(our_address)}\")\n\n\nif __name__ == '__main__':\n    TestApp().main()\n"
  },
  {
    "path": "tests/manual_test_mcd.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2019-2020 EdNoepel\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nimport os\nimport sys\nfrom web3 import Web3, HTTPProvider\n\nfrom pymaker import Address\nfrom pymaker.deployment import DssDeployment\nfrom pymaker.keys import register_keys\nfrom pymaker.numeric import Wad\nfrom pymaker.oracles import OSM\n\nassert os.environ['ETH_RPC_URL']\nweb3 = Web3(HTTPProvider(endpoint_uri=os.environ['ETH_RPC_URL'], request_kwargs={\"timeout\": 10}))\nour_address = None\nif len(sys.argv) > 1:\n    web3.eth.defaultAccount = sys.argv[1]   # ex: 0x0000000000000000000000000000000aBcdef123\n    our_address = Address(web3.eth.defaultAccount)\nif len(sys.argv) > 2:\n    register_keys(web3, [sys.argv[2]])  # ex: key_file=~keys/default-account.json,pass_file=~keys/default-account.pass\n    run_transactions = True\nelse:\n    run_transactions = False\nmcd = DssDeployment.from_node(web3)\n\n# Print a list of collaterals available for this deployment of MCD\nfor collateral in mcd.collaterals.values():\n    osm: OSM = collateral.pip\n    liquidation = \"clip\" if collateral.clipper else \"flip\"\n    print(f\"Found {collateral.ilk.name:>15} - {collateral.gem.name():<21} with {collateral.adapter.dec():>2} decimals, \" \n          f\"OSM price {round(float(osm.peek()), 3):>14}, \"\n          f\"rate {float(collateral.ilk.rate):<18}, using {liquidation} liquidations\")\n\n# Choose the desired collateral; in this case we'll wrap some Eth\ncollateral = mcd.collaterals['ETH-A']\nilk = collateral.ilk\n\nif run_transactions:\n    # Determine minimum amount of Dai which can be drawn\n    dai_amount = Wad(ilk.dust)\n    # Set an amount of collateral to join and an amount of Dai to draw\n    collateral_amount = Wad.from_number(105)\n    if collateral.gem.balance_of(our_address) > collateral_amount:\n        if collateral.ilk.name.startswith(\"ETH\"):\n            # Wrap ETH to produce WETH\n            assert collateral.gem.deposit(collateral_amount).transact()\n\n        # Add collateral and allocate the desired amount of Dai\n        collateral.approve(our_address)\n        assert collateral.adapter.join(our_address, collateral_amount).transact()\n        assert mcd.vat.frob(ilk, our_address, dink=collateral_amount, dart=Wad(0)).transact()\n        assert mcd.vat.frob(ilk, our_address, dink=Wad(0), dart=dai_amount).transact()\n        print(f\"Urn balance: {mcd.vat.urn(ilk, our_address)}\")\n        print(f\"Dai balance: {mcd.vat.dai(our_address)}\")\n\n        # Mint and withdraw our Dai\n        mcd.approve_dai(our_address)\n        assert mcd.dai_adapter.exit(our_address, dai_amount).transact()\n        print(f\"Dai balance after withdrawal:  {mcd.vat.dai(our_address)}\")\n\n        # Repay (and burn) our Dai\n        assert mcd.dai_adapter.join(our_address, dai_amount).transact()\n        print(f\"Dai balance after repayment:   {mcd.vat.dai(our_address)}\")\n\n        # Withdraw our collateral; stability fee accumulation may make these revert\n        assert mcd.vat.frob(ilk, our_address, dink=Wad(0), dart=dai_amount*-1).transact()\n        assert mcd.vat.frob(ilk, our_address, dink=collateral_amount*-1, dart=Wad(0)).transact()\n        assert collateral.adapter.exit(our_address, collateral_amount).transact()\n        print(f\"Dai balance w/o collateral:    {mcd.vat.dai(our_address)}\")\n    else:\n        print(f\"Not enough {ilk.name} to join to the vat\")\n\nif our_address:\n    print(f\"Collateral balance: {mcd.vat.gem(ilk, our_address)}\")\n    print(f\"Urn balance: {mcd.vat.urn(ilk, our_address)}\")\n"
  },
  {
    "path": "tests/manual_test_node.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2020 EdNoepel\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nimport logging\nimport os\nimport sys\nfrom web3 import Web3, HTTPProvider\n\nfrom pymaker import Address\nfrom pymaker.lifecycle import Lifecycle\nfrom pymaker.deployment import DssDeployment\nfrom pymaker.keys import register_keys\nfrom pymaker.numeric import Wad\n\nlogging.basicConfig(format='%(asctime)-15s %(levelname)-8s %(message)s', level=logging.DEBUG)\n# reduce logspew\nlogging.getLogger('urllib3').setLevel(logging.INFO)\nlogging.getLogger(\"web3\").setLevel(logging.INFO)\nlogging.getLogger(\"asyncio\").setLevel(logging.INFO)\nlogging.getLogger(\"requests\").setLevel(logging.INFO)\n\nendpoint_uri = sys.argv[1]              # ex: https://localhost:8545\nweb3 = Web3(HTTPProvider(endpoint_uri=endpoint_uri, request_kwargs={\"timeout\": 30}))\nif len(sys.argv) > 3:\n    web3.eth.defaultAccount = sys.argv[2]  # ex: 0x0000000000000000000000000000000aBcdef123\n    register_keys(web3, [sys.argv[3]])      # ex: key_file=~keys/default-account.json,pass_file=~keys/default-account.pass\n    our_address = Address(web3.eth.defaultAccount)\n    run_transactions = True\nelif len(sys.argv) > 2:\n    our_address = Address(sys.argv[2])\n    run_transactions = False\nelse:\n    our_address = None\n    run_transactions = False\n\nmcd = DssDeployment.from_node(web3)\ncollateral = mcd.collaterals['ETH-A']\nilk = collateral.ilk\nif run_transactions:\n    collateral.approve(our_address)\npast_blocks = 100\n\n\nclass TestApp:\n    def __init__(self):\n        self.amount = Wad(3)\n        self.joined = Wad(0)\n\n    def main(self):\n        with Lifecycle(web3) as lifecycle:\n            lifecycle.on_shutdown(self.on_shutdown)\n            lifecycle.on_block(self.on_block)\n\n    def on_block(self):\n        if run_transactions:\n            logging.info(f\"Found block {web3.eth.blockNumber}, joining {self.amount} {ilk.name}  to our urn\")\n            collateral.gem.deposit(self.amount).transact()\n            assert collateral.adapter.join(our_address, self.amount).transact()\n            self.joined += self.amount\n        else:\n            logging.info(f\"Found block; web3.eth.blockNumber={web3.eth.blockNumber}\")\n        if our_address:\n            logging.info(f\"Urn balance is {mcd.vat.gem(ilk, our_address)} {ilk.name}\")\n        # self.request_history()\n\n    def request_history(self):\n        logs = mcd.vat.past_frobs(web3.eth.blockNumber - past_blocks)\n        logging.info(f\"Found {len(logs)} frobs in the past {past_blocks} blocks\")\n\n    def on_shutdown(self):\n        if run_transactions and self.joined > Wad(0):\n            logging.info(f\"Exiting {self.joined} {ilk.name} from our urn\")\n            assert collateral.adapter.exit(our_address, self.joined).transact()\n            assert collateral.gem.withdraw(self.joined).transact()\n\n\nif __name__ == '__main__':\n    TestApp().main()\n"
  },
  {
    "path": "tests/manual_test_tx_recovery.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2020 EdNoepel\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nimport asyncio\nimport logging\nimport os\nimport sys\nimport time\nimport threading\nfrom pprint import pprint\n\nfrom pymaker import Address, get_pending_transactions, Wad, web3_via_http\nfrom pymaker.deployment import DssDeployment\nfrom pymaker.gas import FixedGasPrice, GeometricGasPrice\nfrom pymaker.keys import register_keys\n\nlogging.basicConfig(format='%(asctime)-15s %(levelname)-8s %(message)s', level=logging.DEBUG)\n# reduce logspew\nlogging.getLogger('urllib3').setLevel(logging.INFO)\nlogging.getLogger(\"web3\").setLevel(logging.INFO)\nlogging.getLogger(\"asyncio\").setLevel(logging.INFO)\nlogging.getLogger(\"requests\").setLevel(logging.INFO)\n\nweb3 = web3_via_http(endpoint_uri=os.environ['ETH_RPC_URL'])\nweb3.eth.defaultAccount = sys.argv[1]   # ex: 0x0000000000000000000000000000000aBcdef123\nregister_keys(web3, [sys.argv[2]])      # ex: key_file=~keys/default-account.json,pass_file=~keys/default-account.pass\nour_address = Address(web3.eth.defaultAccount)\nweth = DssDeployment.from_node(web3).collaterals['ETH-A'].gem\nstuck_txes_to_submit = int(sys.argv[3]) if len(sys.argv) > 3 else 1\n\nGWEI = 1000000000\nincreasing_gas = GeometricGasPrice(initial_price=int(1 * GWEI), every_secs=30, coefficient=1.5, max_price=100 * GWEI)\n\n\nclass TestApp:\n    def main(self):\n        self.startup()\n\n        pending_txes = get_pending_transactions(web3)\n        pprint(list(map(lambda t: f\"{t.name()} with gas {t.current_gas}\", pending_txes)))\n\n        if len(pending_txes) > 0:\n            while len(pending_txes) > 0:\n                pending_txes[0].cancel(gas_price=increasing_gas)\n                # After the synchronous cancel, wait to see if subsequent transactions get mined\n                time.sleep(15)\n                pending_txes = get_pending_transactions(web3)\n        else:\n            logging.info(f\"No pending transactions were found; submitting {stuck_txes_to_submit}\")\n            for i in range(1, stuck_txes_to_submit+1):\n                self._run_future(weth.deposit(Wad(i)).transact_async(gas_price=FixedGasPrice(int(0.4 * i * GWEI))))\n            time.sleep(2)\n\n        self.shutdown()\n\n    def startup(self):\n        pass\n\n    def shutdown(self):\n        pass\n\n    @staticmethod\n    def _run_future(future):\n        def worker():\n            loop = asyncio.new_event_loop()\n            asyncio.set_event_loop(loop)\n            try:\n                asyncio.get_event_loop().run_until_complete(future)\n            finally:\n                loop.close()\n\n        thread = threading.Thread(target=worker, daemon=True)\n        thread.start()\n\n\nif __name__ == '__main__':\n    TestApp().main()\n"
  },
  {
    "path": "tests/manual_test_zrxv2.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2017-2018 reverendus\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nimport sys\nimport time\n\nfrom web3 import EthereumTesterProvider, Web3, HTTPProvider\n\nfrom pymaker import Address\nfrom pymaker.approval import directly\nfrom pymaker.numeric import Wad\nfrom pymaker.token import DSToken, ERC20Token\nfrom pymaker.util import bytes_to_hexstring\nfrom pymaker.zrxv2 import ZrxExchangeV2, Order, ZrxRelayerApiV2, ERC20Asset\nfrom tests.helpers import is_hashable, wait_until_mock_called\n\n#EXCHANGE_ADDR = '0x4f833a24e1f95d70f028921e27040ca56e09ab0b'  # Mainnet\nEXCHANGE_ADDR = sys.argv[1]\nSRAV2_URL = 'https://kovan-staging.ercdex.com/api'\n\nKOVAN_DAI = Address('0xc4375b7de8af5a38a93548eb8453a498222c4ff2')\nKOVAN_WETH = Address('0xd0a1e359811322d97991e03f863a0c30c2cf029c')\n\n\nweb3 = Web3(HTTPProvider('http://localhost:8545'))\nweb3.eth.defaultAccount = web3.eth.accounts[0]\n\nexchange = ZrxExchangeV2(web3=web3, address=Address(EXCHANGE_ADDR))\n#exchange.approve([ERC20Token(web3=web3, address=KOVAN_DAI),\n#                  ERC20Token(web3=web3, address=KOVAN_WETH)], directly())\n\norder = exchange.create_order(pay_asset=ERC20Asset(KOVAN_WETH),\n                              pay_amount=Wad.from_number(0.1),\n                              buy_asset=ERC20Asset(KOVAN_DAI),\n                              buy_amount=Wad.from_number(25),\n                              expiration=int(time.time())+60*35)\n\napi = ZrxRelayerApiV2(exchange=exchange, api_server=SRAV2_URL)\norder = api.configure_order(order)\norder = exchange.sign_order(order)\n#print(order)\n\n#print(api.submit_order(order))\n#print(api.get_orders(KOVAN_WETH, KOVAN_DAI))\nprint(api.get_orders_by_maker(Address(web3.eth.defaultAccount)))\n\n#print(exchange.past_fill(500))\n"
  },
  {
    "path": "tests/test_approval.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2017-2018 reverendus\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nimport asyncio\nfrom unittest.mock import MagicMock\n\nimport pytest\nfrom web3 import HTTPProvider\nfrom web3 import Web3\n\nfrom pymaker import Address\nfrom pymaker import Wad\nfrom pymaker.approval import directly, via_tx_manager\nfrom pymaker.gas import FixedGasPrice\nfrom pymaker.token import DSToken\nfrom pymaker.transactional import TxManager\n\n\nclass FailingTransact:\n    def transact(self):\n        return None\n\n    async def transact_async(self):\n        return None\n\n\ndef setup_module():\n    global web3, our_address, second_address, third_address\n    web3 = Web3(HTTPProvider(\"http://localhost:8555\"))\n    web3.eth.defaultAccount = web3.eth.accounts[0]\n    our_address = Address(web3.eth.defaultAccount)\n    second_address = Address(web3.eth.accounts[1])\n    third_address = Address(web3.eth.accounts[2])\n\n\ndef setup_function():\n    global token\n    token = DSToken.deploy(web3, 'ABC')\n\n\ndef test_direct_approval():\n    # given\n    global web3, our_address, second_address, token\n\n    # when\n    directly()(token, second_address, \"some-name\")\n\n    # then\n    assert token.allowance_of(our_address, second_address) == Wad(2**256-1)\n\n\ndef test_direct_approval_should_obey_from_address():\n    # given\n    global web3, our_address, second_address, third_address, token\n    # and\n    # [there is already approval from the `defaultAccount`]\n    # [so that we make sure we check for the existing approval properly]\n    directly()(token, second_address, \"some-name\")\n\n    # when\n    directly(from_address=third_address)(token, second_address, \"some-name\")\n\n    # then\n    assert token.allowance_of(third_address, second_address) == Wad(2**256-1)\n\n\ndef test_direct_approval_should_obey_gas_price():\n    # given\n    global web3, our_address, second_address, token\n\n    # when\n    directly(gas_price=FixedGasPrice(25000000000))(token, second_address, \"some-name\")\n\n    # then\n    assert web3.eth.getBlock('latest', full_transactions=True).transactions[0].gasPrice == 25000000000\n\n\ndef test_direct_approval_should_not_approve_if_already_approved():\n    # given\n    global web3, our_address, second_address, token\n    token.approve(second_address, Wad(2**248+17)).transact()\n\n    # when\n    directly()(token, second_address, \"some-name\")\n\n    # then\n    assert token.allowance_of(our_address, second_address) == Wad(2**248+17)\n\n\ndef test_direct_approval_should_raise_exception_if_approval_fails():\n    # given\n    global web3, our_address, second_address, token\n    token.approve = MagicMock(return_value=FailingTransact())\n\n    # expect\n    with pytest.raises(Exception):\n        directly()(token, second_address, \"some-name\")\n\n\ndef test_via_tx_manager_approval():\n    # given\n    global web3, our_address, second_address, token\n    tx = TxManager.deploy(web3)\n\n    # when\n    via_tx_manager(tx)(token, second_address, \"some-name\")\n\n    # then\n    assert token.allowance_of(tx.address, second_address) == Wad(2**256-1)\n\n\ndef test_via_tx_manager_approval_should_obey_gas_price():\n    # given\n    global web3, our_address, second_address, token\n    tx = TxManager.deploy(web3)\n\n    # when\n    via_tx_manager(tx, gas_price=FixedGasPrice(15000000000))(token, second_address, \"some-name\")\n\n    # then\n    assert web3.eth.getBlock('latest', full_transactions=True).transactions[0].gasPrice == 15000000000\n\n\ndef test_via_tx_manager_approval_should_not_approve_if_already_approved():\n    # given\n    global web3, our_address, second_address, token\n    tx = TxManager.deploy(web3)\n    tx.execute([], [token.approve(second_address, Wad(2**248+19)).invocation()]).transact()\n\n    # when\n    via_tx_manager(tx)(token, second_address, \"some-name\")\n\n    # then\n    assert token.allowance_of(tx.address, second_address) == Wad(2**248+19)\n\n\ndef test_via_tx_manager_approval_should_raise_exception_if_approval_fails():\n    # given\n    global web3, our_address, second_address, token\n    tx = TxManager.deploy(web3)\n    tx.execute = MagicMock(return_value=FailingTransact())\n\n    # when\n    with pytest.raises(Exception):\n        via_tx_manager(tx)(token, second_address, \"some-name\")\n"
  },
  {
    "path": "tests/test_auctions.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2018-2019 reverendus, bargst, EdNoepel\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n\nimport pytest\nfrom datetime import datetime\nfrom web3 import Web3\n\nfrom pymaker import Address\nfrom pymaker.approval import directly, hope_directly\nfrom pymaker.auctions import DealableAuctionContract, Clipper, Flapper, Flipper, Flopper\nfrom pymaker.deployment import Collateral, DssDeployment\nfrom pymaker.numeric import Wad, Ray, Rad\nfrom tests.helpers import time_travel_by\nfrom tests.test_dss import wrap_eth, mint_mkr, set_collateral_price, frob, cleanup_urn, max_dart\n\n\ndef create_surplus(mcd: DssDeployment, flapper: Flapper, deployment_address: Address):\n    assert isinstance(mcd, DssDeployment)\n    assert isinstance(flapper, Flapper)\n    assert isinstance(deployment_address, Address)\n\n    joy = mcd.vat.dai(mcd.vow.address)\n    if joy < mcd.vat.sin(mcd.vow.address) + mcd.vow.hump() + mcd.vow.bump():\n        print('Creating a vault with surplus')\n        collateral = mcd.collaterals['ETH-C']\n        ink = Wad.from_number(200)\n        wrap_eth(mcd, deployment_address, ink)\n        collateral.approve(deployment_address)\n        assert collateral.adapter.join(deployment_address, ink).transact(\n            from_address=deployment_address)\n        # CAUTION: dart needs to be adjusted over time to keep tests happy\n        frob(mcd, collateral, deployment_address, dink=ink, dart=Wad.from_number(3000))\n        assert mcd.jug.drip(collateral.ilk).transact(from_address=deployment_address)\n        joy = mcd.vat.dai(mcd.vow.address)\n        # total surplus > total debt + surplus auction lot size + surplus buffer\n        assert float(joy) > float(mcd.vat.sin(mcd.vow.address)) + float(mcd.vow.bump()) + float(mcd.vow.hump())\n    else:\n        print(f'Surplus of {joy} already exists; skipping CDP creation')\n\n\ndef create_debt(web3: Web3, mcd: DssDeployment, our_address: Address, deployment_address: Address):\n    assert isinstance(web3, Web3)\n    assert isinstance(mcd, DssDeployment)\n    assert isinstance(our_address, Address)\n    assert isinstance(deployment_address, Address)\n\n    # Create a vault\n    collateral = mcd.collaterals['ETH-A']\n    ilk = collateral.ilk\n    ink = Wad.from_number(10)\n    wrap_eth(mcd, deployment_address, ink)\n    collateral.approve(deployment_address)\n    assert collateral.adapter.join(deployment_address, ink).transact(from_address=deployment_address)\n    frob(mcd, collateral, deployment_address, dink=ink, dart=Wad(0))\n    dart = max_dart(mcd, collateral, deployment_address) - Wad(1)\n    frob(mcd, collateral, deployment_address, dink=Wad(0), dart=dart)\n    assert not mcd.cat.can_bite(ilk, mcd.vat.urn(collateral.ilk, deployment_address))\n\n    # Undercollateralize by dropping the spot price, and then bite the vault\n    to_price = Wad(Web3.toInt(collateral.pip.read())) / Wad.from_number(2)\n    set_collateral_price(mcd, collateral, to_price)\n    urn = mcd.vat.urn(collateral.ilk, deployment_address)\n    assert urn.ink is not None and urn.art is not None\n    assert ilk.spot is not None\n    assert mcd.cat.can_bite(collateral.ilk, urn)\n    assert mcd.cat.bite(collateral.ilk, urn).transact()\n    flip_kick = collateral.flipper.kicks()\n\n    # Generate some Dai, submit a zero bid to win the flip auction without covering all the debt\n    wrap_eth(mcd, our_address, Wad.from_number(10))\n    collateral.approve(our_address)\n    assert collateral.adapter.join(our_address, Wad.from_number(10)).transact(from_address=our_address)\n    web3.eth.defaultAccount = our_address.address\n    frob(mcd, collateral, our_address, dink=Wad.from_number(10), dart=Wad.from_number(20))\n    collateral.flipper.approve(mcd.vat.address, approval_function=hope_directly())\n    current_bid = collateral.flipper.bids(flip_kick)\n    TestFlipper.tend(collateral.flipper, flip_kick, our_address, current_bid.lot, Rad(1))\n    mcd.vat.can(our_address, collateral.flipper.address)\n    time_travel_by(web3, collateral.flipper.ttl() + 1)\n    assert collateral.flipper.deal(flip_kick).transact()\n\n    # Raise debt from the queue (note that vow.wait is 0 on our testchain)\n    assert mcd.vow.wait() == 0\n    bites = mcd.cat.past_bites(100)\n    assert len(bites) > 0\n    for bite in bites:\n        era_bite = bite.era(web3)\n        assert era_bite > int(datetime.now().timestamp()) - 120\n        assert mcd.vow.sin_of(era_bite) > Rad(0)\n        assert mcd.vow.flog(era_bite).transact()\n        assert mcd.vow.sin_of(era_bite) == Rad(0)\n    barks = mcd.dog.past_barks(100)\n    assert len(barks) > 0\n    for bark in barks:\n        era_bark = bark.era(web3)\n        assert era_bark > int(datetime.now().timestamp()) - 120\n        assert mcd.vow.sin_of(era_bark) > Rad(0)\n        assert mcd.vow.flog(era_bark).transact()\n        assert mcd.vow.sin_of(era_bark) == Rad(0)\n    # Cancel out surplus and debt\n    dai_vow = mcd.vat.dai(mcd.vow.address)\n    # to do fix heal\n    #assert dai_vow <= mcd.vow.woe()\n    #assert mcd.vow.heal(dai_vow).transact()\n    #assert mcd.vow.woe() >= mcd.vow.sump()\n\n\ndef check_active_auctions(auction: DealableAuctionContract):\n    for bid in auction.active_auctions():\n        assert bid.id > 0\n        assert auction.kicks() >= bid.id\n        assert isinstance(bid.guy, Address)\n        assert bid.guy != Address(\"0x0000000000000000000000000000000000000000\")\n\n\nclass TestFlapper:\n    @pytest.fixture(scope=\"session\")\n    def flapper(self, mcd: DssDeployment) -> Flapper:\n        return mcd.flapper\n\n    @staticmethod\n    def tend(flapper: Flapper, id: int, address: Address, lot: Rad, bid: Wad):\n        assert (isinstance(flapper, Flapper))\n        assert (isinstance(id, int))\n        assert (isinstance(lot, Rad))\n        assert (isinstance(bid, Wad))\n\n        assert flapper.live() == 1\n\n        current_bid = flapper.bids(id)\n        assert current_bid.guy != Address(\"0x0000000000000000000000000000000000000000\")\n        assert current_bid.tic > datetime.now().timestamp() or current_bid.tic == 0\n        assert current_bid.end > datetime.now().timestamp()\n\n        assert lot == current_bid.lot\n        assert bid > current_bid.bid\n        assert bid >= flapper.beg() * current_bid.bid\n\n        assert flapper.tend(id, lot, bid).transact(from_address=address)\n        log = TestFlapper.last_log(flapper)\n        assert isinstance(log, Flapper.TendLog)\n        assert log.guy == address\n        assert log.id == id\n        assert log.lot == lot\n        assert log.bid == bid\n\n    @staticmethod\n    def last_log(flapper: Flapper):\n        current_block = flapper.web3.eth.blockNumber\n        return flapper.past_logs(current_block-1, current_block)[0]\n\n    def test_getters(self, mcd, flapper):\n        assert flapper.vat() == mcd.vat.address\n        assert flapper.beg() > Wad.from_number(1)\n        assert flapper.ttl() > 0\n        assert flapper.tau() > flapper.ttl()\n        assert flapper.kicks() >= 0\n\n    def test_scenario(self, web3, mcd, flapper, our_address, other_address, deployment_address):\n        create_surplus(mcd, flapper, deployment_address)\n\n        joy_before = mcd.vat.dai(mcd.vow.address)\n        # total surplus > total debt + surplus auction lot size + surplus buffer\n        assert joy_before > mcd.vat.sin(mcd.vow.address) + mcd.vow.bump() + mcd.vow.hump()\n        assert (mcd.vat.sin(mcd.vow.address) - mcd.vow.sin()) - mcd.vow.ash() == Rad(0)\n        assert mcd.vow.flap().transact()\n        kick = flapper.kicks()\n        assert kick == 1\n        assert len(flapper.active_auctions()) == 1\n        check_active_auctions(flapper)\n        current_bid = flapper.bids(1)\n        assert current_bid.lot > Rad(0)\n        log = self.last_log(flapper)\n        assert isinstance(log, Flapper.KickLog)\n        assert log.id == kick\n        assert log.lot == current_bid.lot\n        assert log.bid == current_bid.bid\n\n        # Allow the auction to expire, and then resurrect it\n        time_travel_by(web3, flapper.tau()+1)\n        assert flapper.tick(kick).transact()\n\n        # Bid on the resurrected auction\n        mint_mkr(mcd.mkr, our_address, Wad.from_number(10))\n        flapper.approve(mcd.mkr.address, directly(from_address=our_address))\n        bid = Wad.from_number(0.001)\n        assert mcd.mkr.balance_of(our_address) > bid\n        TestFlapper.tend(flapper, kick, our_address, current_bid.lot, bid)\n        current_bid = flapper.bids(kick)\n        assert current_bid.bid == bid\n        assert current_bid.guy == our_address\n\n        # Exercise _deal_ after bid has expired\n        time_travel_by(web3, flapper.ttl()+1)\n        now = datetime.now().timestamp()\n        assert 0 < current_bid.tic < now or current_bid.end < now\n        assert flapper.deal(kick).transact(from_address=our_address)\n        joy_after = mcd.vat.dai(mcd.vow.address)\n        print(f'joy_before={str(joy_before)}, joy_after={str(joy_after)}')\n        assert joy_before - joy_after == mcd.vow.bump()\n        log = self.last_log(flapper)\n        assert isinstance(log, Flapper.DealLog)\n        assert log.usr == our_address\n        assert log.id == kick\n\n        # Grab our dai\n        mcd.approve_dai(our_address)\n        assert mcd.dai_adapter.exit(our_address, Wad(current_bid.lot)).transact(from_address=our_address)\n        assert mcd.dai.balance_of(our_address) >= Wad(current_bid.lot)\n        assert (mcd.vat.sin(mcd.vow.address) - mcd.vow.sin()) - mcd.vow.ash() == Rad(0)\n\n\nclass TestFlipper:\n    @pytest.fixture(scope=\"session\")\n    def collateral(self, mcd: DssDeployment) -> Collateral:\n        return mcd.collaterals['ETH-A']\n\n    @pytest.fixture(scope=\"session\")\n    def flipper(self, collateral, deployment_address) -> Flipper:\n        return collateral.flipper\n\n    @staticmethod\n    def tend(flipper: Flipper, id: int, address: Address, lot: Wad, bid: Rad):\n        assert (isinstance(flipper, Flipper))\n        assert (isinstance(id, int))\n        assert (isinstance(lot, Wad))\n        assert (isinstance(bid, Rad))\n\n        current_bid = flipper.bids(id)\n        assert current_bid.guy != Address(\"0x0000000000000000000000000000000000000000\")\n        assert current_bid.tic > datetime.now().timestamp() or current_bid.tic == 0\n        assert current_bid.end > datetime.now().timestamp()\n\n        assert lot == current_bid.lot\n        assert bid <= current_bid.tab\n        assert bid > current_bid.bid\n        assert (bid >= Rad(flipper.beg()) * current_bid.bid) or (bid == current_bid.tab)\n\n        assert flipper.tend(id, lot, bid).transact(from_address=address)\n\n    @staticmethod\n    def dent(flipper: Flipper, id: int, address: Address, lot: Wad, bid: Rad):\n        assert (isinstance(flipper, Flipper))\n        assert (isinstance(id, int))\n        assert (isinstance(lot, Wad))\n        assert (isinstance(bid, Rad))\n\n        current_bid = flipper.bids(id)\n        assert current_bid.guy != Address(\"0x0000000000000000000000000000000000000000\")\n        assert current_bid.tic > datetime.now().timestamp() or current_bid.tic == 0\n        assert current_bid.end > datetime.now().timestamp()\n\n        assert bid == current_bid.bid\n        assert bid == current_bid.tab\n        assert lot < current_bid.lot\n        assert flipper.beg() * lot <= current_bid.lot\n\n        assert flipper.dent(id, lot, bid).transact(from_address=address)\n\n    @staticmethod\n    def last_log(flipper: Flipper):\n        current_block = flipper.web3.eth.blockNumber\n        return flipper.past_logs(current_block-1, current_block)[0]\n\n    def test_getters(self, mcd, flipper):\n        assert flipper.vat() == mcd.vat.address\n        assert flipper.beg() > Wad.from_number(1)\n        assert flipper.ttl() > 0\n        assert flipper.tau() > flipper.ttl()\n        assert flipper.kicks() >= 0\n\n    def test_scenario(self, web3, mcd, collateral, flipper, our_address, other_address, deployment_address):\n        # Create a vault\n        kicks_before = flipper.kicks()\n        ilk = collateral.ilk\n        ink = Wad.from_number(0.5)\n        wrap_eth(mcd, deployment_address, ink)\n        collateral.approve(deployment_address)\n        assert collateral.adapter.join(deployment_address, ink).transact(\n            from_address=deployment_address)\n        frob(mcd, collateral, deployment_address, dink=ink, dart=Wad(0))\n        dart = max_dart(mcd, collateral, deployment_address) - Wad(1)\n        frob(mcd, collateral, deployment_address, dink=Wad(0), dart=dart)\n\n        # Mint and withdraw all the Dai\n        mcd.approve_dai(deployment_address)\n        assert mcd.dai_adapter.exit(deployment_address, dart).transact(from_address=deployment_address)\n        assert mcd.dai.balance_of(deployment_address) == dart\n\n        # Undercollateralize the vault\n        to_price = Wad(Web3.toInt(collateral.pip.read())) / Wad.from_number(2)\n        set_collateral_price(mcd, collateral, to_price)\n        urn = mcd.vat.urn(collateral.ilk, deployment_address)\n        ilk = mcd.vat.ilk(ilk.name)\n        assert ilk.rate is not None\n        assert ilk.spot is not None\n        safe = Ray(urn.art) * mcd.vat.ilk(ilk.name).rate <= Ray(urn.ink) * ilk.spot\n        assert not safe\n        assert len(flipper.active_auctions()) == 0\n        litter_before = mcd.cat.litter()\n\n        # Bite the vault, which moves debt to the vow and kicks the flipper\n        urn = mcd.vat.urn(collateral.ilk, deployment_address)\n        assert urn.ink > Wad(0)\n        art = min(urn.art, Wad(mcd.cat.dunk(ilk)))  # Wad\n        tab = art * ilk.rate  # Wad\n        assert tab == dart\n        assert mcd.cat.can_bite(ilk, urn)\n        assert mcd.cat.bite(ilk, urn).transact()\n        kick = flipper.kicks()\n        assert kick == kicks_before + 1\n        urn = mcd.vat.urn(collateral.ilk, deployment_address)\n        # Check vat, vow, and cat\n        assert urn.ink == Wad(0)\n        assert urn.art == dart - art\n        assert mcd.vat.vice() > Rad(0)\n        assert mcd.vow.sin() == Rad(tab)\n        bites = mcd.cat.past_bites(1)\n        assert len(bites) == 1\n        last_bite = bites[0]\n        assert last_bite.tab > Rad(0)\n        assert last_bite.id == 1\n        litter_after = mcd.cat.litter()\n        assert litter_before < litter_after\n        # Check the flipper\n        current_bid = flipper.bids(kick)\n        assert isinstance(current_bid, Flipper.Bid)\n        assert current_bid.lot > Wad(0)\n        assert current_bid.tab > Rad(0)\n        assert current_bid.bid == Rad(0)\n        # Cat doesn't incorporate the liquidation penalty (chop), but the kicker includes it.\n        # Awaiting word from @dc why this is so.\n        #assert last_bite.tab == current_bid.tab\n        log = self.last_log(flipper)\n        assert isinstance(log, Flipper.KickLog)\n        assert log.id == kick\n        assert log.lot == current_bid.lot\n        assert log.bid == current_bid.bid\n        assert log.tab == current_bid.tab\n        assert log.usr == deployment_address\n        assert log.gal == mcd.vow.address\n\n        # Allow the auction to expire, and then resurrect it\n        time_travel_by(web3, flipper.tau()+1)\n        assert flipper.tick(kick).transact()\n\n        # Wrap some eth and handle approvals before bidding\n        eth_required = Wad(current_bid.tab / Rad(ilk.spot)) * Wad.from_number(1.13)\n        wrap_eth(mcd, other_address, eth_required)\n        collateral.approve(other_address)\n        assert collateral.adapter.join(other_address, eth_required).transact(from_address=other_address)\n        wrap_eth(mcd, our_address, eth_required)\n        collateral.approve(our_address)\n        assert collateral.adapter.join(our_address, eth_required).transact(from_address=our_address)\n\n        # Test the _tend_ phase of the auction\n        flipper.approve(mcd.vat.address, approval_function=hope_directly(from_address=other_address))\n        # Add Wad(1) to counter precision error converting tab from Rad to Wad\n        frob(mcd, collateral, other_address, dink=eth_required, dart=Wad(current_bid.tab) + Wad(1))\n        urn = mcd.vat.urn(collateral.ilk, other_address)\n        assert Rad(urn.art) >= current_bid.tab\n        # Bid the tab to instantly transition to dent stage\n        TestFlipper.tend(flipper, kick, other_address, current_bid.lot, current_bid.tab)\n        current_bid = flipper.bids(kick)\n        assert current_bid.guy == other_address\n        assert current_bid.bid == current_bid.tab\n        assert len(flipper.active_auctions()) == 1\n        check_active_auctions(flipper)\n        log = self.last_log(flipper)\n        assert isinstance(log, Flipper.TendLog)\n        assert log.guy == current_bid.guy\n        assert log.id == current_bid.id\n        assert log.lot == current_bid.lot\n        assert log.bid == current_bid.bid\n\n        # Test the _dent_ phase of the auction\n        flipper.approve(mcd.vat.address, approval_function=hope_directly(from_address=our_address))\n        frob(mcd, collateral, our_address, dink=eth_required, dart=Wad(current_bid.tab) + Wad(1))\n        lot = current_bid.lot - Wad.from_number(0.3)\n        assert flipper.beg() * lot <= current_bid.lot\n        TestFlipper.dent(flipper, kick, our_address, lot, current_bid.tab)\n        current_bid = flipper.bids(kick)\n        assert current_bid.guy == our_address\n        assert current_bid.bid == current_bid.tab\n        assert current_bid.lot == lot\n        log = self.last_log(flipper)\n        assert isinstance(log, Flipper.DentLog)\n        assert log.guy == current_bid.guy\n        assert log.id == current_bid.id\n        assert log.lot == current_bid.lot\n        assert log.bid == current_bid.bid\n\n        # Exercise _deal_ after bid has expired\n        time_travel_by(web3, flipper.ttl()+1)\n        now = datetime.now().timestamp()\n        assert 0 < current_bid.tic < now or current_bid.end < now\n        assert flipper.deal(kick).transact(from_address=our_address)\n        assert len(flipper.active_auctions()) == 0\n        log = self.last_log(flipper)\n        assert isinstance(log, Flipper.DealLog)\n        assert log.usr == our_address\n\n        # Grab our collateral\n        collateral_before = collateral.gem.balance_of(our_address)\n        assert collateral.adapter.exit(our_address, current_bid.lot).transact(from_address=our_address)\n        collateral_after = collateral.gem.balance_of(our_address)\n        assert collateral_before < collateral_after\n\n        # Cleanup\n        set_collateral_price(mcd, collateral, Wad.from_number(230))\n        cleanup_urn(mcd, collateral, other_address)\n\n\nclass TestClipper:\n    @pytest.fixture(scope=\"session\")\n    def collateral(self, mcd: DssDeployment) -> Collateral:\n        return mcd.collaterals['ETH-B']\n\n    @pytest.fixture(scope=\"session\")\n    def clipper(self, collateral, deployment_address) -> Clipper:\n        return collateral.clipper\n\n    @staticmethod\n    def last_log(clipper: Clipper):\n        current_block = clipper.web3.eth.blockNumber\n        return clipper.past_logs(current_block-1, current_block)[0]\n\n    def test_getters(self, mcd, clipper):\n        assert clipper.ilk_name() == \"ETH-B\"\n        collateral = mcd.collaterals[clipper.ilk_name()]\n        assert clipper.kicks() == 0\n        assert clipper.buf() == Ray.from_number(1.50)\n        assert clipper.tail() == 10800\n        assert clipper.cusp() == Ray.from_number(0.3333)\n        assert clipper.chip() == Wad.from_number(0.02)\n        assert clipper.tip() == Rad.from_number(100)\n        assert clipper.chost() == collateral.ilk.dust * Rad(mcd.dog.chop(collateral.ilk))\n        assert isinstance(clipper.calc, Address)\n        assert clipper.calc != Address.zero()\n        assert clipper.dog.address == mcd.dog.address\n        assert clipper.vat.address == mcd.vat.address\n        assert clipper.active_auctions() == []\n\n    def test_scenario(self, web3, mcd, collateral, clipper, our_address, other_address, deployment_address):\n        dirt_before = mcd.dog.dog_dirt()\n        vice_before = mcd.vat.vice()\n        sin_before = mcd.vow.sin()\n\n        # Create a vault\n        ilk = collateral.ilk\n        ink = Wad.from_number(1)\n        wrap_eth(mcd, deployment_address, ink)\n        collateral.approve(deployment_address)\n        assert collateral.adapter.join(deployment_address, ink).transact(\n            from_address=deployment_address)\n        frob(mcd, collateral, deployment_address, dink=ink, dart=Wad(0))\n        dart = max_dart(mcd, collateral, deployment_address) - Wad(1)\n        frob(mcd, collateral, deployment_address, dink=Wad(0), dart=dart)\n\n        # Mint and withdraw all the Dai\n        mcd.approve_dai(deployment_address)\n        assert mcd.dai_adapter.exit(deployment_address, dart).transact(from_address=deployment_address)\n\n        # Undercollateralize the vault\n        to_price = Wad(Web3.toInt(collateral.pip.read())) / Wad.from_number(2)\n        set_collateral_price(mcd, collateral, to_price)\n        urn = mcd.vat.urn(collateral.ilk, deployment_address)\n        ilk = mcd.vat.ilk(ilk.name)\n        safe = Ray(urn.art) * mcd.vat.ilk(ilk.name).rate <= Ray(urn.ink) * ilk.spot\n        assert not safe\n        assert clipper.active_count() == 0\n\n        # Bark the vault, which moves debt to the vow and kicks the clipper\n        dai_before_bark: Rad = mcd.vat.dai(our_address)\n        urn = mcd.vat.urn(collateral.ilk, deployment_address)\n        assert urn.ink > Wad(0)\n        tab = urn.art * ilk.rate  # Wad\n        assert tab == dart\n        assert mcd.dog.bark(ilk, urn).transact()\n        barks = mcd.dog.past_barks(1)\n        assert len(barks) == 1\n        last_bite = barks[0]\n        assert last_bite.due > Rad(0)\n        assert clipper.active_count() == 1\n        kick = clipper.kicks()\n        assert kick == 1\n        assert len(clipper.active_auctions()) == 1\n        assert clipper.active_auctions()[0].id == 1\n        assert mcd.active_auctions()['clips']['ETH-B'][0].id == 1\n        urn = mcd.vat.urn(collateral.ilk, deployment_address)\n        (needs_redo, price, lot, tab) = clipper.status(kick)\n        assert not needs_redo\n        assert price == Ray.from_number(172.5)\n        assert lot == ink\n        assert float(tab) == 105.0\n        # Check vat, vow, and dog\n        assert urn.ink == Wad(0)\n        assert vice_before < mcd.vat.vice()\n        assert sin_before < mcd.vow.sin()\n        assert dirt_before < mcd.dog.dog_dirt()\n        # Check the clipper\n        current_sale = clipper.sales(kick)\n        assert isinstance(current_sale, Clipper.Sale)\n        assert current_sale.pos == 0\n        assert float(current_sale.tab) == 105.0\n        assert current_sale.lot == ink\n        assert current_sale.usr == deployment_address\n        assert current_sale.tic > 0\n        assert round(current_sale.top, 1) == price\n        coin = Rad(clipper.tip() + (current_sale.tab * clipper.chip()))\n        # Confirm we received our liquidation reward\n        dai_after_bark: Rad = mcd.vat.dai(our_address)\n        assert dai_after_bark == dai_before_bark + coin\n        kick_log = self.last_log(clipper)\n        assert isinstance(kick_log, Clipper.KickLog)\n        assert kick_log.id == kick\n        assert kick_log.top == current_sale.top\n        assert kick_log.tab == current_sale.tab\n        assert kick_log.lot == current_sale.lot\n        assert kick_log.usr == deployment_address\n        assert kick_log.kpr == our_address\n        assert kick_log.coin == coin\n\n        # Wrap some eth and handle approvals before bidding\n        eth_required = Wad(current_sale.tab / Rad(ilk.spot)) * Wad.from_number(1.1)\n        wrap_eth(mcd, our_address, eth_required)\n        collateral.approve(our_address)\n        assert collateral.adapter.join(our_address, eth_required).transact(from_address=our_address)\n        clipper.approve(mcd.vat.address, approval_function=hope_directly(from_address=our_address))\n\n        # Ensure we cannot take collateral below the current price\n        (needs_redo, price, lot, tab) = clipper.status(kick)\n        with pytest.raises(AssertionError):\n            clipper.validate_take(kick, ink, price - Ray.from_number(1))\n        assert not clipper.take(kick, ink, Ray.from_number(140)).transact(from_address=our_address)\n\n        # Take some collateral with max above the top price\n        clipper.validate_take(kick, Wad.from_number(0.07), Ray.from_number(180))\n        assert web3.eth.defaultAccount == our_address.address\n        assert clipper.take(kick, Wad.from_number(0.07), Ray.from_number(180)).transact(from_address=our_address)\n        (needs_redo, price, lot, tab) = clipper.status(kick)\n        assert not needs_redo\n        current_sale = clipper.sales(kick)\n        assert current_sale.lot > Wad(0)\n        assert current_sale.top > price\n        assert Rad(0) < current_sale.tab < kick_log.tab\n        first_take_log = self.last_log(clipper)\n        assert first_take_log.id == 1\n        assert first_take_log.max == Ray.from_number(180)\n        assert first_take_log.price == price\n        assert first_take_log.lot == current_sale.lot\n        assert first_take_log.usr == deployment_address\n        assert first_take_log.sender == our_address\n        assert round(first_take_log.owe, 18) == round(Rad.from_number(0.07) * Rad(price), 18)\n\n        # Allow the auction to expire, and then resurrect it\n        # TODO: If abaci contract is ever wrapped, read tau from it\n        time_travel_by(web3, 24)\n        (needs_redo, price, lot, tab) = clipper.status(kick)\n        assert needs_redo\n        assert len(clipper.active_auctions()) == clipper.active_count()\n        assert clipper.redo(kick, our_address).transact()\n        (needs_redo, price, lot, tab) = clipper.status(kick)\n        assert not needs_redo\n        current_sale = clipper.sales(kick)\n        assert current_sale.lot > Wad(0)\n        redo_log = self.last_log(clipper)\n        assert isinstance(redo_log, Clipper.RedoLog)\n        assert redo_log.id == kick\n        assert redo_log.top == current_sale.top\n        assert redo_log.tab == current_sale.tab\n        assert redo_log.lot == current_sale.lot\n        assert redo_log.usr == deployment_address\n        assert redo_log.kpr == our_address\n        coin = Rad(clipper.tip() + (current_sale.tab * clipper.chip()))\n        assert round(float(redo_log.coin), 18) == round(float(coin), 18)\n\n        # Sleep until price has gone down enough to bid with remaining Dai\n        dai = mcd.vat.dai(our_address)\n        last_price = price\n        print(f\"Bid cost={float(price * Ray(current_sale.lot))}, Dai balance={float(dai)}\")\n        while price * Ray(current_sale.lot) > Ray(dai):\n            print(f\"Bid cost {price * Ray(current_sale.lot)} exceeds Dai balance {dai}\")\n            time_travel_by(web3, 2)\n            (needs_redo, price, lot, tab) = clipper.status(kick)\n            assert price < last_price\n            assert not needs_redo\n            last_price = price\n        clipper.validate_take(kick, current_sale.lot, price)\n        assert clipper.take(kick, current_sale.lot, price).transact(from_address=our_address)\n        current_sale = clipper.sales(kick)\n        assert current_sale.lot == Wad(0)\n        assert current_sale.tab == Rad(0)\n        assert clipper.active_count() == 0\n        assert len(clipper.active_auctions()) == 0\n\n        # Ensure we can retrieve our collateral\n        collateral_before = collateral.gem.balance_of(our_address)\n        assert collateral.adapter.exit(our_address, ink).transact(from_address=our_address)\n        collateral_after = collateral.gem.balance_of(our_address)\n        assert collateral_before < collateral_after\n\n        # Cleanup\n        set_collateral_price(mcd, collateral, Wad.from_number(230))\n        cleanup_urn(mcd, collateral, our_address)\n\n\nclass TestFlopper:\n    @pytest.fixture(scope=\"session\")\n    def flopper(self, mcd: DssDeployment) -> Flopper:\n        return mcd.flopper\n\n    @staticmethod\n    def dent(flopper: Flopper, id: int, address: Address, lot: Wad, bid: Rad):\n        assert (isinstance(flopper, Flopper))\n        assert (isinstance(id, int))\n        assert (isinstance(lot, Wad))\n        assert (isinstance(bid, Rad))\n\n        assert flopper.live() == 1\n\n        current_bid = flopper.bids(id)\n        assert current_bid.guy != Address(\"0x0000000000000000000000000000000000000000\")\n        assert current_bid.tic > datetime.now().timestamp() or current_bid.tic == 0\n        assert current_bid.end > datetime.now().timestamp()\n\n        assert bid == current_bid.bid\n        assert Wad(0) < lot < current_bid.lot\n        assert flopper.beg() * lot <= current_bid.lot\n\n        assert flopper.dent(id, lot, bid).transact(from_address=address)\n        log = TestFlopper.last_log(flopper)\n        assert isinstance(log, Flopper.DentLog)\n        assert log.guy == address\n        assert log.id == id\n        assert log.lot == lot\n        assert log.bid == bid\n\n    @staticmethod\n    def last_log(flopper: Flopper):\n        current_block = flopper.web3.eth.blockNumber\n        return flopper.past_logs(current_block-1, current_block)[0]\n\n    def test_getters(self, mcd, flopper):\n        assert flopper.vat() == mcd.vat.address\n        assert flopper.beg() > Wad.from_number(1)\n        assert flopper.ttl() > 0\n        assert flopper.tau() > flopper.ttl()\n        assert flopper.kicks() >= 0\n\n    @pytest.mark.skip(reason=\"fix flop tests\")\n    def test_scenario(self, web3, mcd, flopper, our_address, other_address, deployment_address):\n        create_debt(web3, mcd, our_address, deployment_address)\n\n        # Kick off the flop auction\n        assert flopper.kicks() == 0\n        assert len(flopper.active_auctions()) == 0\n        assert mcd.vat.dai(mcd.vow.address) == Rad(0)\n        assert mcd.vow.flop().transact()\n        kick = flopper.kicks()\n        assert kick == 1\n        assert len(flopper.active_auctions()) == 1\n        check_active_auctions(flopper)\n        current_bid = flopper.bids(kick)\n        log = self.last_log(flopper)\n        assert isinstance(log, Flopper.KickLog)\n        assert log.id == kick\n        assert log.lot == current_bid.lot\n        assert log.bid == current_bid.bid\n        assert log.gal == mcd.vow.address\n\n        # Allow the auction to expire, and then resurrect it\n        time_travel_by(web3, flopper.tau()+1)\n        assert flopper.tick(kick).transact()\n        assert flopper.bids(kick).lot == current_bid.lot * flopper.pad()\n\n        # Bid on the resurrected auction\n        bid = Wad.from_number(0.000005)\n        flopper.approve(mcd.vat.address, hope_directly())\n        assert mcd.vat.can(our_address, flopper.address)\n        TestFlopper.dent(flopper, kick, our_address, bid, current_bid.bid)\n        current_bid = flopper.bids(kick)\n        assert current_bid.guy == our_address\n\n        # Confirm victory\n        time_travel_by(web3, flopper.ttl()+1)\n        assert flopper.live()\n        now = int(datetime.now().timestamp())\n        assert (current_bid.tic < now and current_bid.tic != 0) or current_bid.end < now\n        mkr_before = mcd.mkr.balance_of(our_address)\n        assert flopper.deal(kick).transact(from_address=our_address)\n        mkr_after = mcd.mkr.balance_of(our_address)\n        assert mkr_after > mkr_before\n        log = self.last_log(flopper)\n        assert isinstance(log, Flopper.DealLog)\n        assert log.usr == our_address\n        assert log.id == kick\n\n        # Cleanup\n        collateral = mcd.collaterals['ETH-A']\n        set_collateral_price(mcd, collateral, Wad.from_number(230))\n"
  },
  {
    "path": "tests/test_auth.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2017-2018 reverendus\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nimport pytest\nfrom web3 import Web3, HTTPProvider\n\nfrom pymaker import Address\nfrom pymaker.auth import DSGuard, DSAuth\nfrom pymaker.util import hexstring_to_bytes\n\n\nclass TestDSGuard:\n    def setup_method(self):\n        self.web3 = Web3(HTTPProvider(\"http://localhost:8555\"))\n        self.web3.eth.defaultAccount = self.web3.eth.accounts[0]\n        self.our_address = Address(self.web3.eth.defaultAccount)\n        self.ds_guard = DSGuard.deploy(self.web3)\n\n    def can_call(self, src: str, dst: str, sig: str) -> bool:\n        return self.ds_guard._contract.functions.canCall(src, dst, hexstring_to_bytes(sig)).call()\n\n    def test_fail_when_no_contract_under_that_address(self):\n        # expect\n        with pytest.raises(Exception):\n            DSGuard(web3=self.web3, address=Address('0xdeadadd1e5500000000000000000000000000000'))\n\n    def test_no_permit_by_default(self):\n        # expect\n        assert not self.can_call(src='0x1111111111222222222211111111112222222222',\n                                 dst='0x3333333333444444444433333333334444444444',\n                                 sig='0xab121fd7')\n\n    def test_permit_any_to_any_with_any_sig(self):\n        # when\n        self.ds_guard.permit(DSGuard.ANY, DSGuard.ANY, DSGuard.ANY).transact()\n\n        # then\n        assert self.can_call(src='0x1111111111222222222211111111112222222222',\n                             dst='0x3333333333444444444433333333334444444444',\n                             sig='0xab121fd7')\n\n    def test_permit_specific_addresses_and_sig(self):\n        # when\n        self.ds_guard.permit(src=Address('0x1111111111222222222211111111112222222222'),\n                             dst=Address('0x3333333333444444444433333333334444444444'),\n                             sig=hexstring_to_bytes('0xab121fd7')).transact()\n\n        # then\n        assert self.can_call(src='0x1111111111222222222211111111112222222222',\n                             dst='0x3333333333444444444433333333334444444444',\n                             sig='0xab121fd7')\n\n        # and\n        assert not self.can_call(src='0x3333333333444444444433333333334444444444',\n                                 dst='0x1111111111222222222211111111112222222222',\n                                 sig='0xab121fd7')  # different addresses\n        assert not self.can_call(src='0x1111111111222222222211111111112222222222',\n                                 dst='0x3333333333444444444433333333334444444444',\n                                 sig='0xab121fd8')  # different sig\n\n\nclass TestDSAuth:\n    def setup_method(self):\n        self.web3 = Web3(HTTPProvider(\"http://localhost:8555\"))\n        self.web3.eth.defaultAccount = self.web3.eth.accounts[0]\n        self.our_address = Address(self.web3.eth.defaultAccount)\n\n        self.ds_auth = DSAuth.deploy(self.web3)\n\n    @pytest.mark.skip(reason=\"calls to ABI/BIN are not working on ganache\")\n    def test_owner(self):\n        owner = self.ds_auth.get_owner()\n        assert isinstance(owner, Address)\n        assert owner == self.web3.eth.accounts[0]\n\n        assert self.ds_auth.set_owner(self.web3.eth.accounts[1])\n        assert self.ds_auth.get_owner() == self.web3.eth.accounts[1]\n"
  },
  {
    "path": "tests/test_cdpmanager.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2020 EdNoepel\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nfrom pymaker import Address\nfrom pymaker.deployment import DssDeployment\nfrom pymaker.cdpmanager import Urn\n\n\nclass TestCdpManager:\n\n    def test_none(self, our_address: Address, mcd: DssDeployment):\n        assert mcd.cdp_manager.first(our_address) == 0\n        assert mcd.cdp_manager.last(our_address) == 0\n        assert mcd.cdp_manager.count(our_address) == 0\n\n    def test_open(self, our_address: Address, mcd: DssDeployment):\n        ilk = mcd.collaterals['ETH-A'].ilk\n        assert mcd.cdp_manager.open(ilk, our_address).transact()\n        assert mcd.cdp_manager.last(our_address) == 1\n        assert mcd.cdp_manager.ilk(1).name == ilk.name\n        assert mcd.cdp_manager.owns(1) == our_address\n        assert isinstance(mcd.cdp_manager.urn(1), Urn)\n\n    def test_one(self, our_address: Address, mcd: DssDeployment):\n        assert mcd.cdp_manager.first(our_address) == 1\n        assert mcd.cdp_manager.last(our_address) == 1\n        assert mcd.cdp_manager.count(our_address) == 1\n"
  },
  {
    "path": "tests/test_dsrmanager.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2020 Maker Ecosystem Growth Holdings, INC\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nimport pytest\nfrom pymaker import Address\nfrom tests.helpers import time_travel_by\nfrom pymaker.numeric import Wad, Rad, Ray\nfrom pymaker.deployment import DaiJoin, DssDeployment\nfrom pymaker.dss import Pot\nfrom pymaker.token import DSToken\nfrom tests.test_dss import wrap_eth, frob\n\n\ndef mint_dai(mcd: DssDeployment, amount: Wad, ilkName: str, our_address: Address):\n    startingAmount = mcd.dai.balance_of(our_address)\n    dai = amount\n    # Add collateral to our CDP and draw internal Dai\n    collateral=mcd.collaterals[ilkName]\n    ilk = mcd.vat.ilk(collateral.ilk.name)\n    dink = Wad.from_number(1)\n    dart = Wad( Rad(dai) / Rad(ilk.rate))\n    wrap_eth(mcd, our_address, dink)\n    assert collateral.gem.balance_of(our_address) >= dink\n    assert collateral.gem.approve(collateral.adapter.address).transact(from_address=our_address)\n    assert collateral.adapter.join(our_address, dink).transact(from_address=our_address)\n    frob(mcd, collateral, our_address, dink=dink, dart=dart)\n\n    # Exit to Dai Token and make some checks\n    assert mcd.vat.hope(mcd.dai_adapter.address).transact(from_address=our_address)\n    assert mcd.dai_adapter.exit(our_address, dai).transact(from_address=our_address)\n    assert mcd.dai.balance_of(our_address) == dai + startingAmount\n\n\npytest.global_dai = Wad(0)\n\n\nclass TestDsrManager:\n\n    def test_getters(self, mcd: DssDeployment):\n        assert isinstance(mcd.dsr_manager.pot(), Pot)\n        assert mcd.dsr_manager.pot().address.address == mcd.pot.address.address\n        assert isinstance(mcd.dsr_manager.dai(), DSToken)\n        assert mcd.dsr_manager.dai().address.address == mcd.dai.address.address\n        assert isinstance(mcd.dsr_manager.dai_adapter(), DaiJoin)\n        assert mcd.dsr_manager.dai_adapter().address.address == mcd.dai_adapter.address.address\n\n    def test_join(self, mcd: DssDeployment, our_address: Address):\n\n        # Mint 58 Dai and lock it in the Pot contract through DsrManager\n        more_dai = Wad.from_number(58)\n        mint_dai(mcd=mcd, amount=more_dai, ilkName='ETH-A', our_address=our_address)\n        dai = mcd.dai.balance_of(our_address)\n        assert mcd.dai.approve(mcd.dsr_manager.address).transact(from_address=our_address)\n        assert mcd.dsr_manager.supply() == Wad(0)\n\n        # Join through DsrManager an ensure Dai Token balance is depleted\n        assert mcd.dsr_manager.join(our_address, dai).transact(from_address=our_address)\n        assert mcd.dai.balance_of(our_address) == Wad(0)\n\n        pytest.global_dai = dai\n\n    def test_supply_pie_dai(self, mcd: DssDeployment, our_address: Address):\n\n        dai = pytest.global_dai\n        chi1 = mcd.pot.chi()\n        # assert chi1 == Ray.from_number(1) Commented out in case there's some initial state on testchain\n        pie = Wad(Rad(dai) / Rad(chi1))\n        assert mcd.dsr_manager.supply() == pie\n        assert mcd.dsr_manager.pie_of(our_address) == pie\n\n        time_travel_by(web3=mcd.web3, seconds=10)\n        assert mcd.pot.drip().transact(from_address=our_address)\n        chi2 = mcd.pot.chi()\n        assert chi1 != chi2\n        dai = Rad(pie) * Rad(chi2)\n        assert mcd.dsr_manager.dai_of(our_address) == dai\n\n    def test_exit(self, mcd: DssDeployment, our_address: Address):\n\n        dai = pytest.global_dai\n        assert mcd.dai.balance_of(our_address) == Wad.from_number(0)\n        # since drip was called in previous test, there should be some amount left\n        assert mcd.dsr_manager.exit(our_address, dai).transact(from_address=our_address)\n\n        assert mcd.dai.balance_of(our_address) == dai\n        assert mcd.dsr_manager.supply() != Wad(0)\n\n        assert mcd.dsr_manager.exitAll(our_address).transact(from_address=our_address)\n        assert mcd.dai.balance_of(our_address) > dai\n        assert mcd.dsr_manager.supply() == Wad(0)\n"
  },
  {
    "path": "tests/test_dss.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2018-2019 bargst, EdNoepel\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nimport json\nimport pytest\nimport time\nfrom datetime import datetime\nfrom web3 import Web3\n\nfrom pymaker import Address\nfrom pymaker.approval import hope_directly\nfrom pymaker.deployment import Collateral, DssDeployment\nfrom pymaker.dss import Ilk, Urn, Vat, Vow\nfrom pymaker.feed import DSValue\nfrom pymaker.join import DaiJoin, GemJoin, GemJoin5\nfrom pymaker.numeric import Wad, Ray, Rad\nfrom pymaker.oracles import OSM\nfrom pymaker.token import DSToken, DSEthToken, ERC20Token\nfrom tests.conftest import validate_contracts_loaded\n\n\n@pytest.fixture\ndef urn(our_address: Address, mcd: DssDeployment):\n    collateral = mcd.collaterals['ETH-A']\n    urn = mcd.vat.urn(collateral.ilk, our_address)\n    assert urn.ilk is not None\n    assert urn.ilk == collateral.ilk\n    return urn\n\n\ndef wrap_eth(mcd: DssDeployment, address: Address, amount: Wad):\n    assert isinstance(mcd, DssDeployment)\n    assert isinstance(address, Address)\n    assert isinstance(amount, Wad)\n    assert amount > Wad(0)\n\n    collateral = mcd.collaterals['ETH-A']\n    assert isinstance(collateral.gem, DSEthToken)\n    assert collateral.gem.deposit(amount).transact(from_address=address)\n\n\ndef mint_mkr(mkr: DSToken, recipient_address: Address, amount: Wad):\n    assert isinstance(mkr, DSToken)\n    assert isinstance(recipient_address, Address)\n    assert isinstance(amount, Wad)\n    assert amount > Wad(0)\n\n    deployment_address = Address(\"0x00a329c0648769A73afAc7F9381E08FB43dBEA72\")\n    assert mkr.mint(amount).transact(from_address=deployment_address)\n    assert mkr.balance_of(deployment_address) > Wad(0)\n    assert mkr.approve(recipient_address).transact(from_address=deployment_address)\n    assert mkr.transfer(recipient_address, amount).transact(from_address=deployment_address)\n\n\ndef get_collateral_price(collateral: Collateral):\n    assert isinstance(collateral, Collateral)\n    return Wad(Web3.toInt(collateral.pip.read()))\n\n\ndef set_collateral_price(mcd: DssDeployment, collateral: Collateral, price: Wad):\n    assert isinstance(mcd, DssDeployment)\n    assert isinstance(collateral, Collateral)\n    assert isinstance(price, Wad)\n    assert price > Wad(0)\n\n    pip = collateral.pip\n    assert isinstance(pip, DSValue)\n\n    print(f\"Changing price of {collateral.ilk.name} to {price}\")\n    assert pip.poke_with_int(price.value).transact(from_address=pip.get_owner())\n    assert mcd.spotter.poke(ilk=collateral.ilk).transact(from_address=pip.get_owner())\n\n    assert get_collateral_price(collateral) == price\n\n\ndef frob(mcd: DssDeployment, collateral: Collateral, address: Address, dink: Wad, dart: Wad):\n    \"\"\"Wraps vat.frob for debugging purposes\"\"\"\n    # given\n    assert isinstance(mcd, DssDeployment)\n    assert isinstance(collateral, Collateral)\n    assert isinstance(address, Address)\n    assert isinstance(dink, Wad)\n    assert isinstance(dart, Wad)\n    ilk = collateral.ilk\n\n    # when\n    ink_before = mcd.vat.urn(ilk, address).ink\n    art_before = mcd.vat.urn(ilk, address).art\n\n    # then\n    if dart < Wad(0):\n        assert mcd.vat.dai(address) >= Rad(dart*-1)\n    assert mcd.vat.frob(ilk=ilk, urn_address=address, dink=dink, dart=dart).transact(from_address=address)\n    assert mcd.vat.urn(ilk, address).ink == ink_before + dink\n    assert mcd.vat.urn(ilk, address).art == art_before + dart\n\n\ndef max_dart(mcd: DssDeployment, collateral: Collateral, our_address: Address) -> Wad:\n    \"\"\"Determines how much stablecoin should be reserved in an `urn` to make it as poorly collateralized as\n    possible, such that a small change to the collateral price could trip the liquidation ratio.\"\"\"\n    assert isinstance(mcd, DssDeployment)\n    assert isinstance(collateral, Collateral)\n    assert isinstance(our_address, Address)\n\n    urn = mcd.vat.urn(collateral.ilk, our_address)\n    ilk = mcd.vat.ilk(collateral.ilk.name)\n\n    # change in art = (collateral balance * collateral price with safety margin) - CDP's stablecoin debt\n    dart = urn.ink * ilk.spot - Wad(Ray(urn.art) * ilk.rate)\n\n    # change in debt must also take the rate into account\n    dart = dart * Wad(Ray.from_number(1) / ilk.rate)\n\n    # prevent the change in debt from exceeding the collateral debt ceiling\n    if (Rad(urn.art) + Rad(dart)) >= ilk.line:\n        print(\"max_dart is avoiding collateral debt ceiling\")\n        dart = Wad(ilk.line - Rad(urn.art))\n\n    # prevent the change in debt from exceeding the total debt ceiling\n    debt = mcd.vat.debt() + Rad(ilk.rate * dart)\n    line = Rad(ilk.line)\n    if (debt + Rad(dart)) >= line:\n        print(\"max_dart is avoiding total debt ceiling\")\n        dart = Wad(debt - Rad(urn.art))\n\n    assert dart > Wad(0)\n    return dart\n\n\ndef cleanup_urn(mcd: DssDeployment, collateral: Collateral, address: Address):\n    assert isinstance(mcd, DssDeployment)\n    assert isinstance(collateral, Collateral)\n    assert isinstance(address, Address)\n    urn = mcd.vat.urn(collateral.ilk, address)\n    ilk = mcd.vat.ilk(collateral.ilk.name)\n\n    # If jug.drip has been called, we won't have sufficient dai to repay the CDP\n    if ilk.rate > Ray.from_number(1):\n        return\n\n    # Repay borrowed Dai\n    mcd.approve_dai(address)\n    # Put all the user's Dai back into the vat\n    if mcd.dai.balance_of(address) >= Wad(0):\n        assert mcd.dai_adapter.join(address, mcd.dai.balance_of(address)).transact(from_address=address)\n    # tab = Ray(urn.art) * ilk.rate\n    # print(f'tab={str(tab)}, rate={str(ilk.rate)}, dai={str(mcd.vat.dai(address))}')\n    if urn.art > Wad(0) and mcd.vat.dai(address) >= Rad(urn.art):\n        frob(mcd, collateral, address, Wad(0), urn.art * -1)\n\n    # Withdraw collateral\n    collateral.approve(address)\n    urn = mcd.vat.urn(collateral.ilk, address)\n    # dink = Wad((Ray(urn.art) * ilk.rate) / ilk.spot)\n    # print(f'dink={str(dink)}, ink={str(urn.ink)}')\n    if urn.art == Wad(0) and urn.ink > Wad(0):\n        frob(mcd, collateral, address, urn.ink * -1, Wad(0))\n    assert collateral.adapter.exit(address, mcd.vat.gem(collateral.ilk, address)).transact(from_address=address)\n    # TestVat.ensure_clean_urn(mcd, collateral, address)\n\n\n@pytest.fixture(scope=\"session\")\ndef bite(web3: Web3, mcd: DssDeployment, our_address: Address):\n    collateral = mcd.collaterals['ETH-A']\n\n    # Add collateral to our CDP\n    dink = Wad.from_number(1)\n    wrap_eth(mcd, our_address, dink)\n    assert collateral.gem.balance_of(our_address) >= dink\n    assert collateral.adapter.join(our_address, dink).transact()\n    frob(mcd, collateral, our_address, dink, Wad(0))\n\n    # Define required bite parameters\n    to_price = Wad(Web3.toInt(collateral.pip.read())) / Wad.from_number(2)\n\n    # Manipulate price to make our CDP underwater\n    # Note this will only work on a testchain deployed with fixed prices, where PIP is a DSValue\n    frob(mcd, collateral, our_address, Wad(0), max_dart(mcd, collateral, our_address))\n    set_collateral_price(mcd, collateral, to_price)\n\n    # Bite the CDP\n    assert mcd.cat.can_bite(collateral.ilk, Urn(our_address))\n    assert mcd.cat.bite(collateral.ilk, Urn(our_address)).transact()\n\n\n@pytest.fixture(scope=\"session\")\ndef bite_event(web3: Web3, mcd: DssDeployment, our_address: Address):\n    bite(web3, mcd, our_address)\n    # Return the corresponding event\n    return mcd.cat.past_bites(1)[0]\n\n\nclass TestConfig:\n    def test_from_json(self, web3: Web3, mcd: DssDeployment):\n        # fixture calls DssDeployment.from_json\n        assert len(mcd.config.collaterals) >= 3\n        assert len(mcd.collaterals) >= 3\n        assert len(mcd.config.to_dict()) > 10\n        assert len(mcd.collaterals) == len(mcd.config.collaterals)\n\n    def test_to_json(self, web3: Web3, mcd: DssDeployment):\n        config_out = mcd.to_json()\n        dict = json.loads(config_out)\n        assert \"MCD_GOV\" in dict\n        assert \"MCD_DAI\" in dict\n        assert len(dict) > 20\n\n    def test_from_node(self, web3: Web3):\n        mcd_testnet = DssDeployment.from_node(web3)\n        validate_contracts_loaded(mcd_testnet)\n\n    def test_collaterals(self, mcd):\n        for collateral in mcd.collaterals.values():\n            assert isinstance(collateral.ilk, Ilk)\n            assert isinstance(collateral.gem, ERC20Token)\n            assert len(collateral.ilk.name) > 0\n            assert len(collateral.gem.name()) > 0\n            assert len(collateral.gem.symbol()) > 0\n            assert collateral.adapter\n            assert collateral.flipper or collateral.clipper\n            assert collateral.pip\n\n    def test_account_transfers(self, web3: Web3, mcd, our_address, other_address):\n        print(mcd.collaterals)\n        collateral = mcd.collaterals['ETH-A']\n        token = collateral.gem\n        amount = Wad(10)\n\n        assert web3.eth.defaultAccount == our_address.address\n        assert our_address != other_address\n        wrap_eth(mcd, our_address, amount)\n\n        # Move eth between each account to confirm keys are properly set up\n        before = token.balance_of(our_address)\n        assert token.transfer_from(our_address, other_address, amount).transact()\n        after = token.balance_of(our_address)\n        assert (before - amount) == after\n\n        assert token.transfer_from(other_address, our_address, amount).transact(from_address=other_address)\n        assert token.balance_of(our_address) == before\n\n    def test_get_active_auctions(self, mcd):\n        auctions = mcd.active_auctions()\n        assert \"flips\" in auctions\n        assert \"flaps\" in auctions\n        assert \"flops\" in auctions\n\n\nclass TestVat:\n    @staticmethod\n    def ensure_clean_urn(mcd: DssDeployment, collateral: Collateral, address: Address):\n        assert isinstance(mcd, DssDeployment)\n        assert isinstance(collateral, Collateral)\n        assert isinstance(address, Address)\n\n        urn = mcd.vat.urn(collateral.ilk, address)\n        assert urn.ink == Wad(0)\n        assert urn.art == Wad(0)\n        assert mcd.vat.gem(collateral.ilk, address) == Wad(0)\n\n    def test_getters(self, mcd):\n        assert isinstance(mcd.vat.live(), bool)\n\n    def test_ilk(self, mcd):\n        assert mcd.vat.ilk('XXX') == Ilk('XXX',\n                                         rate=Ray(0), ink=Wad(0), art=Wad(0), spot=Ray(0), line=Rad(0), dust=Rad(0))\n\n        ilk = mcd.collaterals[\"ETH-C\"].ilk\n        assert ilk.line == Rad.from_number(15000000)\n        assert ilk.dust == Rad(0)\n\n        representation = repr(ilk)\n        assert \"ETH-C\" in representation\n\n    def test_gem(self, web3: Web3, mcd: DssDeployment, our_address: Address):\n        # given\n        collateral = mcd.collaterals['ETH-A']\n        amount_to_join = Wad(10)\n        our_urn = mcd.vat.urn(collateral.ilk, our_address)\n        assert isinstance(collateral.ilk, Ilk)\n        assert isinstance(collateral.adapter, GemJoin)\n        assert collateral.ilk.name == collateral.adapter.ilk().name\n        assert our_urn.address == our_address\n        wrap_eth(mcd, our_address, amount_to_join)\n        assert collateral.gem.balance_of(our_address) >= amount_to_join\n\n        # when\n        before_join = mcd.vat.gem(collateral.ilk, our_urn.address)\n        collateral.approve(our_address)\n        assert collateral.adapter.join(our_address, amount_to_join).transact()\n        after_join = mcd.vat.gem(collateral.ilk, our_urn.address)\n        assert collateral.adapter.exit(our_address, amount_to_join).transact()\n        after_exit = mcd.vat.gem(collateral.ilk, our_urn.address)\n\n        # then\n        assert after_join - before_join == amount_to_join\n        assert after_exit == before_join\n\n    def test_gem_join(self, mcd: DssDeployment):\n        collateral_bat = mcd.collaterals['BAT-A']\n        assert isinstance(collateral_bat.adapter, GemJoin)\n        assert collateral_bat.adapter.dec() == 18\n\n        collateral_usdc = mcd.collaterals['USDC-A']\n        assert isinstance(collateral_usdc.adapter, GemJoin5)\n        assert collateral_usdc.adapter.dec() == 6\n\n    def test_dai(self, mcd, urn):\n        dai = mcd.vat.dai(urn.address)\n        assert dai >= Rad(0)\n\n    def test_sin(self, mcd, urn):\n        sin = mcd.vat.sin(urn.address)\n        assert isinstance(sin, Rad)\n        assert sin == Rad(0)\n\n    def test_debt(self, mcd):\n        debt = mcd.vat.debt()\n        assert debt >= Rad(0)\n        assert debt < mcd.vat.line()\n\n    def test_urn(self, urn):\n        time.sleep(11)\n        assert urn.ilk is not None\n        urn_bytes = urn.toBytes()\n        urn_from_bytes = urn.fromBytes(urn_bytes)\n        assert urn_from_bytes.address == urn.address\n\n    def test_frob_noop(self, mcd, our_address):\n        # given\n        collateral = mcd.collaterals['ETH-A']\n        our_urn = mcd.vat.urn(collateral.ilk, our_address)\n\n        # when\n        assert mcd.vat.frob(collateral.ilk, our_address, Wad(0), Wad(0)).transact()\n\n        # then\n        assert mcd.vat.urn(collateral.ilk, our_address) == our_urn\n\n    def test_frob_add_ink(self, mcd, our_address):\n        # given\n        collateral = mcd.collaterals['ETH-A']\n        our_urn = mcd.vat.urn(collateral.ilk, our_address)\n\n        # when\n        wrap_eth(mcd, our_address, Wad(10))\n        assert collateral.adapter.join(our_address, Wad(10)).transact()\n        assert mcd.vat.frob(collateral.ilk, our_address, Wad(10), Wad(0)).transact()\n\n        # then\n        assert mcd.vat.urn(collateral.ilk, our_address).ink == our_urn.ink + Wad(10)\n\n        # rollback\n        cleanup_urn(mcd, collateral, our_address)\n\n    def test_frob_add_art(self, mcd, our_address: Address):\n        # given\n        collateral = mcd.collaterals['ETH-A']\n        our_urn = mcd.vat.urn(collateral.ilk, our_address)\n\n        # when\n        wrap_eth(mcd, our_address, Wad.from_number(10))\n        assert collateral.adapter.join(our_address, Wad.from_number(3)).transact()\n        assert mcd.vat.frob(collateral.ilk, our_address, Wad.from_number(3), Wad.from_number(24)).transact()\n\n        # then\n        assert mcd.vat.urn(collateral.ilk, our_address).art == our_urn.art + Wad.from_number(24)\n\n        # rollback\n        cleanup_urn(mcd, collateral, our_address)\n\n    def test_frob_other_account(self, web3, mcd, other_address):\n        # given\n        collateral = mcd.collaterals['ETH-A']\n        collateral.approve(other_address)\n        mcd.dai_adapter.approve(hope_directly(from_address=other_address), mcd.vat.address)\n        urn = mcd.vat.urn(collateral.ilk, other_address)\n        assert urn.address == other_address\n\n        # when\n        wrap_eth(mcd, other_address, Wad.from_number(10))\n        assert collateral.gem.balance_of(other_address) >= Wad.from_number(10)\n        assert collateral.gem == collateral.adapter.gem()\n        collateral.gem.approve(collateral.adapter.address)\n        assert collateral.adapter.join(other_address, Wad.from_number(3)).transact(from_address=other_address)\n        assert mcd.vat.frob(collateral.ilk, other_address, Wad.from_number(3), Wad.from_number(20)).transact(from_address=other_address)\n\n        # then\n        assert mcd.vat.urn(collateral.ilk, other_address).art == urn.art + Wad.from_number(20)\n\n        # rollback\n        cleanup_urn(mcd, collateral, other_address)\n\n    def test_past_frob(self, mcd, our_address, other_address):\n        # given\n        collateral0 = mcd.collaterals['ETH-B']\n        ilk0 = collateral0.ilk\n        collateral1 = mcd.collaterals['ETH-C']\n        ilk1 = collateral1.ilk\n\n        try:\n            # when\n            wrap_eth(mcd, our_address, Wad.from_number(18))\n            wrap_eth(mcd, other_address, Wad.from_number(18))\n\n            collateral0.approve(our_address)\n            assert collateral0.adapter.join(our_address, Wad.from_number(9)).transact()\n            assert mcd.vat.frob(ilk0, our_address, Wad.from_number(3), Wad.from_number(0)).transact()\n\n            collateral1.approve(other_address)\n            assert collateral1.adapter.join(other_address, Wad.from_number(9)).transact(from_address=other_address)\n            assert mcd.vat.frob(ilk1, other_address, Wad.from_number(9), Wad.from_number(0)).transact(from_address=other_address)\n            assert mcd.vat.frob(ilk1, other_address, Wad.from_number(-3), Wad.from_number(0)).transact(from_address=other_address)\n\n            assert mcd.vat.frob(ilk1, our_address, Wad.from_number(3), Wad.from_number(0),\n                                collateral_owner=other_address, dai_recipient=other_address).transact(\n                from_address=other_address)\n\n            # then\n            current_block = mcd.web3.eth.blockNumber\n            from_block = current_block - 6\n            frobs = mcd.vat.past_frobs(from_block)\n            assert len(frobs) == 4\n            assert frobs[0].ilk == ilk0.name\n            assert frobs[0].urn == our_address\n            assert frobs[0].dink == Wad.from_number(3)\n            assert frobs[0].dart == Wad(0)\n            assert frobs[1].ilk == ilk1.name\n            assert frobs[1].urn == other_address\n            assert frobs[1].dink == Wad.from_number(9)\n            assert frobs[1].dart == Wad(0)\n            assert frobs[2].ilk == ilk1.name\n            assert frobs[2].urn == other_address\n            assert frobs[2].dink == Wad.from_number(-3)\n            assert frobs[2].dart == Wad(0)\n            assert frobs[3].urn == our_address\n            assert frobs[3].collateral_owner == other_address\n            assert frobs[3].dink == Wad.from_number(3)\n            assert frobs[3].dart == Wad(0)\n\n            assert len(mcd.vat.past_frobs(from_block, ilk=ilk0)) == 1\n            assert len(mcd.vat.past_frobs(from_block, ilk=ilk1)) == 3\n            assert len(mcd.vat.past_frobs(from_block, ilk=mcd.collaterals['USDC-A'].ilk)) == 0\n\n        finally:\n            # teardown\n            cleanup_urn(mcd, collateral0, our_address)\n            cleanup_urn(mcd, collateral1, other_address)\n\n    def test_heal(self, mcd):\n        assert mcd.vat.heal(Rad(0)).transact()\n\n    def test_flux(self, mcd, our_address, other_address):\n        # given\n        collateral = mcd.collaterals['ETH-A']\n        collateral.approve(our_address)\n        other_balance_before = mcd.vat.gem(collateral.ilk, other_address)\n        amount = Wad.from_number(3)\n        wrap_eth(mcd, our_address, amount)\n        assert collateral.adapter.join(our_address, amount).transact()\n\n        # when\n        assert mcd.vat.flux(collateral.ilk, our_address, other_address, amount).transact()\n\n        # then\n        other_balance_after = mcd.vat.gem(collateral.ilk, other_address)\n        assert Wad(other_balance_before) + amount == Wad(other_balance_after)\n\n        # teardown\n        cleanup_urn(mcd, collateral, our_address)\n\n    def test_move(self, mcd, our_address, other_address):\n        # given\n        collateral = mcd.collaterals['ETH-A']\n        collateral.approve(our_address)\n        our_urn = mcd.vat.urn(collateral.ilk, our_address)\n        wrap_eth(mcd, our_address, Wad.from_number(10))\n        assert collateral.adapter.join(our_address, Wad.from_number(3)).transact()\n        assert mcd.vat.frob(collateral.ilk, our_address, Wad.from_number(3), Wad.from_number(30)).transact()\n        other_balance_before = mcd.vat.dai(other_address)\n\n        # when\n        assert mcd.vat.move(our_address, other_address, Rad.from_number(30)).transact()\n\n        # then\n        other_balance_after = mcd.vat.dai(other_address)\n        assert other_balance_before + Rad.from_number(30) == other_balance_after\n\n        # confirm log was emitted and could be parsed\n        from_block = mcd.web3.eth.blockNumber\n        logs = mcd.vat.past_logs(from_block)\n        assert isinstance(logs[0], Vat.LogMove)\n        logmove: Vat.LogMove = logs[0]\n        assert logmove.src == our_address\n        assert logmove.dst == other_address\n        assert logmove.dart == Rad.from_number(30)\n\n        # rollback\n        cleanup_urn(mcd, collateral, our_address)\n\n    def test_fork(self, mcd, our_address, other_address):\n        # given\n        collateral = mcd.collaterals['ETH-A']\n        mcd.vat.hope(our_address).transact(from_address=other_address)\n        mcd.vat.hope(other_address).transact(from_address=our_address)\n\n        our_urn = mcd.vat.urn(collateral.ilk, our_address)\n        wrap_eth(mcd, our_address, Wad.from_number(6))\n        assert collateral.adapter.join(our_address, Wad.from_number(6)).transact()\n        assert mcd.vat.frob(collateral.ilk, our_address, Wad.from_number(6), Wad.from_number(40)).transact()\n        urn_before = mcd.vat.urn(collateral.ilk, other_address)\n\n        # when\n        assert mcd.vat.fork(collateral.ilk, our_address, other_address, Wad.from_number(3), Wad.from_number(20)).transact()\n\n        # then\n        urn_after = mcd.vat.urn(collateral.ilk, other_address)\n        assert urn_before.ink + Wad.from_number(3) == urn_after.ink\n        assert urn_before.art + Wad.from_number(20) == urn_after.art\n\n        # confirm log was emitted and could be parsed\n        from_block = mcd.web3.eth.blockNumber\n        logs = mcd.vat.past_logs(from_block)\n        assert isinstance(logs[0], Vat.LogFork)\n        logfork: Vat.LogFork = logs[0]\n        assert logfork.ilk == collateral.ilk.name\n        assert logfork.src == our_address\n        assert logfork.dst == other_address\n        assert logfork.dink == Wad.from_number(3)\n        assert logfork.dart == Wad.from_number(20)\n\n        # rollback\n        cleanup_urn(mcd, collateral, our_address)\n\n\nclass TestCat:\n    def test_getters(self, mcd):\n        assert isinstance(mcd.cat.live(), bool)\n        assert isinstance(mcd.cat.vat, Vat)\n        assert isinstance(mcd.cat.vow, Vow)\n\n        collateral = mcd.collaterals['ETH-C']\n        assert not collateral.clipper\n        assert mcd.cat.flipper(collateral.ilk) == collateral.flipper.address\n        assert mcd.cat.chop(collateral.ilk) == Wad.from_number(1.05)\n        assert mcd.cat.dunk(collateral.ilk) == Rad.from_number(1000)\n        assert mcd.cat.box() == Rad.from_number(5000)\n\n\nclass TestDog:\n    def test_getters(self, mcd):\n        assert isinstance(mcd.dog.live(), bool)\n        assert isinstance(mcd.cat.vat, Vat)\n        assert isinstance(mcd.cat.vow, Vow)\n\n        collateral = mcd.collaterals['ETH-B']\n        assert not collateral.flipper\n        assert mcd.dog.clipper(collateral.ilk) == collateral.clipper.address\n        assert mcd.dog.chop(collateral.ilk) == Wad.from_number(1.05)\n        assert mcd.dog.hole(collateral.ilk) == Rad.from_number(300)\n        assert mcd.dog.dirt(collateral.ilk) == Rad(0)\n        assert mcd.dog.dog_hole() == Rad.from_number(5000)\n        assert mcd.dog.dog_dirt() == Rad(0)\n\n\nclass TestSpotter:\n    def test_mat(self, mcd):\n        val = Ray(mcd.collaterals['ETH-A'].pip.read_as_int())\n\n        ilk = mcd.vat.ilk('ETH-A')\n        par = mcd.spotter.par()\n        mat = mcd.spotter.mat(ilk)\n\n        assert mat == (Ray(val * 10 ** 9) / par) / (ilk.spot)\n\n\nclass TestVow:\n    def test_getters(self, mcd):\n        assert isinstance(mcd.vow.vat, Vat)\n        assert isinstance(mcd.vow.live(), bool)\n        assert isinstance(mcd.vow.flopper(), Address)\n        assert isinstance(mcd.vow.flopper(), Address)\n        assert isinstance(mcd.vow.sin(), Rad)\n        assert isinstance(mcd.vow.sin_of(0), Rad)\n        assert isinstance(mcd.vow.ash(), Rad)\n        assert isinstance(mcd.vow.woe(), Rad)\n        assert isinstance(mcd.vow.wait(), int)\n        assert isinstance(mcd.vow.dump(), Wad)\n        assert isinstance(mcd.vow.sump(), Rad)\n        assert isinstance(mcd.vow.bump(), Rad)\n        assert isinstance(mcd.vow.hump(), Rad)\n\n    def test_empty_flog(self, mcd):\n        assert mcd.vow.flog(0).transact()\n\n    def test_heal(self, mcd):\n        assert mcd.vow.heal(Rad(0)).transact()\n\n    def test_kiss(self, mcd):\n        assert mcd.vow.kiss(Rad(0)).transact()\n\n\nclass TestJug:\n    def test_getters(self, mcd, our_address):\n        c = mcd.collaterals['ETH-A']\n        assert isinstance(mcd.jug.vat, Vat)\n        assert isinstance(mcd.jug.vow, Vow)\n        assert isinstance(mcd.jug.base(), Ray)\n        assert isinstance(mcd.jug.duty(c.ilk), Ray)\n        assert isinstance(mcd.jug.rho(c.ilk), int)\n        assert not mcd.jug.wards(our_address)\n\n    def test_drip(self, mcd):\n        # given\n        c = mcd.collaterals['ETH-A']\n\n        # then\n        rho_before = mcd.jug.rho(c.ilk)\n        assert rho_before > 0\n        assert mcd.jug.drip(c.ilk).transact()\n        rho_after = mcd.jug.rho(c.ilk)\n        assert rho_before < rho_after\n\n\nclass TestPot:\n    def test_getters(self, mcd):\n        assert isinstance(mcd.pot.pie(), Wad)\n        assert isinstance(mcd.pot.dsr(), Ray)\n        assert isinstance(mcd.pot.rho(), datetime)\n\n        assert mcd.pot.pie() >= Wad(0)\n        assert mcd.pot.dsr() > Ray.from_number(1)\n        # assert datetime.fromtimestamp(0) < mcd.pot.rho() < datetime.utcnow()\n\n    def test_drip(self, mcd):\n        chi_before = mcd.pot.chi()\n        assert isinstance(chi_before, Ray)\n        assert mcd.pot.drip().transact()\n        chi_after = mcd.pot.chi()\n        if mcd.pot.dsr() == Ray.from_number(1):\n            assert chi_before == chi_after\n        else:\n            assert chi_before < chi_after\n\n\nclass TestOsm:\n    def test_price(self, web3, mcd):\n        collateral = mcd.collaterals['ETH-B']\n        set_collateral_price(mcd, collateral, Wad.from_number(200))\n        # Note this isn't actually an OSM, but we can still read storage slots\n        osm = OSM(web3, collateral.pip.address)\n        raw_price = osm._extract_price(2)\n        assert isinstance(raw_price, int)\n        assert Wad.from_number(200) == Wad(raw_price)\n\n\nclass TestMcd:\n    def test_healthy_cdp(self, mcd, our_address):\n        collateral = mcd.collaterals['ETH-B']\n        ilk = collateral.ilk\n        TestVat.ensure_clean_urn(mcd, collateral, our_address)\n        initial_dai = mcd.vat.dai(our_address)\n        wrap_eth(mcd, our_address, Wad.from_number(9))\n\n        # Ensure our collateral enters the urn\n        collateral_balance_before = collateral.gem.balance_of(our_address)\n        collateral.approve(our_address)\n        assert collateral.adapter.join(our_address, Wad.from_number(9)).transact()\n        assert collateral.gem.balance_of(our_address) == collateral_balance_before - Wad.from_number(9)\n\n        # Add collateral without generating Dai\n        frob(mcd, collateral, our_address, dink=Wad.from_number(3), dart=Wad(0))\n        print(f\"After adding collateral:         {mcd.vat.urn(ilk, our_address)}\")\n        assert mcd.vat.urn(ilk, our_address).ink == Wad.from_number(3)\n        assert mcd.vat.urn(ilk, our_address).art == Wad(0)\n        assert mcd.vat.gem(ilk, our_address) == Wad.from_number(9) - mcd.vat.urn(ilk, our_address).ink\n        assert mcd.vat.dai(our_address) == initial_dai\n\n        # Generate some Dai\n        frob(mcd, collateral, our_address, dink=Wad(0), dart=Wad.from_number(153))\n        print(f\"After generating dai:            {mcd.vat.urn(ilk, our_address)}\")\n        assert mcd.vat.urn(ilk, our_address).ink == Wad.from_number(3)\n        assert mcd.vat.urn(ilk, our_address).art == Wad.from_number(153)\n        assert mcd.vat.dai(our_address) == initial_dai + Rad.from_number(153)\n\n        # Add collateral and generate some more Dai\n        frob(mcd, collateral, our_address, dink=Wad.from_number(6), dart=Wad.from_number(180))\n        print(f\"After adding collateral and dai: {mcd.vat.urn(ilk, our_address)}\")\n        assert mcd.vat.urn(ilk, our_address).ink == Wad.from_number(9)\n        assert mcd.vat.gem(ilk, our_address) == Wad(0)\n        assert mcd.vat.urn(ilk, our_address).art == Wad.from_number(333)\n        assert mcd.vat.dai(our_address) == initial_dai + Rad.from_number(333)\n\n        # Mint and withdraw our Dai\n        dai_balance_before = mcd.dai.balance_of(our_address)\n        mcd.approve_dai(our_address)\n        assert isinstance(mcd.dai_adapter, DaiJoin)\n        assert mcd.dai_adapter.exit(our_address, Wad.from_number(333)).transact()\n        assert mcd.dai.balance_of(our_address) == dai_balance_before + Wad.from_number(333)\n        assert mcd.vat.dai(our_address) == initial_dai\n        assert mcd.vat.debt() >= initial_dai + Rad.from_number(333)\n\n        # Repay (and burn) our Dai\n        assert mcd.dai_adapter.join(our_address, Wad.from_number(333)).transact()\n        assert mcd.dai.balance_of(our_address) == Wad(0)\n        assert mcd.vat.dai(our_address) == initial_dai + Rad.from_number(333)\n        wipe = mcd.vat.get_wipe_all_dart(collateral.ilk, our_address)\n        assert wipe >= Wad.from_number(333)\n        frob(mcd, collateral, our_address, dink=Wad(0), dart=wipe*-1)\n\n        # Withdraw our collateral\n        frob(mcd, collateral, our_address, dink=Wad.from_number(-9), dart=Wad(0))\n        assert mcd.vat.gem(ilk, our_address) == Wad.from_number(9)\n        assert collateral.adapter.exit(our_address, Wad.from_number(9)).transact()\n        collateral_balance_after = collateral.gem.balance_of(our_address)\n        assert collateral_balance_before == collateral_balance_after\n\n        # Cleanup\n        cleanup_urn(mcd, collateral, our_address)\n\n    @pytest.mark.skip(\"awaiting change to dss-deploy-scripts allowing faucets to be enabled on local testnet\")\n    def test_faucet(self, mcd, our_address):\n        token = mcd.collaterals['GUSD-A'].gem\n        balance_before = token.balance_of(our_address)\n        assert mcd.faucet.gulp(token.address).transact(from_address=our_address)\n        balance_after = token.balance_of(our_address)\n        assert balance_before < balance_after\n\n    def test_empty_auctions_collection(self, mcd):\n        for auction_type, collection in mcd.active_auctions().items():\n            assert collection is not None\n            if auction_type in ['flaps', 'flops']:\n                assert len(collection) == 0\n            elif auction_type in ['clips', 'flips']:\n                assert len(collection) > 0\n                for collateral, collateral_auctions in collection.items():\n                    assert isinstance(collateral, str)\n                    assert collateral_auctions is not None\n                    assert len(collateral_auctions) == 0\n"
  },
  {
    "path": "tests/test_etherdelta.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2017-2018 reverendus\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nimport pytest\nfrom mock import Mock\nfrom web3 import Web3, HTTPProvider\n\nfrom pymaker import Address\nfrom pymaker.approval import directly\nfrom pymaker.etherdelta import EtherDelta, EtherDeltaApi\nfrom pymaker.numeric import Wad\nfrom pymaker.token import DSToken\nfrom tests.helpers import is_hashable, wait_until_mock_called\n\nPAST_BLOCKS = 100\n\n\nclass TestEtherDelta:\n    def setup_method(self):\n        self.web3 = Web3(HTTPProvider(\"http://localhost:8555\"))\n        self.web3.eth.defaultAccount = self.web3.eth.accounts[0]\n        self.our_address = Address(self.web3.eth.defaultAccount)\n        self.etherdelta = EtherDelta.deploy(self.web3,\n                                            admin=Address('0x1111100000999998888877777666665555544444'),\n                                            fee_account=Address('0x8888877777666665555544444111110000099999'),\n                                            account_levels_addr=Address('0x0000000000000000000000000000000000000000'),\n                                            fee_make=Wad.from_number(0.01),\n                                            fee_take=Wad.from_number(0.02),\n                                            fee_rebate=Wad.from_number(0.03))\n        self.token1 = DSToken.deploy(self.web3, 'AAA')\n        self.token1.mint(Wad.from_number(100)).transact()\n        self.token2 = DSToken.deploy(self.web3, 'BBB')\n        self.token2.mint(Wad.from_number(100)).transact()\n\n    def test_fail_when_no_contract_under_that_address(self):\n        # expect\n        with pytest.raises(Exception):\n            EtherDelta(web3=self.web3, address=Address('0xdeadadd1e5500000000000000000000000000000'))\n\n    def test_addresses(self):\n        # expect\n        assert self.etherdelta.admin() == Address('0x1111100000999998888877777666665555544444')\n        assert self.etherdelta.fee_account() == Address('0x8888877777666665555544444111110000099999')\n        assert self.etherdelta.account_levels_addr() == Address('0x0000000000000000000000000000000000000000')\n\n    def test_fees(self):\n        # expect\n        assert self.etherdelta.fee_make() == Wad.from_number(0.01)\n        assert self.etherdelta.fee_take() == Wad.from_number(0.02)\n        assert self.etherdelta.fee_rebate() == Wad.from_number(0.03)\n\n    def test_deposit_and_withdraw_eth(self):\n        # when\n        self.etherdelta.deposit(Wad.from_number(2.5)).transact()\n\n        # then\n        assert self.etherdelta.balance_of(self.our_address) == Wad.from_number(2.5)\n\n        # when\n        self.etherdelta.withdraw(Wad.from_number(1.1)).transact()\n\n        # then\n        assert self.etherdelta.balance_of(self.our_address) == Wad.from_number(1.4)\n\n    def test_deposit_and_withdraw_token(self):\n        # given\n        self.etherdelta.approve([self.token1], directly())\n\n        # when\n        self.etherdelta.deposit_token(self.token1.address, Wad.from_number(1.5)).transact()\n\n        # then\n        assert self.etherdelta.balance_of_token(self.token1.address, self.our_address) == Wad.from_number(1.5)\n\n        # when\n        self.etherdelta.withdraw_token(self.token1.address, Wad.from_number(0.2)).transact()\n\n        # then\n        assert self.etherdelta.balance_of_token(self.token1.address, self.our_address) == Wad.from_number(1.3)\n\n    def test_offchain_order_happy_path(self):\n        # given\n        self.etherdelta.approve([self.token1, self.token2], directly())\n        self.etherdelta.deposit_token(self.token1.address, Wad.from_number(10)).transact()\n        self.etherdelta.deposit_token(self.token2.address, Wad.from_number(10)).transact()\n\n        # when\n        order = self.etherdelta.create_order(pay_token=self.token1.address, pay_amount=Wad.from_number(2),\n                                             buy_token=self.token2.address, buy_amount=Wad.from_number(4),\n                                             expires=100000000)\n\n        # then\n        assert order.maker == self.our_address\n        assert order.pay_token == self.token1.address\n        assert order.pay_amount == Wad.from_number(2)\n        assert order.buy_token == self.token2.address\n        assert order.buy_amount == Wad.from_number(4)\n        assert order.expires == 100000000\n\n        # and\n        assert self.etherdelta.amount_available(order) == Wad.from_number(4)\n        assert self.etherdelta.amount_filled(order) == Wad.from_number(0)\n        assert order.remaining_sell_amount == Wad.from_number(2)\n        assert order.remaining_buy_amount == Wad.from_number(4)\n\n        # and\n        assert self.etherdelta.can_trade(order, Wad.from_number(1.5))\n        assert not self.etherdelta.can_trade(order, Wad.from_number(5.5))\n\n        # when\n        self.etherdelta.trade(order, Wad.from_number(1.5)).transact()\n\n        # then\n        assert self.etherdelta.amount_available(order) == Wad.from_number(2.5)\n        assert self.etherdelta.amount_filled(order) == Wad.from_number(1.5)\n        assert order.remaining_sell_amount == Wad.from_number(1.25)\n        assert order.remaining_buy_amount == Wad.from_number(2.5)\n\n        # when\n        self.etherdelta.withdraw_token(self.token1.address, Wad.from_number(9.3)).transact()\n\n        # then\n        assert self.etherdelta.amount_available(order) == Wad.from_number(1.4)\n        assert self.etherdelta.amount_filled(order) == Wad.from_number(1.5)\n\n        # when\n        self.etherdelta.cancel_order(order).transact()\n\n        # then\n        assert self.etherdelta.amount_available(order) == Wad.from_number(0)\n        assert self.etherdelta.amount_filled(order) == Wad.from_number(4)\n\n    def test_no_past_events_on_startup(self):\n        assert self.etherdelta.past_trade(PAST_BLOCKS) == []\n\n    def test_past_take(self):\n        # given\n        self.etherdelta.approve([self.token1, self.token2], directly())\n        self.etherdelta.deposit_token(self.token1.address, Wad.from_number(10)).transact()\n        self.etherdelta.deposit_token(self.token2.address, Wad.from_number(10)).transact()\n\n        # when\n        order = self.etherdelta.create_order(pay_token=self.token1.address, pay_amount=Wad.from_number(2),\n                                             buy_token=self.token2.address, buy_amount=Wad.from_number(4),\n                                             expires=100000000)\n\n        # and\n        self.etherdelta.trade(order, Wad.from_number(1.5)).transact()\n\n        # then\n        past_trade = self.etherdelta.past_trade(PAST_BLOCKS)\n        assert len(past_trade) == 1\n        assert past_trade[0].maker == self.our_address\n        assert past_trade[0].taker == self.our_address\n        assert past_trade[0].pay_token == self.token1.address\n        assert past_trade[0].buy_token == self.token2.address\n        assert past_trade[0].take_amount == Wad.from_number(0.75)\n        assert past_trade[0].give_amount == Wad.from_number(1.5)\n        assert past_trade[0].raw['blockNumber'] > 0\n\n    def test_order_comparison(self):\n        # given\n        order1 = self.etherdelta.create_order(pay_token=self.token1.address, pay_amount=Wad.from_number(2),\n                                              buy_token=self.token2.address, buy_amount=Wad.from_number(4),\n                                              expires=100000000)\n\n        # and\n        order2 = self.etherdelta.create_order(pay_token=self.token1.address, pay_amount=Wad.from_number(2),\n                                              buy_token=self.token2.address, buy_amount=Wad.from_number(4),\n                                              expires=100000000)\n\n        # then\n        assert order1 == order1\n        assert order1 != order2  # even if both orders seem to be identical, they will have different\n                                 # nonces generated so they are not the same order\n\n    def test_order_hashable(self):\n        # given\n        order1 = self.etherdelta.create_order(pay_token=self.token1.address, pay_amount=Wad.from_number(2),\n                                              buy_token=self.token2.address, buy_amount=Wad.from_number(4),\n                                              expires=100000000)\n\n        # expect\n        assert is_hashable(order1)\n\n    def test_should_have_printable_representation(self):\n        assert repr(self.etherdelta) == f\"EtherDelta('{self.etherdelta.address}')\"\n\n\nclass TestEtherDeltaApi:\n    def setup_method(self):\n        self.etherdelta_api = EtherDeltaApi(client_tool_directory='some-dir',\n                                            client_tool_command='some command',\n                                            api_server='https://127.0.0.1:66666',\n                                            number_of_attempts=1,\n                                            retry_interval=15,\n                                            timeout=90)\n\n    def test_should_have_printable_representation(self):\n        assert repr(self.etherdelta_api) == f\"EtherDeltaApi()\"\n"
  },
  {
    "path": "tests/test_feed.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2017-2018 reverendus\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nimport pytest\nfrom web3 import Web3, HTTPProvider\n\nfrom pymaker import Address\nfrom pymaker.feed import DSValue\n\n\nclass TestDSValue:\n    def setup_method(self):\n        self.web3 = Web3(HTTPProvider(\"http://localhost:8555\"))\n        self.web3.eth.defaultAccount = self.web3.eth.accounts[0]\n        self.dsvalue = DSValue.deploy(self.web3)\n\n    def test_fail_when_no_contract_under_that_address(self):\n        # expect\n        with pytest.raises(Exception):\n            DSValue(web3=self.web3, address=Address('0xdeadadd1e5500000000000000000000000000000'))\n\n    def test_address(self):\n        assert isinstance(self.dsvalue.address, Address)\n\n    def test_no_value_after_deploy(self):\n        # expect\n        assert self.dsvalue.has_value() is False\n        with pytest.raises(Exception):\n            self.dsvalue.read()\n        with pytest.raises(Exception):\n            self.dsvalue.read_as_int()\n        with pytest.raises(Exception):\n            self.dsvalue.read_as_hex()\n\n    def test_poke(self):\n        # when\n        self.dsvalue.poke(bytes([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n                                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n                                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n                                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf4])).transact()\n\n        # then\n        assert self.dsvalue.has_value() is True\n        assert self.dsvalue.read_as_int() == 500\n        assert self.dsvalue.read() == bytes([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n                                             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n                                             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n                                             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf4])\n\n    def test_poke_with_int(self):\n        # when\n        self.dsvalue.poke_with_int(500).transact()\n\n        # then\n        assert self.dsvalue.has_value() is True\n        assert self.dsvalue.read_as_int() == 500\n        assert self.dsvalue.read() == bytes([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n                                             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n                                             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n                                             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf4])\n\n    def test_void(self):\n        # given\n        self.dsvalue.poke_with_int(250).transact()\n        assert self.dsvalue.has_value() is True\n\n        # when\n        self.dsvalue.void().transact()\n\n        # then\n        assert self.dsvalue.has_value() is False\n\n    def test_should_have_printable_representation(self):\n        assert repr(self.dsvalue) == f\"DSValue('{self.dsvalue.address}')\"\n"
  },
  {
    "path": "tests/test_gas.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2017-2018 reverendus\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nimport pytest\nfrom typing import Optional\nfrom web3 import Web3\n\nfrom pymaker.gas import DefaultGasPrice, FixedGasPrice, GasPrice, GeometricGasPrice, IncreasingGasPrice, NodeAwareGasPrice\nfrom tests.conftest import web3\n\n\nclass TestGasPrice:\n    def test_not_implemented(self):\n        with pytest.raises(Exception):\n            GasPrice().get_gas_price(0)\n\n    def test_gwei(self):\n        assert GasPrice.GWEI == 1000000000\n\n\nclass TestDefaultGasPrice:\n    def test_should_always_be_default(self):\n        # given\n        default_gas_price = DefaultGasPrice()\n\n        # expect\n        assert default_gas_price.get_gas_price(0) is None\n        assert default_gas_price.get_gas_price(1) is None\n        assert default_gas_price.get_gas_price(1000000) is None\n\n\nclass TestNodeAwareGasPrice:\n    class DumbSampleImplementation(NodeAwareGasPrice):\n        def get_gas_price(self, time_elapsed: int) -> Optional[int]:\n            return self.get_node_gas_price() * max(time_elapsed, 1)\n\n    class BadImplementation(NodeAwareGasPrice):\n        pass\n\n    def test_retrieve_node_gas_price(self, web3):\n        strategy = TestNodeAwareGasPrice.DumbSampleImplementation(web3)\n        assert strategy.get_gas_price(0) > 0\n        assert strategy.get_gas_price(60) < strategy.get_gas_price(120)\n\n    def test_not_implemented(self, web3):\n        with pytest.raises(NotImplementedError):\n            NodeAwareGasPrice(web3)\n\n        bad = TestNodeAwareGasPrice.BadImplementation(web3)\n        with pytest.raises(NotImplementedError):\n            bad.get_gas_price(0)\n\n\nclass TestFixedGasPrice:\n    def test_gas_price_should_stay_the_same(self):\n        # given\n        value = 9000000000\n        fixed_gas_price = FixedGasPrice(value)\n\n        # expect\n        assert fixed_gas_price.get_gas_price(0) == value\n        assert fixed_gas_price.get_gas_price(1) == value\n        assert fixed_gas_price.get_gas_price(2) == value\n        assert fixed_gas_price.get_gas_price(5) == value\n        assert fixed_gas_price.get_gas_price(60) == value\n        assert fixed_gas_price.get_gas_price(120) == value\n        assert fixed_gas_price.get_gas_price(600) == value\n        assert fixed_gas_price.get_gas_price(1000000) == value\n\n    def test_gas_price_should_be_updated_by_update_gas_price_method(self):\n        # given\n        value1 = 9000000000\n        value2 = 16000000000\n\n        # and\n        fixed_gas_price = FixedGasPrice(value1)\n\n        # and\n        assert fixed_gas_price.get_gas_price(0) == value1\n        assert fixed_gas_price.get_gas_price(1) == value1\n        assert fixed_gas_price.get_gas_price(2) == value1\n        assert fixed_gas_price.get_gas_price(5) == value1\n\n        # when\n        fixed_gas_price.update_gas_price(value2)\n\n        # then\n        assert fixed_gas_price.get_gas_price(60) == value2\n        assert fixed_gas_price.get_gas_price(120) == value2\n        assert fixed_gas_price.get_gas_price(600) == value2\n\n\nclass TestIncreasingGasPrice:\n    def test_gas_price_should_increase_with_time(self):\n        # given\n        increasing_gas_price = IncreasingGasPrice(1000, 100, 60, None)\n\n        # expect\n        assert increasing_gas_price.get_gas_price(0) == 1000\n        assert increasing_gas_price.get_gas_price(1) == 1000\n        assert increasing_gas_price.get_gas_price(59) == 1000\n        assert increasing_gas_price.get_gas_price(60) == 1100\n        assert increasing_gas_price.get_gas_price(119) == 1100\n        assert increasing_gas_price.get_gas_price(120) == 1200\n        assert increasing_gas_price.get_gas_price(1200) == 3000\n\n    def test_gas_price_should_obey_max_value(self):\n        # given\n        increasing_gas_price = IncreasingGasPrice(1000, 100, 60, 2500)\n\n        # expect\n        assert increasing_gas_price.get_gas_price(0) == 1000\n        assert increasing_gas_price.get_gas_price(1) == 1000\n        assert increasing_gas_price.get_gas_price(59) == 1000\n        assert increasing_gas_price.get_gas_price(60) == 1100\n        assert increasing_gas_price.get_gas_price(119) == 1100\n        assert increasing_gas_price.get_gas_price(120) == 1200\n        assert increasing_gas_price.get_gas_price(1200) == 2500\n        assert increasing_gas_price.get_gas_price(3000) == 2500\n        assert increasing_gas_price.get_gas_price(1000000) == 2500\n\n    def test_should_require_positive_initial_price(self):\n        with pytest.raises(Exception):\n            IncreasingGasPrice(0, 1000, 60, None)\n\n        with pytest.raises(Exception):\n            IncreasingGasPrice(-1, 1000, 60, None)\n\n    def test_should_require_positive_increase_by_value(self):\n        with pytest.raises(Exception):\n            IncreasingGasPrice(1000, 0, 60, None)\n\n        with pytest.raises(Exception):\n            IncreasingGasPrice(1000, -1, 60, None)\n\n    def test_should_require_positive_every_secs_value(self):\n        with pytest.raises(Exception):\n            IncreasingGasPrice(1000, 100, 0, None)\n\n        with pytest.raises(Exception):\n            IncreasingGasPrice(1000, 100, -1, None)\n\n    def test_should_require_positive_max_price_if_provided(self):\n        with pytest.raises(Exception):\n            IncreasingGasPrice(1000, 1000, 60, 0)\n\n        with pytest.raises(Exception):\n            IncreasingGasPrice(1000, 1000, 60, -1)\n\n\nclass TestGeometricGasPrice:\n    def test_gas_price_should_increase_with_time(self):\n        # given\n        geometric_gas_price = GeometricGasPrice(100, 10)\n\n        # expect\n        assert geometric_gas_price.get_gas_price(0) == 100\n        assert geometric_gas_price.get_gas_price(1) == 100\n        assert geometric_gas_price.get_gas_price(10) == 113\n        assert geometric_gas_price.get_gas_price(15) == 113\n        assert geometric_gas_price.get_gas_price(20) == 127\n        assert geometric_gas_price.get_gas_price(30) == 143\n        assert geometric_gas_price.get_gas_price(50) == 181\n        assert geometric_gas_price.get_gas_price(100) == 325\n\n    def test_gas_price_should_obey_max_value(self):\n        # given\n        geometric_gas_price = GeometricGasPrice(1000, 60, 1.125, 2500)\n\n        # expect\n        assert geometric_gas_price.get_gas_price(0) == 1000\n        assert geometric_gas_price.get_gas_price(1) == 1000\n        assert geometric_gas_price.get_gas_price(59) == 1000\n        assert geometric_gas_price.get_gas_price(60) == 1125\n        assert geometric_gas_price.get_gas_price(119) == 1125\n        assert geometric_gas_price.get_gas_price(120) == 1266\n        assert geometric_gas_price.get_gas_price(1200) == 2500\n        assert geometric_gas_price.get_gas_price(3000) == 2500\n        assert geometric_gas_price.get_gas_price(1000000) == 2500\n\n    def test_behaves_with_realistic_values(self):\n        # given\n        GWEI = 1000000000\n        geometric_gas_price = GeometricGasPrice(100*GWEI, 10, 1+(0.125*2))\n\n        for seconds in [0,1,10,12,30,60]:\n            print(f\"gas price after {seconds} seconds is {geometric_gas_price.get_gas_price(seconds)/GWEI}\")\n\n        assert round(geometric_gas_price.get_gas_price(0) / GWEI, 1) == 100.0\n        assert round(geometric_gas_price.get_gas_price(1) / GWEI, 1) == 100.0\n        assert round(geometric_gas_price.get_gas_price(10) / GWEI, 1) == 125.0\n        assert round(geometric_gas_price.get_gas_price(12) / GWEI, 1) == 125.0\n        assert round(geometric_gas_price.get_gas_price(30) / GWEI, 1) == 195.3\n        assert round(geometric_gas_price.get_gas_price(60) / GWEI, 1) == 381.5\n\n    def test_should_require_positive_initial_price(self):\n        with pytest.raises(AssertionError):\n            GeometricGasPrice(0, 60)\n\n        with pytest.raises(AssertionError):\n            GeometricGasPrice(-1, 60)\n\n    def test_should_require_positive_every_secs_value(self):\n        with pytest.raises(AssertionError):\n            GeometricGasPrice(1000, 0)\n\n        with pytest.raises(AssertionError):\n            GeometricGasPrice(1000, -1)\n\n    def test_should_require_positive_coefficient(self):\n        with pytest.raises(AssertionError):\n            GeometricGasPrice(1000, 60, 0)\n\n        with pytest.raises(AssertionError):\n            GeometricGasPrice(1000, 60, 1)\n\n        with pytest.raises(AssertionError):\n            GeometricGasPrice(1000, 60, -1)\n\n    def test_should_require_positive_max_price_if_provided(self):\n        with pytest.raises(AssertionError):\n            GeometricGasPrice(1000, 60, 1.125, 0)\n\n        with pytest.raises(AssertionError):\n            GeometricGasPrice(1000, 60, 1.125, -1)\n\n    def test_max_price_should_exceed_initial_price(self):\n        with pytest.raises(AssertionError):\n            GeometricGasPrice(6000, 30, 2.25, 5000)\n"
  },
  {
    "path": "tests/test_general.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2017-2018 reverendus\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nimport pytest\nfrom hexbytes import HexBytes\nfrom web3 import HTTPProvider, Web3\nfrom web3._utils.request import _get_session\n\nfrom pymaker import Address, Calldata, Contract, Receipt, Transfer, web3_via_http\nfrom pymaker.numeric import Wad\nfrom pymaker.util import eth_balance\nfrom tests.helpers import is_hashable\n\ntest_abi = Contract._load_abi(__name__, 'abi/GemMock.abi')\n\n\nclass TestConnect:\n    def test_connect_to_testchain(self, our_address):\n        uri = \"http://0.0.0.0:8545\"\n        web3 = web3_via_http(uri, 63, 39)\n        assert isinstance(web3.provider, HTTPProvider)\n        assert web3.provider._request_kwargs['timeout'] == 63\n\n        for adapter in _get_session(uri).adapters.values():\n            assert adapter._pool_connections == 39\n            assert adapter._pool_maxsize == 39\n\n        assert isinstance(web3, Web3)\n        assert eth_balance(web3, our_address) > Wad(0)\n\n    def test_unsupported_url(self):\n        with pytest.raises(ValueError):\n            web3_via_http(\"wss://0.0.0.0:8545\")\n\n\nclass TestAddress:\n    def test_creation_from_various_representations(self):\n        # expect\n        assert Address('0x0000000000111111111100000000001111111111').address == \\\n               '0x0000000000111111111100000000001111111111'\n        assert Address('0000000000111111111100000000001111111111').address == \\\n               '0x0000000000111111111100000000001111111111'\n\n    def test_creation_from_another_address(self):\n        # given\n        some_address = Address('0x0000000000111111111100000000001111111111')\n\n        # expect\n        assert Address(some_address).address == some_address.address\n\n    def test_should_fail_creation_from_invalid_representation(self):\n        # expect\n        with pytest.raises(Exception):\n            Address('0x000000000011111111110000000000111111111')  # too short\n\n        # expect\n        with pytest.raises(Exception):\n            Address('0x00000000001111111111000000000011111111111')  # too long\n\n    def test_as_bytes(self):\n        # expect\n        assert Address('0x0000011111000001111100000111110000011111').as_bytes() == \\\n               b'\\0\\0\\x01\\x11\\x11\\0\\0\\x01\\x11\\x11\\0\\0\\x01\\x11\\x11\\0\\0\\x01\\x11\\x11'\n\n    def test_string_value(self):\n        # expect\n        assert str(Address('0x0000011111000001111100000111110000011111')) == \\\n               '0x0000011111000001111100000111110000011111'\n\n    def test_repr(self):\n        # expect\n        assert repr(Address('0x0000011111000001111100000111110000011111')) == \\\n               \"Address('0x0000011111000001111100000111110000011111')\"\n\n    def test_should_be_hashable(self):\n        assert is_hashable(Address('0x0000011111000001111100000111110000011111'))\n\n    def test_equality(self):\n        # given\n        address1a = Address('0x0000011111000001111100000111110000011111')\n        address1b = Address('0x0000011111000001111100000111110000011111')\n        address2 = Address('0x0000011111000001111100000111110000022222')\n\n        # expect\n        assert address1a == address1b\n        assert address1a != address2\n        assert address1b != address2\n\n    def test_ordering(self):\n        # given\n        address1 = Address('0x0000011111000001111100000111110000011111')\n        address2 = Address('0x0000011111000001111100000111110000022222')\n        address3 = Address('0x0000011111000001111100000111110000033333')\n\n        # expect\n        assert address1 < address2\n        assert not address1 > address2\n        assert address2 > address1\n        assert not address2 < address1\n        assert address1 <= address2\n        assert address2 >= address1\n        assert address1 < address3\n        assert address1 <= address3\n\n\nclass TestCalldata:\n    def test_creation(self):\n        # expect\n        assert Calldata('0xa9059cbb').value == '0xa9059cbb'\n\n    def test_creation_from_bytes(self):\n        # expect\n        assert Calldata(b'\\xa9\\x05\\x9c\\xbb').value == '0xa9059cbb'\n\n    def test_should_fail_creation_from_invalid_calldata(self):\n        # expect\n        with pytest.raises(Exception):\n            Calldata('a9059cbb')  # without `0x`\n\n    def test_as_bytes(self):\n        # expect\n        assert Calldata('0xa9059cbb').as_bytes() == b'\\xa9\\x05\\x9c\\xbb'\n\n    def test_string_value(self):\n        # expect\n        assert str(Calldata('0xa9059cbb')) == '0xa9059cbb'\n\n    def test_repr(self):\n        # expect\n        assert repr(Calldata('0xa9059cbb')) == \"Calldata('0xa9059cbb')\"\n\n    def test_should_be_hashable(self):\n        assert is_hashable(Calldata('0xa9059cbb'))\n\n    def test_equality(self):\n        # given\n        calldata1a = Calldata('0xa9059cbb')\n        calldata1b = Calldata('0xa9059cbb')\n        calldata2 = Calldata('0xa9059ccc')\n\n        # expect\n        assert calldata1a == calldata1b\n        assert calldata1a != calldata2\n        assert calldata1b != calldata2\n\n    def test_from_signature(self, web3):\n        # given\n        calldata1a = Calldata('0xa9059cbb'  # function 4byte signature\n                              '00000000000000000000000011223344556600000000000000000000000000ff'\n                              '000000000000000000000000000000000000000000000000000000000000007b')\n        calldata1b = Calldata.from_signature(web3,\n                                             'transfer(address,uint256)',\n                                             ['0x11223344556600000000000000000000000000ff', 123])\n\n        # expect\n        assert calldata1a == calldata1b\n\n        # given\n        calldata2a = Calldata('0x2b4e4e96'  # function 4byte signature\n                              '00000000000000000000000011223344556600000000000000000000000000ff'\n                              '0000000000000000000000000000000000000000000000000000000000000040'\n                              '0000000000000000000000000000000000000000000000000000000000000002'\n                              '000000000000000000000000000000000000000000000000000000000000007b'\n                              '00000000000000000000000000000000000000000000000000000000000001c8')\n        calldata2b = Calldata.from_signature(web3,\n                                             'transfer(address,uint256[])',\n                                             ['0x11223344556600000000000000000000000000ff', [123, 456]])\n\n        # expect\n        assert calldata2a == calldata2b\n\n    def test_from_contract_abi(self, web3):\n        # given\n        calldata1a = Calldata('0xa9059cbb'  # function 4byte signature\n                              '00000000000000000000000011223344556600000000000000000000000000ff'\n                              '000000000000000000000000000000000000000000000000000000000000007b')\n\n        calldata1b = Calldata.from_contract_abi(web3,\n                                             'transfer(address,uint256)',\n                                             ['0x11223344556600000000000000000000000000ff', 123],\n                                             test_abi)\n\n        # expect\n        assert calldata1a == calldata1b\n\n\nclass TestReceipt:\n    @pytest.fixture()\n    def receipt_success(self) -> dict:\n        return {'blockHash': '0xef523d31d16592a53826962962bd126d1c66203780a2db59839eee3d3ff7d0b7',\n                'blockNumber': 3890533,\n                'contractAddress': None,\n                'cumulativeGasUsed': 57192,\n                'gasUsed': 57192,\n                'logs': [{'address': '0x53eccc9246c1e537d79199d0c7231e425a40f896',\n                          'blockHash': '0xef523d31d16592a53826962962bd126d1c66203780a2db59839eee3d3ff7d0b7',\n                          'blockNumber': 3890533,\n                          'data': '0x0000000000000000000000000000000000000000000000000de0b6b3a7640000',\n                          'logIndex': 0,\n                          'topics': [HexBytes('0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'),\n                                     HexBytes('0x000000000000000000000000375d52588c3f39ee7710290237a95c691d8432e7'),\n                                     HexBytes('0x0000000000000000000000000046f01ad360270605e0e5d693484ec3bfe43ba8')],\n                          'transactionHash': '0x8b6851e40d017b2004a54eae3e9e47614398b54bbbaae150eaa889ec36470ec8',\n                          'transactionIndex': 0,\n                          'transactionLogIndex': '0x0',\n                          'type': 'mined'},\n                         {'address': '0x375d52588c3f39ee7710290237a95c691d8432e7',\n                          'blockHash': '0xef523d31d16592a53826962962bd126d1c66203780a2db59839eee3d3ff7d0b7',\n                          'blockNumber': 3890533,\n                          'data': '0x00000000000000000000000000000000000000000000000000000000000000a2',\n                          'logIndex': 1,\n                          'topics': [HexBytes('0xa2c251311b1a7a475913900a2a73dc9789a21b04bc737e050bbc506dd4eb3488')],\n                          'transactionHash': '0x8b6851e40d017b2004a54eae3e9e47614398b54bbbaae150eaa889ec36470ec8',\n                          'transactionIndex': 0,\n                          'transactionLogIndex': '0x1',\n                          'type': 'mined'},\n                         {'address': '0x375d52588c3f39ee7710290237a95c691d8432e7',\n                          'blockHash': '0xef523d31d16592a53826962962bd126d1c66203780a2db59839eee3d3ff7d0b7',\n                          'blockNumber': 3890533,\n                          'data': '0x00000000000000000000000053eccc9246c1e537d79199d0c7231e425a40f896000000000000000000000000228bf3d5be3ee4b80718b89b68069b023c32131e0000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000f6d7ac92d746b00000000000000000000000000000000000000000000000000000000000059c17c9c',\n                          'logIndex': 2,\n                          'topics': [HexBytes('0x9577941d28fff863bfbee4694a6a4a56fb09e169619189d2eaa750b5b4819995'),\n                                     HexBytes('0x00000000000000000000000000000000000000000000000000000000000000a2'),\n                                     HexBytes('0x7188d03e276d4dead4b0c037a93892d986e043a3af3305d7488a731ccaff4b76'),\n                                     HexBytes('0x0000000000000000000000000046f01ad360270605e0e5d693484ec3bfe43ba8')],\n                          'transactionHash': '0x8b6851e40d017b2004a54eae3e9e47614398b54bbbaae150eaa889ec36470ec8',\n                          'transactionIndex': 0,\n                          'transactionLogIndex': '0x2',\n                          'type': 'mined'}],\n                'logsBloom': '0x00000000000000000000000000000000000000000000002002000000000080000000000000800010000000000000000000000000000000000000000000000000000000000000000000000008000020000000000000000000000000040040000000000000000000100000000000000000000000000000000000000030000000000400000000000000000000000400000000000000000000000000000000000040001040000000000000000000000001000400000000000000000000002000000000000002000000100800000000080000080000000100000000000000000000002000000000000000000000000000000000000000000000000000000000000000',\n                'root': None,\n                'transactionHash': '0x8b6851e40d017b2004a54eae3e9e47614398b54bbbaae150eaa889ec36470ec8',\n                'transactionIndex': 0}\n\n    @pytest.fixture()\n    def receipt_failed(self) -> dict:\n        return {'blockHash': '0x827e0e913f4388318d5c08eff06e200ed1be1cb8b31aa30f932dcf5595c8d81d',\n                'blockNumber': 3890936,\n                'contractAddress': None,\n                'cumulativeGasUsed': 3171658,\n                'gasUsed': 3100000,\n                'logs': [],\n                'logsBloom': '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',\n                'root': None,\n                'transactionHash': '0x570369e4f70df947e3c4bc08ed9b06c181190a423ee5fcf17db203574e2d5d77',\n                'transactionIndex': 1}\n\n    def test_parsing_receipt(self, receipt_success):\n        # given\n        receipt = Receipt(receipt_success)\n\n        # expect\n        assert receipt.transaction_hash == '0x8b6851e40d017b2004a54eae3e9e47614398b54bbbaae150eaa889ec36470ec8'\n        assert receipt.gas_used == 57192\n        assert len(receipt.transfers) == 1\n        assert len(receipt.logs) == 3\n        assert receipt.transfers[0] == Transfer(token_address=Address('0x53eccc9246c1e537d79199d0c7231e425a40f896'),\n                                                from_address=Address('0x375d52588c3f39ee7710290237a95c691d8432e7'),\n                                                to_address=Address('0x0046f01ad360270605e0e5d693484ec3bfe43ba8'),\n                                                value=Wad.from_number(1))\n\n    def test_should_recognize_successful_and_failed_transactions(self, receipt_success, receipt_failed):\n        # expect\n        assert Receipt(receipt_success).successful is True\n        assert Receipt(receipt_failed).successful is False\n\n\nclass TestTransfer:\n    def test_equality(self):\n        # given\n        transfer1a = Transfer(token_address=Address('0x0000011111222223333344444555556666677777'),\n                              from_address=Address('0x0000000000111111111100000000001111111111'),\n                              to_address=Address('0x1111111111000000000011111111110000000000'),\n                              value=Wad.from_number(20))\n        transfer1b = Transfer(token_address=Address('0x0000011111222223333344444555556666677777'),\n                              from_address=Address('0x0000000000111111111100000000001111111111'),\n                              to_address=Address('0x1111111111000000000011111111110000000000'),\n                              value=Wad.from_number(20))\n        transfer2 = Transfer(token_address=Address('0x0000011111222223333344444555556666677777'),\n                             from_address=Address('0x0000000000111111111100000000001111111111'),\n                             to_address=Address('0x1111111111000000000011111111112222222222'),\n                             value=Wad.from_number(20))\n\n        # expect\n        assert transfer1a == transfer1b\n        assert transfer1b == transfer1a\n        assert transfer1a != transfer2\n        assert transfer1b != transfer2\n        assert transfer2 != transfer1a\n        assert transfer2 != transfer1b\n"
  },
  {
    "path": "tests/test_general2.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2017-2018 reverendus\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nimport asyncio\nimport pytest\nfrom mock import MagicMock\nfrom web3 import Web3, HTTPProvider\n\nfrom pymaker import Address, eth_transfer, get_pending_transactions, RecoveredTransact, TransactStatus, Calldata, Receipt\nfrom pymaker.gas import FixedGasPrice\nfrom pymaker.numeric import Wad\nfrom pymaker.proxy import DSProxy, DSProxyCache\nfrom pymaker.token import DSToken\nfrom pymaker.util import synchronize, eth_balance\n\n\nclass TestTransact:\n    def setup_method(self):\n        self.web3 = Web3(HTTPProvider(\"http://localhost:8555\"))\n        self.web3.eth.defaultAccount = self.web3.eth.accounts[0]\n        self.our_address = Address(self.web3.eth.defaultAccount)\n        self.second_address = Address(self.web3.eth.accounts[1])\n        self.third_address = Address(self.web3.eth.accounts[2])\n        self.token = DSToken.deploy(self.web3, 'ABC')\n        self.token.mint(Wad(1000000)).transact()\n\n    def test_can_only_execute_once(self):\n        # given\n        transact = self.token.transfer(self.second_address, Wad(500))\n        # and\n        transact.transact()\n\n        # expect\n        with pytest.raises(Exception):\n            transact.transact()\n\n    def test_can_only_execute_once_even_if_tx_failed(self):\n        # given\n        transact = self.token.transfer(self.second_address, Wad(2000000))  # more than we minted\n        # and\n        try:\n            transact.transact()\n        # CAUTION: Note ganache 6.5+ causes a ValueError while older versions fail without exception\n        except ValueError:\n            pass\n\n        # expect\n        with pytest.raises(Exception):\n            transact.transact()\n\n    def test_should_update_status_when_finished(self):\n        # given\n        transact = self.token.transfer(self.second_address, Wad(500))\n        assert transact.status == TransactStatus.NEW\n\n        # when\n        transact.transact()\n        # then\n        assert transact.status == TransactStatus.FINISHED\n\n    def test_should_update_status_to_finished_even_if_tx_failed(self):\n        # given\n        transact = self.token.transfer(self.second_address, Wad(2000000))  # more than we minted\n        assert transact.status == TransactStatus.NEW\n\n        # when\n        try:\n            transact.transact()\n        except ValueError:\n            pass\n        # then\n        assert transact.status == TransactStatus.FINISHED\n\n    def test_default_gas(self):\n        # when\n        receipt = self.token.transfer(self.second_address, Wad(500)).transact()\n\n        # then\n        assert 100000 <= self.web3.eth.getTransaction(receipt.transaction_hash)['gas'] <= 1200000\n\n    def test_default_gas_async(self):\n        # when\n        receipt = synchronize([self.token.transfer(self.second_address, Wad(500)).transact_async()])[0]\n\n        # then\n        assert 100000 <= self.web3.eth.getTransaction(receipt.transaction_hash)['gas'] <= 1200000\n\n    def test_custom_gas(self):\n        # when\n        receipt = self.token.transfer(self.second_address, Wad(500)).transact(gas=129995)\n\n        # then\n        assert self.web3.eth.getTransaction(receipt.transaction_hash)['gas'] == 129995\n\n    def test_custom_gas_async(self):\n        # when\n        receipt = synchronize([self.token.transfer(self.second_address, Wad(500)).transact_async(gas=129995)])[0]\n\n        # then\n        assert self.web3.eth.getTransaction(receipt.transaction_hash)['gas'] == 129995\n\n    def test_custom_gas_buffer(self):\n        # when\n        receipt = self.token.transfer(self.second_address, Wad(500)).transact(gas_buffer=2500000)\n\n        # then\n        assert self.web3.eth.getTransaction(receipt.transaction_hash)['gas'] > 2500000\n\n    def test_gas_and_gas_buffer_not_allowed_at_the_same_time(self):\n        # expect\n        with pytest.raises(Exception):\n            self.token.transfer(self.second_address, Wad(500)).transact(gas=129995, gas_buffer=3000000)\n\n    def test_gas_and_gas_buffer_not_allowed_at_the_same_time_async(self):\n        # expect\n        with pytest.raises(Exception):\n            synchronize([self.token.transfer(self.second_address, Wad(500)).transact_async(gas=129995,\n                                                                                           gas_buffer=3000000)])\n\n    def test_custom_gas_price(self):\n        # given\n        gas_price = FixedGasPrice(25000000100)\n\n        # when\n        self.token.transfer(self.second_address, Wad(500)).transact(gas_price=gas_price)\n\n        # then\n        assert self.web3.eth.getBlock('latest', full_transactions=True).transactions[0].gasPrice == gas_price.gas_price\n\n    def test_custom_gas_price_async(self):\n        # given\n        gas_price = FixedGasPrice(25000000200)\n\n        # when\n        synchronize([self.token.transfer(self.second_address, Wad(500)).transact_async(gas_price=gas_price)])\n\n        # then\n        assert self.web3.eth.getBlock('latest', full_transactions=True).transactions[0].gasPrice == gas_price.gas_price\n\n    def test_custom_from_address(self):\n        # given\n        self.token.transfer(self.second_address, Wad(self.token.balance_of(self.our_address))).transact()\n\n        # when\n        receipt = self.token.transfer(self.our_address, Wad(250)).transact(from_address=self.second_address)\n\n        # then\n        assert Address(self.web3.eth.getTransaction(receipt.transaction_hash)['from']) == self.second_address\n\n    def test_name_formatting(self):\n        # given\n        transact = self.token.transfer(self.second_address, Wad(123))\n\n        # expect\n        assert transact.name() == f\"DSToken('{self.token.address}').transfer('{self.second_address}', 123)\"\n\n    def test_name_formatting_with_hexstrings(self):\n        # given\n        proxy_cache = DSProxyCache.deploy(self.web3)\n        proxy = DSProxy.deploy(self.web3, proxy_cache.address)\n\n        # when\n        transact = proxy.execute(\"0x11223344\", Calldata(\"0x55667788\"))\n\n        # then\n        assert transact.name() == f\"DSProxy('{proxy.address}').execute(bytes,bytes)('0x11223344', '0x55667788')\"\n\n    def test_eth_transfer(self):\n        # given\n        initial_balance = eth_balance(self.web3, self.second_address)\n\n        # when\n        eth_transfer(self.web3, self.second_address, Wad.from_number(1.5)).transact()\n\n        # then\n        assert eth_balance(self.web3, self.second_address) == initial_balance + Wad.from_number(1.5)\n\n    def test_eth_transfer_from_other_account(self):\n        # given\n        initial_balance_second_address = eth_balance(self.web3, self.second_address)\n        initial_balance_third_address = eth_balance(self.web3, self.third_address)\n\n        # when\n        eth_transfer(self.web3, self.third_address, Wad.from_number(1.5)).transact(from_address=self.second_address)\n\n        # then\n        assert eth_balance(self.web3, self.second_address) < initial_balance_second_address\n        assert eth_balance(self.web3, self.third_address) == initial_balance_third_address + Wad.from_number(1.5)\n\n    def test_should_raise_exception_on_unknown_kwarg(self):\n        # expect\n        with pytest.raises(Exception):\n            self.token.transfer(self.second_address, Wad(123)).transact(unknown_kwarg=\"some_value\")\n\n        # expect\n        with pytest.raises(Exception):\n            synchronize([self.token.transfer(self.second_address, Wad(123)).transact_async(unknown_kwarg=\"some_value\")])\n\n\nclass TestTransactReplace:\n    def setup_method(self):\n        self.web3 = Web3(HTTPProvider(\"http://localhost:8555\"))\n        self.web3.eth.defaultAccount = self.web3.eth.accounts[0]\n        self.our_address = Address(self.web3.eth.defaultAccount)\n        self.second_address = Address(self.web3.eth.accounts[1])\n        self.third_address = Address(self.web3.eth.accounts[2])\n        self.token = DSToken.deploy(self.web3, 'ABC')\n        self.token.mint(Wad(1000000)).transact()\n\n    @pytest.mark.asyncio\n    async def test_transaction_replace(self):\n        # given\n        original_send_transaction = self.web3.eth.sendTransaction\n        original_get_transaction = self.web3.eth.getTransaction\n        nonce = self.web3.eth.getTransactionCount(self.our_address.address)\n\n        # when\n        self.web3.eth.sendTransaction = MagicMock(return_value='0xaaaaaaaaaabbbbbbbbbbccccccccccdddddddddd')\n        self.web3.eth.getTransaction = MagicMock(return_value={'nonce': nonce})\n        # and\n        transact_1 = self.token.transfer(self.second_address, Wad(500))\n        future_receipt_1 = asyncio.ensure_future(transact_1.transact_async(gas_price=FixedGasPrice(100000)))\n        # and\n        await asyncio.sleep(2)\n        # then\n        assert future_receipt_1.done() is False\n        assert self.token.balance_of(self.second_address) == Wad(0)\n\n        # when\n        self.web3.eth.sendTransaction = original_send_transaction\n        self.web3.eth.getTransaction = original_get_transaction\n        # and\n        transact_2 = self.token.transfer(self.third_address, Wad(700))\n        future_receipt_2 = asyncio.ensure_future(transact_2.transact_async(replace=transact_1,\n                                                                           gas_price=FixedGasPrice(150000)))\n        # and\n        await asyncio.sleep(10)\n        # then\n        assert transact_1.status == TransactStatus.FINISHED\n        assert future_receipt_1.done()\n        assert future_receipt_1.result() is None\n        # and\n        assert transact_2.status == TransactStatus.FINISHED\n        assert future_receipt_2.done()\n        assert isinstance(future_receipt_2.result(), Receipt)\n        assert future_receipt_2.result().successful is True\n        # and\n        assert self.token.balance_of(self.second_address) == Wad(0)\n        assert self.token.balance_of(self.third_address) == Wad(700)\n\n    @pytest.mark.timeout(10)\n    def test_transaction_replace_of_failed_transaction(self):\n        # given\n        original_send_transaction = self.web3.eth.sendTransaction\n\n        # when\n        transact_1 = self.token.transfer(self.second_address, Wad(2000000))  # more than we minted\n        receipt_1 = None\n        try:\n            receipt_1 = transact_1.transact()\n        except ValueError:\n            pass\n        # then\n        assert transact_1.status == TransactStatus.FINISHED\n        assert receipt_1 is None\n\n        # when\n        def second_send_transaction(transaction):\n            # TestRPC doesn't support `sendTransaction` calls with the `nonce` parameter\n            # (unlike proper Ethereum nodes which handle it very well)\n            transaction_without_nonce = {key: transaction[key] for key in transaction if key != 'nonce'}\n            return original_send_transaction(transaction_without_nonce)\n\n        self.web3.eth.sendTransaction = MagicMock(side_effect=second_send_transaction)\n        # when\n        transact_2 = self.token.transfer(self.second_address, Wad(500))\n        receipt_2 = transact_2.transact(replace=transact_1)\n        # then\n        assert transact_2.status == TransactStatus.FINISHED\n        assert receipt_2 is not None\n        assert receipt_2.successful\n        # and\n        assert self.token.balance_of(self.second_address) == Wad(500)\n\n\nclass TestTransactRecover:\n    def setup_method(self):\n        self.web3 = Web3(HTTPProvider(\"http://localhost:8555\"))\n        self.web3.eth.defaultAccount = self.web3.eth.accounts[0]\n        self.token = DSToken.deploy(self.web3, 'ABC')\n        assert self.token.mint(Wad(100)).transact()\n\n    def test_nothing_pending(self):\n        # given no pending transactions created by prior tests\n\n        # then\n        assert get_pending_transactions(self.web3) == []\n\n    @pytest.mark.skip(\"Ganache and Parity testchains don't seem to simulate pending transactions in the mempool\")\n    @pytest.mark.asyncio\n    async def test_recover_pending_tx(self, other_address):\n        # given\n        low_gas = FixedGasPrice(1)\n        await self.token.transfer(other_address, Wad(5)).transact_async(gas_price=low_gas)\n        await asyncio.sleep(0.5)\n\n        # when\n        pending = get_pending_transactions(self.web3)\n\n        # and\n        assert len(pending) == 1\n        recovered: RecoveredTransact = pending[0]\n        high_gas = FixedGasPrice(int(1 * FixedGasPrice.GWEI))\n        recovered.cancel(high_gas)\n\n        # then\n        assert get_pending_transactions(self.web3) == []\n"
  },
  {
    "path": "tests/test_governance.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2019 EdNoepel\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nimport pytest\nfrom web3 import Web3, HTTPProvider\n\nfrom pymaker import Address\nfrom pymaker.auth import DSAuth\nfrom pymaker.governance import DSPause, DSChief\nfrom pymaker.numeric import Wad\nfrom pymaker.deployment import DssDeployment\nfrom datetime import datetime, timedelta\n\nfrom tests.test_dss import mint_mkr\n\ndef mint_approve_lock(mcd: DssDeployment, amount: Wad, address: Address):\n    prevBalance = mcd.mkr.balance_of(address)\n    mint_mkr(mcd.mkr, address, amount)\n    assert mcd.mkr.balance_of(address) == amount + prevBalance\n\n    # Lock MKR in DS-Chief\n    assert mcd.mkr.approve(mcd.ds_chief.address).transact(from_address=address)\n    assert mcd.ds_chief.lock(amount).transact(from_address=address)\n    assert mcd.mkr.balance_of(address) == prevBalance\n\ndef approve_iou_free_mkr(mcd: DssDeployment, amount: Wad, address: Address):\n    prevBalance = mcd.mkr.balance_of(address)\n    iou = mcd.ds_chief.iou()\n    assert iou.approve(mcd.ds_chief.address).transact(from_address=address)\n    assert mcd.ds_chief.free(amount).transact(from_address=address)\n    assert mcd.mkr.balance_of(address) == amount + prevBalance\n\n# Relevant to DS-Chief 1.2 \ndef launch_chief(mcd: DssDeployment, address: Address):\n    launchAmount = Wad.from_number(80000)\n    mint_approve_lock(mcd, launchAmount, address)\n\n    # Vote on address(0) to activate DSChief.launch()\n    zero_address = Address(\"0x0000000000000000000000000000000000000000\")\n    assert mcd.ds_chief.vote_yays([zero_address.address]).transact(from_address=address)\n\n    # Launch Ds-Chief (1.2)\n    assert mcd.ds_chief.launch().transact(from_address=address)\n    approve_iou_free_mkr(mcd, launchAmount, address)\n\n\n@pytest.mark.skip(reason=\"not fully implemented\")\nclass TestDSPause:\n    def setup_method(self):\n        self.web3 = Web3(HTTPProvider(\"http://localhost:8555\"))\n        self.web3.eth.defaultAccount = self.web3.eth.accounts[0]\n        self.our_address = Address(self.web3.eth.defaultAccount)\n\n        ds_auth = DSAuth.deploy(self.web3)\n        self.ds_pause = DSPause.deploy(self.web3, 5, self.our_address, ds_auth)\n\n        self.plan = DSPause.Plan(usr=self.our_address,\n                                 fax=self.web3.toBytes(text='abi.encodeWithSignature(\"sig()\")'),\n                                 eta=(datetime.utcnow() + timedelta(seconds=10)))\n\n    def test_drop(self):\n        # assert self.ds_pause.plot(self.plan).transact()\n        assert self.ds_pause.drop(self.plan).transact()\n\n    def test_exec(self):\n        # assert self.ds_pause.plot(self.plan).transact()\n        assert self.ds_pause.exec(self.plan).transact()\n\nclass TestDSChief:\n\n    def test_launch(self, mcd: DssDeployment, our_address: Address):\n        assert mcd.ds_chief.live() == False\n        launch_chief(mcd, our_address)\n        assert mcd.ds_chief.live() == True\n\n    def test_scenario(self, mcd: DssDeployment, our_address: Address, other_address: Address):\n        isinstance(mcd, DssDeployment)\n        isinstance(our_address, Address)\n\n        amount = Wad.from_number(1000)\n        mint_approve_lock(mcd, amount, our_address)\n\n        # Vote for our address\n        assert mcd.ds_chief.vote_yays([our_address.address]).transact(from_address=our_address)\n        assert mcd.ds_chief.etch([other_address.address]).transact(from_address=our_address)\n\n        # Confirm that etch(our address) != etch(other address)\n        etches = mcd.ds_chief.past_etch(3)\n        assert etches[0].slate !=  etches[-1].slate\n\n        assert mcd.ds_chief.get_approvals(our_address.address) == amount\n\n        # Lift hat for our address\n        assert mcd.ds_chief.get_hat() != our_address\n        assert mcd.ds_chief.lift(our_address).transact(from_address=our_address)\n        assert mcd.ds_chief.get_hat() == our_address\n\n        # Now vote for other address\n        assert mcd.ds_chief.vote_etch(etches[-1]).transact(from_address=our_address)\n        assert mcd.ds_chief.lift(other_address).transact(from_address=our_address)\n        assert mcd.ds_chief.get_hat() == other_address\n\n        approve_iou_free_mkr(mcd, amount, our_address)\n\n"
  },
  {
    "path": "tests/test_keys.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2017-2018 reverendus\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nimport pkg_resources\nfrom web3 import Web3, HTTPProvider\n\nfrom pymaker import Address, Wad, eth_transfer\nfrom pymaker.keys import register_key_file, register_key\nfrom pymaker.token import DSToken\n\ndef test_local_accounts():\n    # given\n    # [that address is not recognized by ganache, this way we can be sure it's the local account being used for signing]\n    web3 = Web3(HTTPProvider(\"http://localhost:8555\"))\n    web3.eth.defaultAccount = Address('0x13314e21cd6d343ceb857073f3f6d9368919d1ef').address\n\n    # and\n    keyfile_path = pkg_resources.resource_filename(__name__, \"accounts/4_0x13314e21cd6d343ceb857073f3f6d9368919d1ef.json\")\n    passfile_path = pkg_resources.resource_filename(__name__, \"accounts/pass\")\n    register_key_file(web3, keyfile_path, passfile_path)\n\n    # and\n    # [as ganache does not know this address, we need to send some ETH to it first]\n    eth_transfer(web3, Address(web3.eth.defaultAccount), Wad.from_number(100)) \\\n        .transact(from_address=Address(web3.eth.accounts[0]))\n\n    # when\n    # [we deploy some test contract and mint some tokens]\n    token = DSToken.deploy(web3, 'XYZ')\n    token.mint(Wad.from_number(150000)).transact()\n\n    # then\n    # [these operations were successful]\n    assert token.balance_of(Address(web3.eth.defaultAccount)) == Wad.from_number(150000)\n\ndef test_local_accounts_register_key():\n    # given\n    # [that address is not recognized by ganache, this way we can be sure it's the local account being used for signing]\n    web3 = Web3(HTTPProvider(\"http://localhost:8555\"))\n    web3.eth.defaultAccount = Address('0x13314e21cd6d343ceb857073f3f6d9368919d1ef').address\n\n    # and\n    keyfile_path = pkg_resources.resource_filename(__name__, \"accounts/4_0x13314e21cd6d343ceb857073f3f6d9368919d1ef.json\")\n    passfile_path = pkg_resources.resource_filename(__name__, \"accounts/pass\")\n    register_key(web3, f\"key_file={keyfile_path},pass_file={passfile_path}\")\n\n    # and\n    # [as ganache does not know this address, we need to send some ETH to it first]\n    eth_transfer(web3, Address(web3.eth.defaultAccount), Wad.from_number(100)) \\\n        .transact(from_address=Address(web3.eth.accounts[0]))\n\n    # when\n    # [we deploy some test contract and mint some tokens]\n    token = DSToken.deploy(web3, 'XYZ')\n    token.mint(Wad.from_number(150000)).transact()\n\n    # then\n    # [these operations were successful]\n    assert token.balance_of(Address(web3.eth.defaultAccount)) == Wad.from_number(150000)\n\ndef test_multiple_local_accounts():\n    # given\n    local_account_1 = Address('0x13314e21cd6d343ceb857073f3f6d9368919d1ef')\n    local_account_2 = Address('0x176087fea5c41fc370fabbd850521bc4451690ca')\n\n    # and\n    # [that address is not recognized by ganache, this way we can be sure it's the local account being used for signing]\n    web3 = Web3(HTTPProvider(\"http://localhost:8555\"))\n    web3.eth.defaultAccount = local_account_1.address\n\n    # and\n    keyfile_path = pkg_resources.resource_filename(__name__, \"accounts/4_0x13314e21cd6d343ceb857073f3f6d9368919d1ef.json\")\n    passfile_path = pkg_resources.resource_filename(__name__, \"accounts/pass\")\n    register_key_file(web3, keyfile_path, passfile_path)\n\n    # and\n    keyfile_path = pkg_resources.resource_filename(__name__, \"accounts/5_0x176087fea5c41fc370fabbd850521bc4451690ca.json\")\n    passfile_path = pkg_resources.resource_filename(__name__, \"accounts/pass\")\n    register_key_file(web3, keyfile_path, passfile_path)\n\n    # and\n    # [as ganache does not know these addresses, we need to send some ETH to it first]\n    eth_transfer(web3, local_account_1, Wad.from_number(100)).transact(from_address=Address(web3.eth.accounts[0]))\n    eth_transfer(web3, local_account_2, Wad.from_number(100)).transact(from_address=Address(web3.eth.accounts[0]))\n\n    # when\n    # [we execute some test scenario involving two addresses]\n    token = DSToken.deploy(web3, 'XYZ')\n    token.mint(Wad.from_number(150000)).transact()\n    token.transfer(local_account_2, Wad.from_number(60000)).transact()\n    token.transfer(local_account_1, Wad.from_number(10000)).transact(from_address=local_account_2)\n\n    # then\n    # [these operations were successful]\n    assert token.balance_of(local_account_1) == Wad.from_number(100000)\n    assert token.balance_of(local_account_2) == Wad.from_number(50000)\n"
  },
  {
    "path": "tests/test_lifecycle.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2017-2018 reverendus\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nimport time\nfrom threading import Event\nfrom unittest.mock import Mock\n\nimport pytest\nfrom mock import MagicMock\nfrom web3 import Web3, HTTPProvider\n\nimport pymaker\nfrom pymaker import Address\nfrom pymaker.lifecycle import Lifecycle, trigger_event\n\n\n@pytest.mark.timeout(60)\nclass TestLifecycle:\n    def setup_method(self):\n        self.web3 = Web3(HTTPProvider(\"http://localhost:8555\"))\n        self.web3.eth.defaultAccount = self.web3.eth.accounts[0]\n        self.our_address = Address(self.web3.eth.defaultAccount)\n\n        # `test_etherdelta.py` executes before this test file and creates some event filters,\n        # so we need to clear the list of filter threads as otherwise `Web3Lifecycle` will\n        # be waiting forever for them to terminate and the test harness will never finish\n        pymaker.filter_threads = []\n\n    def use_web3(self, with_web3: bool):\n        return self.web3 if with_web3 else None\n\n    @pytest.mark.parametrize('with_web3', [False, True])\n    def test_should_always_exit(self, with_web3):\n        with pytest.raises(SystemExit):\n            with Lifecycle(self.use_web3(with_web3)):\n                pass\n\n    @pytest.mark.parametrize('with_web3', [False, True])\n    def test_should_start_instantly_if_no_initial_delay(self, with_web3):\n        # given\n        start_time = int(time.time())\n\n        # when\n        with pytest.raises(SystemExit):\n            with Lifecycle(self.use_web3(with_web3)) as lifecycle:\n                pass\n\n        # then\n        end_time = int(time.time())\n        assert end_time - start_time <= 2\n\n    @pytest.mark.parametrize('with_web3', [False, True])\n    def test_should_obey_initial_delay(self, with_web3):\n        # given\n        start_time = int(time.time())\n\n        # when\n        with pytest.raises(SystemExit):\n            with Lifecycle(self.use_web3(with_web3)) as lifecycle:\n                lifecycle.initial_delay(5)\n\n        # then\n        end_time = int(time.time())\n        assert end_time - start_time >= 4\n\n    @pytest.mark.parametrize('with_web3', [False, True])\n    def test_should_check_initial_checks(self, with_web3):\n        # given\n        check_1 = Mock(return_value=True)\n        check_2 = Mock(return_value=True)\n\n        # when\n        with pytest.raises(SystemExit):\n            with Lifecycle(self.use_web3(with_web3)) as lifecycle:\n                lifecycle.wait_for(check_1, 5)\n                lifecycle.wait_for(check_2, 5)\n\n        # then\n        assert check_1.call_count == 1\n        assert check_2.call_count == 1\n\n    @pytest.mark.parametrize('with_web3', [False, True])\n    def test_should_time_out_initial_checks_even_if_they_constantly_return_false(self, with_web3):\n        # given\n        start_time = int(time.time())\n\n        # when\n        with pytest.raises(SystemExit):\n            with Lifecycle(self.use_web3(with_web3)) as lifecycle:\n                lifecycle.wait_for(lambda: False, 5)\n\n        # then\n        end_time = int(time.time())\n        assert end_time - start_time >= 4\n\n    @pytest.mark.parametrize('with_web3', [False, True])\n    def test_should_call_startup_callback(self, with_web3):\n        # given\n        startup_mock = MagicMock()\n\n        # when\n        with pytest.raises(SystemExit):\n            with Lifecycle(self.use_web3(with_web3)) as lifecycle:\n                lifecycle.on_startup(startup_mock)\n\n        # then\n        startup_mock.assert_called()\n\n    @pytest.mark.parametrize('with_web3', [False, True])\n    def test_should_fail_to_register_two_startup_callbacks(self, with_web3):\n        # expect\n        with pytest.raises(BaseException):\n            with Lifecycle(self.use_web3(with_web3)) as lifecycle:\n                lifecycle.on_startup(lambda: 1)\n                lifecycle.on_startup(lambda: 2)\n\n    @pytest.mark.parametrize('with_web3', [False, True])\n    def test_should_call_shutdown_callback(self, with_web3):\n        # given\n        ordering = []\n        startup_mock = MagicMock(side_effect=lambda: ordering.append('STARTUP'))\n        shutdown_mock = MagicMock(side_effect=lambda: ordering.append('SHUTDOWN'))\n\n        # when\n        with pytest.raises(SystemExit):\n            with Lifecycle(self.use_web3(with_web3)) as lifecycle:\n                lifecycle.on_startup(startup_mock)\n                lifecycle.on_shutdown(shutdown_mock)\n\n        # then\n        assert ordering == ['STARTUP', 'SHUTDOWN']\n\n    @pytest.mark.parametrize('with_web3', [False, True])\n    def test_should_fail_to_register_two_shutdown_callbacks(self, with_web3):\n        # expect\n        with pytest.raises(BaseException):\n            with Lifecycle(self.use_web3(with_web3)) as lifecycle:\n                lifecycle.on_shutdown(lambda: 1)\n                lifecycle.on_shutdown(lambda: 2)\n\n    def test_should_fail_to_register_two_block_callbacks(self):\n        # expect\n        with pytest.raises(BaseException):\n            with Lifecycle(self.web3) as lifecycle:\n                lifecycle.on_block(lambda: 1)\n                lifecycle.on_block(lambda: 2)\n\n    def test_should_fail_to_register_block_callback_if_no_web3(self):\n        # expect\n        with pytest.raises(BaseException):\n            with Lifecycle() as lifecycle:\n                lifecycle.on_block(lambda: 1)\n\n    @pytest.mark.parametrize('with_web3', [False, True])\n    def test_every(self, with_web3):\n        self.counter = 0\n\n        def callback():\n            self.counter = self.counter + 1\n            if self.counter >= 2:\n                lifecycle.terminate(\"Unit test is over\")\n\n        # given\n        mock = MagicMock(side_effect=callback)\n\n        # when\n        with pytest.raises(SystemExit):\n            with Lifecycle(self.use_web3(with_web3)) as lifecycle:\n                lifecycle.every(1, mock)\n\n        # then\n        assert mock.call_count >= 2\n        assert lifecycle.terminated_internally\n\n    @pytest.mark.parametrize('with_web3', [False, True])\n    def test_on_event_fires_whenever_event_triggered(self, with_web3):\n        event = Event()\n        self.counter = 0\n\n        def every_callback():\n            self.counter = self.counter + 1\n            trigger_event(event)\n            if self.counter >= 2:\n                time.sleep(1)\n                lifecycle.terminate(\"Unit test is over\")\n\n        # given\n        mock = Mock()\n\n        # when\n        with pytest.raises(SystemExit):\n            with Lifecycle(self.use_web3(with_web3)) as lifecycle:\n                lifecycle.every(1, every_callback)\n                lifecycle.on_event(event, 9999, mock)\n\n        # then\n        assert mock.call_count >= 2\n        assert lifecycle.terminated_internally\n\n    @pytest.mark.parametrize('with_web3', [False, True])\n    def test_on_event_fires_every_min_frequency_if_event_not_triggered(self, with_web3):\n        self.counter = 0\n\n        def callback():\n            self.counter = self.counter + 1\n            if self.counter >= 2:\n                lifecycle.terminate(\"Unit test is over\")\n\n        # given\n        mock = MagicMock(side_effect=callback)\n\n        # when\n        with pytest.raises(SystemExit):\n            with Lifecycle(self.use_web3(with_web3)) as lifecycle:\n                lifecycle.on_event(Event(), 1, mock)\n\n        # then\n        assert mock.call_count >= 2\n        assert lifecycle.terminated_internally\n\n    @pytest.mark.parametrize('with_web3', [False, True])\n    def test_every_does_not_start_operating_until_startup_callback_is_finished(self, with_web3):\n        # given\n        self.every_triggered = False\n\n        def startup_callback():\n            time.sleep(3)\n            assert not self.every_triggered\n\n        def every_callback():\n            self.every_triggered = True\n            lifecycle.terminate(\"Unit test is over\")\n\n        # when\n        with pytest.raises(SystemExit):\n            with Lifecycle(self.use_web3(with_web3)) as lifecycle:\n                lifecycle.on_startup(startup_callback)\n                lifecycle.every(1, every_callback)\n\n        # then\n        assert self.every_triggered\n\n    @pytest.mark.parametrize('with_web3', [False, True])\n    def test_event_does_not_start_operating_until_startup_callback_is_finished(self, with_web3):\n        # given\n        self.event_triggered = False\n\n        def startup_callback():\n            time.sleep(3)\n            assert not self.event_triggered\n\n        def event_callback():\n            self.event_triggered = True\n            lifecycle.terminate(\"Unit test is over\")\n\n        # when\n        with pytest.raises(SystemExit):\n            with Lifecycle(self.use_web3(with_web3)) as lifecycle:\n                lifecycle.on_startup(startup_callback)\n                lifecycle.on_event(Event(), 1, event_callback)\n\n        # then\n        assert self.event_triggered\n\n    @pytest.mark.parametrize('with_web3', [False, True])\n    def test_every_should_not_fire_when_keeper_is_already_terminating(self, with_web3):\n        # given\n        self.every_counter = 0\n\n        def shutdown_callback():\n            time.sleep(5)\n\n        def every_callback():\n            self.every_counter = self.every_counter + 1\n            lifecycle.terminate(\"Unit test is over\")\n\n        # when\n        with pytest.raises(SystemExit):\n            with Lifecycle(self.use_web3(with_web3)) as lifecycle:\n                lifecycle.every(1, every_callback)\n                lifecycle.on_shutdown(shutdown_callback)\n\n        # then\n        assert self.every_counter <= 2\n\n    @pytest.mark.parametrize('with_web3', [False, True])\n    def test_events_should_not_fire_when_keeper_is_already_terminating(self, with_web3):\n        # given\n        self.event_counter = 0\n\n        def shutdown_callback():\n            time.sleep(5)\n\n        def event_callback():\n            self.event_counter = self.event_counter + 1\n            lifecycle.terminate(\"Unit test is over\")\n\n        # when\n        with pytest.raises(SystemExit):\n            with Lifecycle(self.use_web3(with_web3)) as lifecycle:\n                lifecycle.on_event(Event(), 1, event_callback)\n                lifecycle.on_shutdown(shutdown_callback)\n\n        # then\n        assert self.event_counter <= 2\n\n    @pytest.mark.parametrize('with_web3', [False, True])\n    def test_should_not_call_shutdown_until_every_timer_has_finished(self, with_web3):\n        # given\n        self.every1_finished = False\n        self.every2_finished = False\n\n        def shutdown_callback():\n            assert self.every1_finished\n            assert self.every2_finished\n\n        def every_callback_1():\n            time.sleep(1)\n            lifecycle.terminate(\"Unit test is over\")\n            time.sleep(4)\n            self.every1_finished = True\n\n        def every_callback_2():\n            time.sleep(2)\n            self.every2_finished = True\n\n        # expect\n        with pytest.raises(SystemExit):\n            with Lifecycle(self.use_web3(with_web3)) as lifecycle:\n                lifecycle.every(1, every_callback_1)\n                lifecycle.every(1, every_callback_2)\n                lifecycle.on_shutdown(shutdown_callback)  # assertions are in `shutdown_callback`\n\n    @pytest.mark.parametrize('with_web3', [False, True])\n    def test_should_not_call_shutdown_until_every_event_has_finished(self, with_web3):\n        # given\n        self.event1_finished = False\n        self.event2_finished = False\n\n        def shutdown_callback():\n            assert self.event1_finished\n            assert self.event2_finished\n\n        def event_callback_1():\n            time.sleep(1)\n            lifecycle.terminate(\"Unit test is over\")\n            time.sleep(4)\n            self.event1_finished = True\n\n        def event_callback_2():\n            time.sleep(2)\n            self.event2_finished = True\n\n        # expect\n        with pytest.raises(SystemExit):\n            with Lifecycle(self.use_web3(with_web3)) as lifecycle:\n                lifecycle.on_event(Event(), 1, event_callback_1)\n                lifecycle.on_event(Event(), 1, event_callback_2)\n                lifecycle.on_shutdown(shutdown_callback)  # assertions are in `shutdown_callback`\n"
  },
  {
    "path": "tests/test_model.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2020 EdNoepel\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nfrom pymaker import Address, Wad\nfrom pymaker.model import Token\n\n\nclass TestToken:\n    def setup_class(self):\n        self.token = Token(\"COW\", Address('0xbeef00000000000000000000000000000000BEEF'), 4)\n\n    def test_convert(self):\n        # two\n        chain_amount = Wad(20000)\n        assert self.token.normalize_amount(chain_amount) == Wad.from_number(2)\n\n        # three\n        normalized_amount = Wad.from_number(3)\n        assert self.token.unnormalize_amount(normalized_amount) == Wad(30000)\n\n    def test_min_amount(self):\n        assert self.token.min_amount == Wad.from_number(0.0001)\n        assert float(self.token.min_amount) == 0.0001\n        assert self.token.unnormalize_amount(self.token.min_amount) == Wad(1)\n\n        assert Wad.from_number(0.0004) > self.token.min_amount\n        assert Wad.from_number(0.00005) < self.token.min_amount\n\n        assert self.token.unnormalize_amount(Wad.from_number(0.0006)) > self.token.unnormalize_amount(self.token.min_amount)\n        assert self.token.unnormalize_amount(Wad.from_number(0.00007)) < self.token.unnormalize_amount(self.token.min_amount)\n        assert self.token.unnormalize_amount(Wad.from_number(0.00008)) == Wad(0)\n"
  },
  {
    "path": "tests/test_numeric.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2017-2018 reverendus\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nimport math\nimport pytest\n\nfrom pymaker.numeric import Wad, Ray, Rad\nfrom tests.helpers import is_hashable\n\n\nclass TestWad:\n    def test_should_support_negative_values(self):\n        Wad(-1)\n\n    def test_should_support_values_greater_than_uint256(self):\n        Wad(2**256)\n        Wad(2**256 + 1)\n        Wad(2**512)\n\n    def test_should_instantiate_from_a_wad(self):\n        assert Wad(Wad(1)) == Wad(1)\n\n    def test_should_instantiate_from_a_ray(self):\n        assert Wad(Ray(10000000000000001010101010101)) == Wad(10000000000000001010)\n        assert Wad(Ray(10000000000000001019999999999)) == Wad(10000000000000001019)\n\n    def test_should_instantiate_from_an_int(self):\n        assert Wad(10).value == 10\n\n    def test_should_fail_to_instantiate_from_a_float(self):\n        with pytest.raises(ArithmeticError):\n            assert Wad(10.5)\n\n    def test_should_format_to_string_nicely(self):\n        assert str(Wad(1)) == \"0.000000000000000001\"\n        assert str(Wad(500000000000000000)) == \"0.500000000000000000\"\n        assert str(Wad(1500000000000000000)) == \"1.500000000000000000\"\n        assert str(Wad(-1500000000000000000)) == \"-1.500000000000000000\"\n        assert str(Wad(-500000000000000000)) == \"-0.500000000000000000\"\n        assert str(Wad(-1)) == \"-0.000000000000000001\"\n\n    def test_should_have_nice_printable_representation(self):\n        for wad in [Wad(1), Wad(100), Wad.from_number(2.5), Wad(-1)]:\n            assert repr(wad) == f\"Wad({wad.value})\"\n\n    def test_add(self):\n        assert Wad(1) + Wad(2) == Wad(3)\n\n    def test_add_should_not_work_with_rays(self):\n        with pytest.raises(ArithmeticError):\n            Wad(1) + Ray(2)\n\n    def test_add_should_not_work_with_ints(self):\n        with pytest.raises(ArithmeticError):\n            Wad(1) + 2\n\n    def test_subtract(self):\n        assert Wad(10) - Wad(2) == Wad(8)\n        assert Wad(1) - Wad(2) == Wad(-1)\n\n    def test_subtract_should_not_work_with_rays(self):\n        with pytest.raises(ArithmeticError):\n            Wad(10) - Ray(2)\n    \n    def test_modulo(self):\n        assert Wad(10) % Wad(2) == Wad(0)\n        assert Wad(11) % Wad(5) == Wad(1)\n        assert Wad(11) % Wad(3) == Wad(2)\n\n    def test_modulo_should_not_work_with_rays(self):\n        with pytest.raises(ArithmeticError):\n            Wad(10) % Ray(3)\n\n    def test_multiply(self):\n        assert Wad.from_number(2) * Wad.from_number(3) == Wad.from_number(6)\n        assert Wad.from_number(2) * Wad(3) == Wad(6)\n        assert Wad.from_number(2.5) * Wad(3) == Wad(7)\n        assert Wad.from_number(2.99999) * Wad(3) == Wad(8)\n\n    def test_multiply_by_ray(self):\n        assert Wad.from_number(2) * Ray.from_number(3) == Wad.from_number(6)\n        assert Wad.from_number(2) * Ray(3) == Wad(0)\n        assert Wad(2) * Ray(499999999999999999999999999) == Wad(0)\n        assert Wad(2) * Ray(500000000000000000000000000) == Wad(1)\n        assert Wad(2) * Ray(999999999999999999999999999) == Wad(1)\n        assert Wad(2) * Ray(1000000000000000000000000000) == Wad(2)\n\n    def test_multiply_by_int(self):\n        assert Wad.from_number(2) * 3 == Wad.from_number(6)\n        assert Wad.from_number(2) * 1 == Wad.from_number(2)\n\n    def test_should_fail_to_multiply_by_float(self):\n        with pytest.raises(ArithmeticError):\n            Wad(2) * 3.0\n\n    def test_divide(self):\n        assert Wad.from_number(4) / Wad.from_number(2) == Wad.from_number(2)\n        assert Wad(4) / Wad.from_number(2) == Wad(2)\n        assert Wad(3) / Wad.from_number(2) == Wad(1)\n        assert Wad(39) / Wad.from_number(20) == Wad(1)\n        assert Wad(40) / Wad.from_number(20) == Wad(2)\n        assert Wad.from_number(0.2) / Wad.from_number(0.1) == Wad.from_number(2)\n\n    def test_should_fail_to_divide_by_rays(self):\n        with pytest.raises(ArithmeticError):\n            Wad(4) / Ray(2)\n\n    def test_should_fail_to_divide_by_ints(self):\n        with pytest.raises(ArithmeticError):\n            Wad(4) / 2\n\n    def test_should_support_abs(self):\n        assert abs(Wad(1000)) == Wad(1000)\n        assert abs(Wad(0)) == Wad(0)\n        assert abs(Wad(-1000)) == Wad(1000)\n\n    def test_should_compare_wads_with_each_other(self):\n        assert Wad(1000) == Wad(1000)\n        assert Wad(1000) != Wad(999)\n        assert Wad(1000) > Wad(999)\n        assert Wad(999) < Wad(1000)\n        assert Wad(999) <= Wad(1000)\n        assert Wad(1000) <= Wad(1000)\n        assert Wad(1000) >= Wad(1000)\n        assert Wad(1000) >= Wad(999)\n\n    def test_should_reject_comparison_with_rays(self):\n        with pytest.raises(ArithmeticError):\n            assert Wad(1000) == Ray(1000)\n        with pytest.raises(ArithmeticError):\n            assert Wad(1000) != Ray(999)\n        with pytest.raises(ArithmeticError):\n            assert Wad(1000) > Ray(999)\n        with pytest.raises(ArithmeticError):\n            assert Wad(999) < Ray(1000)\n        with pytest.raises(ArithmeticError):\n            assert Wad(999) <= Ray(1000)\n        with pytest.raises(ArithmeticError):\n            assert Wad(1000) <= Ray(1000)\n        with pytest.raises(ArithmeticError):\n            assert Wad(1000) >= Ray(1000)\n        with pytest.raises(ArithmeticError):\n            assert Wad(1000) >= Ray(999)\n\n    def test_should_reject_comparison_with_ints(self):\n        with pytest.raises(ArithmeticError):\n            assert Wad(1000) == 100\n        with pytest.raises(ArithmeticError):\n            assert Wad(1000) != 999\n        with pytest.raises(ArithmeticError):\n            assert Wad(1000) > 999\n        with pytest.raises(ArithmeticError):\n            assert Wad(999) < 1000\n        with pytest.raises(ArithmeticError):\n            assert Wad(999) <= 1000\n        with pytest.raises(ArithmeticError):\n            assert Wad(1000) <= 1000\n        with pytest.raises(ArithmeticError):\n            assert Wad(1000) >= 1000\n        with pytest.raises(ArithmeticError):\n            assert Wad(1000) >= 999\n\n    def test_should_cast_to_int(self):\n        assert int(Wad.from_number(-4.5)) == -4\n        assert int(Wad.from_number(0.99)) == 0\n        assert int(Wad.from_number(1)) == 1\n        assert int(Wad.from_number(1.0)) == 1\n        assert int(Wad.from_number(1.5)) == 1\n        assert int(Wad.from_number(1.9999999999)) == 1\n\n    def test_should_cast_to_float(self):\n        assert float(Wad.from_number(-4.5)) == -4.5\n        assert float(Wad.from_number(0.99)) == 0.99\n        assert float(Wad.from_number(1)) == 1.0\n        assert float(Wad.from_number(1.0)) == 1.0\n        assert float(Wad.from_number(1.5)) == 1.5\n        assert float(Wad.from_number(1.9999999999)) == 1.9999999999\n\n    def test_should_be_hashable(self):\n        assert is_hashable(Wad(123))\n\n    def test_min_value(self):\n        assert Wad.min(Wad(10), Wad(20)) == Wad(10)\n        assert Wad.min(Wad(25), Wad(15)) == Wad(15)\n        assert Wad.min(Wad(25), Wad(15), Wad(5)) == Wad(5)\n\n    def test_min_value_should_reject_comparison_with_rays(self):\n        with pytest.raises(ArithmeticError):\n            Wad.min(Wad(10), Ray(20))\n        with pytest.raises(ArithmeticError):\n            Wad.min(Ray(25), Wad(15))\n\n    def test_min_value_should_reject_comparison_with_ints(self):\n        with pytest.raises(ArithmeticError):\n            Wad.min(Wad(10), 20)\n        with pytest.raises(ArithmeticError):\n            Wad.min(20, Wad(10))\n\n    def test_max_value(self):\n        assert Wad.max(Wad(10), Wad(20)) == Wad(20)\n        assert Wad.max(Wad(25), Wad(15)) == Wad(25)\n        assert Wad.max(Wad(25), Wad(15), Wad(40)) == Wad(40)\n\n    def test_max_value_should_reject_comparison_with_rays(self):\n        with pytest.raises(ArithmeticError):\n            Wad.max(Wad(10), Ray(20))\n        with pytest.raises(ArithmeticError):\n            Wad.max(Wad(25), Ray(15))\n\n    def test_max_value_should_reject_comparison_with_ints(self):\n        with pytest.raises(ArithmeticError):\n            Wad.max(Wad(10), 20)\n        with pytest.raises(ArithmeticError):\n            Wad.max(15, Wad(25))\n\n    def test_round(self):\n        assert round(Wad.from_number(123.4567), 2) == Wad.from_number(123.46)\n        assert round(Wad.from_number(123.4567), 0) == Wad.from_number(123.0)\n        assert round(Wad.from_number(123.4567), -2) == Wad.from_number(100.0)\n\n    def test_round_inequality(self):\n        # should hold for all x, ndigits\n        x = Wad.from_number(7654.321)\n        ndigits = 1\n        round_difference = x - round(x, ndigits)\n        round_distance = Wad(abs(round_difference.value))\n        assert round_distance <= Wad.from_number(0.5 * 10**(-ndigits))    \n\n    def test_square_root(self):\n        test_std_sqrt = Wad.from_number(math.sqrt(16.5))\n        test_pymaker_sqrt = Wad.__sqrt__(Wad.from_number(16.5))\n\n        assert test_std_sqrt == test_pymaker_sqrt\n\nclass TestRay:\n    def test_should_support_negative_values(self):\n        Ray(-1)\n\n    def test_should_support_values_greater_than_uint256(self):\n        Ray(2**256)\n        Ray(2**256 + 1)\n        Ray(2**512)\n\n    def test_should_instantiate_from_a_ray(self):\n        assert Ray(Ray(1)) == Ray(1)\n\n    def test_should_instantiate_from_a_wad(self):\n        assert Ray(Wad(10000000000000000000)) == Ray(10000000000000000000000000000)\n\n    def test_should_instantiate_from_an_int(self):\n        assert Ray(10).value == 10\n\n    def test_should_fail_to_instantiate_from_a_float(self):\n        with pytest.raises(ArithmeticError):\n            assert Ray(10.5)\n\n    def test_should_format_to_string_nicely(self):\n        assert str(Ray(1)) == \"0.000000000000000000000000001\"\n        assert str(Ray(500000000000000000000000000)) == \"0.500000000000000000000000000\"\n        assert str(Ray(1500000000000000000000000000)) == \"1.500000000000000000000000000\"\n        assert str(Ray(-1500000000000000000000000000)) == \"-1.500000000000000000000000000\"\n        assert str(Ray(-500000000000000000000000000)) == \"-0.500000000000000000000000000\"\n        assert str(Ray(-1)) == \"-0.000000000000000000000000001\"\n\n    def test_should_have_nice_printable_representation(self):\n        for ray in [Ray(1), Ray(100), Ray.from_number(2.5), Ray(-1)]:\n            assert repr(ray) == f\"Ray({ray.value})\"\n\n    def test_add(self):\n        assert Ray(1) + Ray(2) == Ray(3)\n\n    def test_add_should_not_work_with_wads(self):\n        with pytest.raises(ArithmeticError):\n            Ray(1) + Wad(2)\n\n    def test_add_should_not_work_with_ints(self):\n        with pytest.raises(ArithmeticError):\n            Ray(1) + 2\n\n    def test_subtract(self):\n        assert Ray(10) - Ray(2) == Ray(8)\n        assert Ray(1) - Ray(2) == Ray(-1)\n\n    def test_subtract_should_not_work_with_wads(self):\n        with pytest.raises(ArithmeticError):\n            Ray(10) - Wad(2)\n\n    def test_modulo(self):\n        assert Ray(10) % Ray(2) == Ray(0)\n        assert Ray(11) % Ray(5) == Ray(1)\n        assert Ray(11) % Ray(3) == Ray(2)\n\n    def test_modulo_should_not_work_with_wads(self):\n        with pytest.raises(ArithmeticError):\n            Ray(10) % Wad(3)\n\n    def test_multiply(self):\n        assert Ray.from_number(2) * Ray.from_number(3) == Ray.from_number(6)\n        assert Ray.from_number(2) * Ray(3) == Ray(6)\n        assert Ray.from_number(2.5) * Ray(3) == Ray(7)\n        assert Ray.from_number(2.99999) * Ray(3) == Ray(8)\n\n    def test_multiply_by_wad(self):\n        assert Ray.from_number(2) * Wad.from_number(3) == Ray.from_number(6)\n        assert Ray.from_number(2) * Wad(3) == Ray(6000000000)\n        assert Ray(2) * Wad(3) == Ray(0)\n        assert Ray(2) * Wad(999999999999999999) == Ray(1)\n        assert Ray(2) * Wad(1000000000000000000) == Ray(2)\n\n    def test_multiply_by_int(self):\n        assert Ray.from_number(2) * 3 == Ray.from_number(6)\n        assert Ray.from_number(2) * 1 == Ray.from_number(2)\n\n    def test_should_fail_to_multiply_by_float(self):\n        with pytest.raises(ArithmeticError):\n            Ray(2) * 3.0\n\n    def test_divide(self):\n        assert Ray.from_number(4) / Ray.from_number(2) == Ray.from_number(2)\n        assert Ray(4) / Ray.from_number(2) == Ray(2)\n        assert Ray(3) / Ray.from_number(2) == Ray(1)\n        assert Ray(39) / Ray.from_number(20) == Ray(1)\n        assert Ray(40) / Ray.from_number(20) == Ray(2)\n        assert Ray.from_number(0.2) / Ray.from_number(0.1) == Ray.from_number(2)\n\n    def test_should_fail_to_divide_by_wads(self):\n        with pytest.raises(ArithmeticError):\n            Ray(4) / Wad(2)\n\n    def test_should_fail_to_divide_by_ints(self):\n        with pytest.raises(ArithmeticError):\n            Ray(4) / 2\n\n    def test_should_support_abs(self):\n        assert abs(Ray(1000)) == Ray(1000)\n        assert abs(Ray(0)) == Ray(0)\n        assert abs(Ray(-1000)) == Ray(1000)\n\n    def test_should_compare_rays_with_each_other(self):\n        assert Ray(1000) == Ray(1000)\n        assert Ray(1000) != Ray(999)\n        assert Ray(1000) > Ray(999)\n        assert Ray(999) < Ray(1000)\n        assert Ray(999) <= Ray(1000)\n        assert Ray(1000) <= Ray(1000)\n        assert Ray(1000) >= Ray(1000)\n        assert Ray(1000) >= Ray(999)\n\n    def test_should_reject_comparison_with_wads(self):\n        with pytest.raises(ArithmeticError):\n            assert Ray(1000) == Wad(1000)\n        with pytest.raises(ArithmeticError):\n            assert Ray(1000) != Wad(999)\n        with pytest.raises(ArithmeticError):\n            assert Ray(1000) > Wad(999)\n        with pytest.raises(ArithmeticError):\n            assert Ray(999) < Wad(1000)\n        with pytest.raises(ArithmeticError):\n            assert Ray(999) <= Wad(1000)\n        with pytest.raises(ArithmeticError):\n            assert Ray(1000) <= Wad(1000)\n        with pytest.raises(ArithmeticError):\n            assert Ray(1000) >= Wad(1000)\n        with pytest.raises(ArithmeticError):\n            assert Ray(1000) >= Wad(999)\n\n    def test_should_reject_comparison_with_ints(self):\n        with pytest.raises(ArithmeticError):\n            assert Ray(1000) == 100\n        with pytest.raises(ArithmeticError):\n            assert Ray(1000) != 999\n        with pytest.raises(ArithmeticError):\n            assert Ray(1000) > 999\n        with pytest.raises(ArithmeticError):\n            assert Ray(999) < 1000\n        with pytest.raises(ArithmeticError):\n            assert Ray(999) <= 1000\n        with pytest.raises(ArithmeticError):\n            assert Ray(1000) <= 1000\n        with pytest.raises(ArithmeticError):\n            assert Ray(1000) >= 1000\n        with pytest.raises(ArithmeticError):\n            assert Ray(1000) >= 999\n\n    def test_should_cast_to_int(self):\n        assert int(Ray.from_number(-4.5)) == -4\n        assert int(Ray.from_number(0.99)) == 0\n        assert int(Ray.from_number(1)) == 1\n        assert int(Ray.from_number(1.0)) == 1\n        assert int(Ray.from_number(1.5)) == 1\n        assert int(Ray.from_number(1.9999999999)) == 1\n\n    def test_should_cast_to_float(self):\n        assert float(Ray.from_number(-4.5)) == -4.5\n        assert float(Ray.from_number(0.99)) == 0.99\n        assert float(Ray.from_number(1)) == 1.0\n        assert float(Ray.from_number(1.0)) == 1.0\n        assert float(Ray.from_number(1.5)) == 1.5\n        assert float(Ray.from_number(1.9999999999)) == 1.9999999999\n\n    def test_should_be_hashable(self):\n        assert is_hashable(Ray(123))\n\n    def test_min_value(self):\n        assert Ray.min(Ray(10), Ray(20)) == Ray(10)\n        assert Ray.min(Ray(25), Ray(15)) == Ray(15)\n        assert Ray.min(Ray(25), Ray(15), Ray(5)) == Ray(5)\n\n    def test_min_value_should_reject_comparison_with_wads(self):\n        with pytest.raises(ArithmeticError):\n            Ray.min(Ray(10), Wad(20))\n        with pytest.raises(ArithmeticError):\n            Ray.min(Wad(25), Ray(15))\n\n    def test_min_value_should_reject_comparison_with_ints(self):\n        with pytest.raises(ArithmeticError):\n            Ray.min(Ray(10), 20)\n        with pytest.raises(ArithmeticError):\n            Ray.min(20, Ray(10))\n\n    def test_max_value(self):\n        assert Ray.max(Ray(10), Ray(20)) == Ray(20)\n        assert Ray.max(Ray(25), Ray(15)) == Ray(25)\n        assert Ray.max(Ray(25), Ray(15), Ray(40)) == Ray(40)\n\n    def test_max_value_should_reject_comparison_with_wads(self):\n        with pytest.raises(ArithmeticError):\n            Ray.max(Ray(10), Wad(20))\n        with pytest.raises(ArithmeticError):\n            Ray.max(Ray(25), Wad(15))\n\n    def test_max_value_should_reject_comparison_with_ints(self):\n        with pytest.raises(ArithmeticError):\n            Ray.max(Ray(10), 20)\n        with pytest.raises(ArithmeticError):\n            Ray.max(15, Ray(25))\n\n    def test_round(self):\n        assert round(Ray.from_number(123.4567), 2) == Ray.from_number(123.46)\n        assert round(Ray.from_number(123.4567), 0) == Ray.from_number(123.0)\n        assert round(Ray.from_number(123.4567), -2) == Ray.from_number(100.0)\n\n    def test_square_root(self):\n        test_std_sqrt = Ray.from_number(math.sqrt(16.5))\n        test_pymaker_sqrt = Ray.__sqrt__(Ray.from_number(16.5))\n\n        assert test_std_sqrt == test_pymaker_sqrt\n\nclass TestRad:\n    def test_should_support_negative_values(self):\n        Rad(-1)\n\n    def test_should_support_values_greater_than_uint256(self):\n        Rad(2**256)\n        Rad(2**256 + 1)\n        Rad(2**512)\n\n    def test_should_instantiate_from_a_rad(self):\n        assert Rad(Rad(1)) == Rad(1)\n\n    def test_should_instantiate_from_a_wad(self):\n        assert Rad(Wad(10000000000000000000)) == Rad.from_number(10)\n\n    def test_should_instantiate_from_a_ray(self):\n        assert Rad(Ray.from_number(10)) == Rad.from_number(10)\n\n    def test_should_instantiate_from_an_int(self):\n        assert Rad(10).value == 10\n\n    def test_should_fail_to_instantiate_from_a_float(self):\n        with pytest.raises(ArithmeticError):\n            assert Rad(10.5)\n\n    def test_should_format_to_string_nicely(self):\n        assert str(Rad(1)) == \"0.000000000000000000000000000000000000000000001\"\n        assert str(Rad(500000000000000000000000000000000000000000000)) == \"0.500000000000000000000000000000000000000000000\"\n        assert str(Rad(1500000000000000000000000000000000000000000000)) == \"1.500000000000000000000000000000000000000000000\"\n        assert str(Rad(-1500000000000000000000000000000000000000000000)) == \"-1.500000000000000000000000000000000000000000000\"\n        assert str(Rad(-500000000000000000000000000000000000000000000)) == \"-0.500000000000000000000000000000000000000000000\"\n        assert str(Rad(-1)) == \"-0.000000000000000000000000000000000000000000001\"\n\n    def test_should_have_nice_printable_representation(self):\n        for ray in [Rad(1), Rad(100), Rad.from_number(2.5), Rad(-1)]:\n            assert repr(ray) == f\"Rad({ray.value})\"\n\n    def test_add(self):\n        assert Rad(1) + Rad(2) == Rad(3)\n\n    def test_add_should_not_work_with_wads(self):\n        with pytest.raises(ArithmeticError):\n            Rad(1) + Wad(2)\n\n    def test_add_should_not_work_with_rays(self):\n        with pytest.raises(ArithmeticError):\n            Rad(1) + Ray(2)\n\n    def test_add_should_not_work_with_ints(self):\n        with pytest.raises(ArithmeticError):\n            Rad(1) + 2\n\n    def test_subtract(self):\n        assert Rad(10) - Rad(2) == Rad(8)\n        assert Rad(1) - Rad(2) == Rad(-1)\n\n    def test_subtract_should_not_work_with_wads(self):\n        with pytest.raises(ArithmeticError):\n            Rad(10) - Wad(2)\n\n    def test_subtract_should_not_work_with_rays(self):\n        with pytest.raises(ArithmeticError):\n            Rad(10) - Ray(2)\n\n    def test_modulo(self):\n        assert Rad(10) % Rad(2) == Rad(0)\n        assert Rad(11) % Rad(5) == Rad(1)\n        assert Rad(11) % Rad(3) == Rad(2)\n\n    def test_modulo_should_not_work_with_wads(self):\n        with pytest.raises(ArithmeticError):\n            Rad(10) % Wad(3)\n\n    def test_multiply(self):\n        assert Rad.from_number(2) * Rad.from_number(3) == Rad.from_number(6)\n        assert Rad.from_number(2) * Rad(3) == Rad(6)\n        assert Rad.from_number(2.5) * Rad(3) == Rad(7)\n        assert Rad.from_number(2.99999) * Rad(3) == Rad(8)\n\n    def test_multiply_by_wad(self):\n        assert Rad.from_number(2) * Wad.from_number(3) == Rad.from_number(6)\n        assert Rad.from_number(2) * Wad(3) == Rad(6000000000000000000000000000)\n        assert Rad(2) * Wad(3) == Rad(0)\n        assert Rad(2) * Wad(999999999999999999) == Rad(1)\n        assert Rad(2) * Wad(1000000000000000000) == Rad(2)\n\n    def test_multiply_by_ray(self):\n        assert Rad.from_number(2) * Ray.from_number(3) == Rad.from_number(6)\n        assert Rad.from_number(2) * Ray(3) == Rad(6000000000000000000)\n        assert Rad(2) * Ray(3) == Rad(0)\n        assert Rad(2) * Ray(999999999999999999999999999) == Rad(1)\n        assert Rad(2) * Ray(1000000000000000000000000000) == Rad(2)\n\n    def test_multiply_by_int(self):\n        assert Rad.from_number(2) * 3 == Rad.from_number(6)\n        assert Rad.from_number(2) * 1 == Rad.from_number(2)\n\n    def test_should_fail_to_multiply_by_float(self):\n        with pytest.raises(ArithmeticError):\n            Rad(2) * 3.0\n\n    def test_divide(self):\n        assert Rad.from_number(4) / Rad.from_number(2) == Rad.from_number(2)\n        assert Rad(4) / Rad.from_number(2) == Rad(2)\n        assert Rad(3) / Rad.from_number(2) == Rad(1)\n        assert Rad(39) / Rad.from_number(20) == Rad(1)\n        assert Rad(40) / Rad.from_number(20) == Rad(2)\n        assert Rad.from_number(0.2) / Rad.from_number(0.1) == Rad.from_number(2)\n\n    def test_should_fail_to_divide_by_wads(self):\n        with pytest.raises(ArithmeticError):\n            Rad(4) / Wad(2)\n\n    def test_should_fail_to_divide_by_rays(self):\n        with pytest.raises(ArithmeticError):\n            Rad(4) / Ray(2)\n\n    def test_should_fail_to_divide_by_ints(self):\n        with pytest.raises(ArithmeticError):\n            Rad(4) / 2\n\n    def test_should_support_abs(self):\n        assert abs(Rad(1000)) == Rad(1000)\n        assert abs(Rad(0)) == Rad(0)\n        assert abs(Rad(-1000)) == Rad(1000)\n\n    def test_should_compare_rays_with_each_other(self):\n        assert Rad(1000) == Rad(1000)\n        assert Rad(1000) != Rad(999)\n        assert Rad(1000) > Rad(999)\n        assert Rad(999) < Rad(1000)\n        assert Rad(999) <= Rad(1000)\n        assert Rad(1000) <= Rad(1000)\n        assert Rad(1000) >= Rad(1000)\n        assert Rad(1000) >= Rad(999)\n\n    def test_should_reject_comparison_with_wads(self):\n        with pytest.raises(ArithmeticError):\n            assert Rad(1000) == Wad(1000)\n        with pytest.raises(ArithmeticError):\n            assert Rad(1000) != Wad(999)\n        with pytest.raises(ArithmeticError):\n            assert Rad(1000) > Wad(999)\n        with pytest.raises(ArithmeticError):\n            assert Rad(999) < Wad(1000)\n        with pytest.raises(ArithmeticError):\n            assert Rad(999) <= Wad(1000)\n        with pytest.raises(ArithmeticError):\n            assert Rad(1000) <= Wad(1000)\n        with pytest.raises(ArithmeticError):\n            assert Rad(1000) >= Wad(1000)\n        with pytest.raises(ArithmeticError):\n            assert Rad(1000) >= Wad(999)\n\n    def test_should_reject_comparison_with_rays(self):\n        with pytest.raises(ArithmeticError):\n            assert Rad(1000) == Ray(1000)\n        with pytest.raises(ArithmeticError):\n            assert Rad(1000) != Ray(999)\n        with pytest.raises(ArithmeticError):\n            assert Rad(1000) > Ray(999)\n        with pytest.raises(ArithmeticError):\n            assert Rad(999) < Ray(1000)\n        with pytest.raises(ArithmeticError):\n            assert Rad(999) <= Ray(1000)\n        with pytest.raises(ArithmeticError):\n            assert Rad(1000) <= Ray(1000)\n        with pytest.raises(ArithmeticError):\n            assert Rad(1000) >= Ray(1000)\n        with pytest.raises(ArithmeticError):\n            assert Rad(1000) >= Ray(999)\n\n    def test_should_reject_comparison_with_ints(self):\n        with pytest.raises(ArithmeticError):\n            assert Rad(1000) == 100\n        with pytest.raises(ArithmeticError):\n            assert Rad(1000) != 999\n        with pytest.raises(ArithmeticError):\n            assert Rad(1000) > 999\n        with pytest.raises(ArithmeticError):\n            assert Rad(999) < 1000\n        with pytest.raises(ArithmeticError):\n            assert Rad(999) <= 1000\n        with pytest.raises(ArithmeticError):\n            assert Rad(1000) <= 1000\n        with pytest.raises(ArithmeticError):\n            assert Rad(1000) >= 1000\n        with pytest.raises(ArithmeticError):\n            assert Rad(1000) >= 999\n\n    def test_should_cast_to_int(self):\n        assert int(Rad.from_number(-4.5)) == -4\n        assert int(Rad.from_number(0.99)) == 0\n        assert int(Rad.from_number(1)) == 1\n        assert int(Rad.from_number(1.0)) == 1\n        assert int(Rad.from_number(1.5)) == 1\n        assert int(Rad.from_number(1.9999999999)) == 1\n\n    def test_should_cast_to_float(self):\n        assert float(Rad.from_number(-4.5)) == -4.5\n        assert float(Rad.from_number(0.99)) == 0.99\n        assert float(Rad.from_number(1)) == 1.0\n        assert float(Rad.from_number(1.0)) == 1.0\n        assert float(Rad.from_number(1.5)) == 1.5\n        assert float(Rad.from_number(1.9999999999)) == 1.9999999999\n\n    def test_should_be_hashable(self):\n        assert is_hashable(Rad(123))\n\n    def test_min_value(self):\n        assert Rad.min(Rad(10), Rad(20)) == Rad(10)\n        assert Rad.min(Rad(25), Rad(15)) == Rad(15)\n        assert Rad.min(Rad(25), Rad(15), Rad(5)) == Rad(5)\n\n    def test_min_value_should_reject_comparison_with_wads(self):\n        with pytest.raises(ArithmeticError):\n            Rad.min(Rad(10), Wad(20))\n        with pytest.raises(ArithmeticError):\n            Rad.min(Wad(25), Rad(15))\n\n    def test_min_value_should_reject_comparison_with_rays(self):\n        with pytest.raises(ArithmeticError):\n            Rad.min(Rad(10), Ray(20))\n        with pytest.raises(ArithmeticError):\n            Rad.min(Ray(25), Rad(15))\n\n    def test_min_value_should_reject_comparison_with_ints(self):\n        with pytest.raises(ArithmeticError):\n            Rad.min(Rad(10), 20)\n        with pytest.raises(ArithmeticError):\n            Rad.min(20, Rad(10))\n\n    def test_max_value(self):\n        assert Rad.max(Rad(10), Rad(20)) == Rad(20)\n        assert Rad.max(Rad(25), Rad(15)) == Rad(25)\n        assert Rad.max(Rad(25), Rad(15), Rad(40)) == Rad(40)\n\n    def test_max_value_should_reject_comparison_with_wads(self):\n        with pytest.raises(ArithmeticError):\n            Rad.max(Rad(10), Wad(20))\n        with pytest.raises(ArithmeticError):\n            Rad.max(Rad(25), Wad(15))\n\n    def test_max_value_should_reject_comparison_with_rays(self):\n        with pytest.raises(ArithmeticError):\n            Rad.max(Rad(10), Ray(20))\n        with pytest.raises(ArithmeticError):\n            Rad.max(Rad(25), Ray(15))\n\n    def test_max_value_should_reject_comparison_with_ints(self):\n        with pytest.raises(ArithmeticError):\n            Rad.max(Rad(10), 20)\n        with pytest.raises(ArithmeticError):\n            Rad.max(15, Rad(25))\n\n    def test_round(self):\n        assert round(Rad.from_number(123.4567), 2) == Rad.from_number(123.46)\n        assert round(Rad.from_number(123.4567), 0) == Rad.from_number(123.0)\n        assert round(Rad.from_number(123.4567), -2) == Rad.from_number(100.0)\n\n    def test_square_root(self):\n        test_std_sqrt = Rad.from_number(math.sqrt(16.5))\n        test_pymaker_sqrt = Rad.__sqrt__(Rad.from_number(16.5))\n\n        assert test_std_sqrt == test_pymaker_sqrt\n"
  },
  {
    "path": "tests/test_oasis.py",
    "content": "# This file is part of Maker Keeper Framework.\n\n#\n# Copyright (C) 2017-2018 reverendus\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\nfrom typing import List\nfrom unittest.mock import Mock\n\nimport logging\nimport pytest\nimport time\nfrom web3 import HTTPProvider\nfrom web3 import Web3\n\nfrom pymaker import Address, Wad, Contract, Transact\nfrom pymaker.approval import directly\nfrom pymaker.oasis import SimpleMarket, MatchingMarket, Order\nfrom pymaker.token import DSToken\nfrom pymaker.model import Token\nfrom tests.helpers import wait_until_mock_called, is_hashable, time_travel_by\n\n\nPAST_BLOCKS = 100\n\n\nclass OasisMockPriceOracle(Contract):\n    \"\"\"A mock price Oracle for deploying Oasis MatchingMarket contract\"\"\"\n\n    abi = Contract._load_abi(__name__, 'abi/OasisMockPriceOracle.abi')\n    bin = Contract._load_bin(__name__, 'abi/OasisMockPriceOracle.bin')\n\n    def __init__(self, web3: Web3, address: Address):\n        assert(isinstance(web3, Web3))\n        assert(isinstance(address, Address))\n\n        self.web3 = web3\n        self.address = address\n        self._contract = self._get_contract(web3, self.abi, address)\n\n    @staticmethod\n    def deploy(web3: Web3):\n        return OasisMockPriceOracle(web3=web3, address=Contract._deploy(web3, OasisMockPriceOracle.abi,\n                                                                        OasisMockPriceOracle.bin, []))\n\n    def set_price(self, price: Wad):\n        assert isinstance(price, Wad)\n        return Transact(self, self.web3, self.abi, self.address, self._contract, 'setPrice', [price.value])\n\n    def __eq__(self, other):\n        return self.address == other.address\n\n    def __repr__(self):\n        return f\"OasisMockPriceOracle('{self.address}')\"\n\n\nclass GeneralMarketTest:\n    def setup_method(self):\n        # reduce logspew\n        logging.getLogger(\"web3\").setLevel(logging.INFO)\n        logging.getLogger(\"urllib3\").setLevel(logging.INFO)\n        logging.getLogger(\"asyncio\").setLevel(logging.INFO)\n\n        self.web3 = Web3(HTTPProvider(\"http://localhost:8555\"))\n        self.web3.eth.defaultAccount = self.web3.eth.accounts[0]\n        self.our_address = Address(self.web3.eth.defaultAccount)\n\n        self.price_oracle = OasisMockPriceOracle.deploy(self.web3)\n        self.price_oracle.set_price(Wad.from_number(10))\n\n        self.token1 = DSToken.deploy(self.web3, 'AAA')\n        self.token1_tokenclass = Token('AAA', self.token1.address, 18)\n        self.token1.mint(Wad.from_number(10000)).transact()\n        self.token2 = DSToken.deploy(self.web3, 'BBB')\n        self.token2_tokenclass = Token('BBB', self.token2.address, 18)\n        self.token2.mint(Wad.from_number(10000)).transact()\n        self.token3 = DSToken.deploy(self.web3, 'CCC')\n        self.token3_tokenclass = Token('CCC', self.token3.address, 18)\n        self.token3.mint(Wad.from_number(10000)).transact()\n        self.otc = None\n\n    def test_approve_and_make_and_getters(self):\n\n        if isinstance(self.otc, MatchingMarket):\n            pay_val = self.token1_tokenclass\n            buy_val = self.token2_tokenclass\n        else:\n            pay_val = self.token1.address\n            buy_val = self.token2.address\n\n        # given\n        assert self.otc.get_last_order_id() == 0\n\n        # when\n        self.otc.approve([self.token1], directly())\n        self.otc.make(pay_val, Wad.from_number(1),\n                      buy_val, Wad.from_number(2)).transact()\n\n        # then\n        assert self.otc.get_last_order_id() == 1\n\n        # and\n        assert self.otc.get_order(1).order_id == 1\n        assert self.otc.get_order(1).pay_token == self.token1.address\n        assert self.otc.get_order(1).pay_amount == Wad.from_number(1)\n        assert self.otc.get_order(1).buy_token == self.token2.address\n        assert self.otc.get_order(1).buy_amount == Wad.from_number(2)\n        assert self.otc.get_order(1).maker == self.our_address\n        assert self.otc.get_order(1).timestamp != 0\n\n        # and\n        assert self.otc.get_orders() == [self.otc.get_order(1)]\n\n    def test_make_returns_new_order_ids(self):\n\n        if isinstance(self.otc, MatchingMarket):\n            pay_val = self.token1_tokenclass\n            buy_val = self.token2_tokenclass\n        else:\n            pay_val = self.token1.address\n            buy_val = self.token2.address\n\n        # given\n        self.otc.approve([self.token1], directly())\n\n        # expect\n        for number in range(1, 10):\n            receipt = self.otc.make(pay_val, Wad.from_number(1),\n                                    buy_val, Wad.from_number(2)).transact()\n\n            assert receipt.result == number\n            assert self.otc.get_last_order_id() == number\n\n    def test_get_orders_by_pair(self):\n\n        if isinstance(self.otc, MatchingMarket):\n            token1_val = self.token1_tokenclass\n            token2_val = self.token2_tokenclass\n            token3_val = self.token3_tokenclass\n\n        else:\n            token1_val = self.token1.address\n            token2_val = self.token2.address\n            token3_val = self.token3.address\n\n\n        # given\n        self.otc.approve([self.token1, self.token2, self.token3], directly())\n\n        # when\n        self.otc.make(token1_val, Wad.from_number(1),\n                      token2_val, Wad.from_number(2)).transact()\n\n        self.otc.make(token1_val, Wad.from_number(1),\n                      token2_val, Wad.from_number(4)).transact()\n\n        self.otc.make(token1_val, Wad.from_number(1),\n                      token2_val, Wad.from_number(3)).transact()\n\n        self.otc.make(token1_val, Wad.from_number(1),\n                      token3_val, Wad.from_number(2)).transact()\n\n        self.otc.make(token1_val, Wad.from_number(1),\n                      token3_val, Wad.from_number(4)).transact()\n\n        self.otc.make(token1_val, Wad.from_number(1),\n                      token3_val, Wad.from_number(3)).transact()\n\n        self.otc.make(token2_val, Wad.from_number(1),\n                      token3_val, Wad.from_number(2)).transact()\n\n        self.otc.make(token2_val, Wad.from_number(1),\n                      token3_val, Wad.from_number(4)).transact()\n\n        self.otc.make(token2_val, Wad.from_number(1),\n                      token3_val, Wad.from_number(3)).transact()\n\n        self.otc.make(token2_val, Wad.from_number(1),\n                      token1_val, Wad.from_number(5)).transact()\n\n        self.otc.make(token2_val, Wad.from_number(1),\n                      token1_val, Wad.from_number(6)).transact()\n\n        # then\n        def order_ids(orders: List[Order]) -> List[int]:\n            return list(map(lambda order: order.order_id, orders))\n\n        assert len(self.otc.get_orders()) == 11\n        assert order_ids(self.otc.get_orders(token1_val, token2_val)) == [1, 2, 3]\n        assert order_ids(self.otc.get_orders(token1_val, token3_val)) == [4, 5, 6]\n        assert order_ids(self.otc.get_orders(token2_val, token3_val)) == [7, 8, 9]\n        assert order_ids(self.otc.get_orders(token2_val, token1_val)) == [10, 11]\n\n        # when\n        self.otc.kill(8).transact()\n\n        # then\n        assert order_ids(self.otc.get_orders(token2_val, token3_val)) == [7, 9]\n\n    def test_get_orders_by_maker(self):\n\n        if isinstance(self.otc, MatchingMarket):\n            token1_val = self.token1_tokenclass\n            token2_val = self.token2_tokenclass\n\n        else:\n            token1_val = self.token1.address\n            token2_val = self.token2.address\n\n        # given\n        maker1 = self.our_address\n        maker2 = Address(self.web3.eth.accounts[1])\n\n        # and\n        self.token1.transfer(maker2, Wad.from_number(500)).transact()\n\n        # when\n        self.otc.approve([self.token1], directly())\n        self.otc.make(token1_val, Wad.from_number(1),\n                      token2_val, Wad.from_number(2)).transact()\n\n        # and\n        self.web3.eth.defaultAccount = self.web3.eth.accounts[1]\n        self.otc.approve([self.token1], directly())\n        self.otc.make(token1_val, Wad.from_number(1),\n                      token2_val, Wad.from_number(2)).transact()\n        self.web3.eth.defaultAccount = self.web3.eth.accounts[0]\n\n        # then\n        assert len(self.otc.get_orders()) == 2\n        assert len(self.otc.get_orders_by_maker(maker1)) == 1\n        assert len(self.otc.get_orders_by_maker(maker2)) == 1\n\n        # and\n        assert self.otc.get_orders_by_maker(maker1)[0].maker == maker1\n        assert self.otc.get_orders_by_maker(maker2)[0].maker == maker2\n\n\n    def test_get_orders_by_block(self):\n\n        if isinstance(self.otc, MatchingMarket):\n            token1_val = self.token1_tokenclass\n            token2_val = self.token2_tokenclass\n\n            # given\n            maker1 = self.our_address\n\n            # when\n            self.otc.approve([self.token1], directly())\n            made_order = self.otc.make(token1_val, Wad.from_number(1),\n                          token2_val, Wad.from_number(2)).transact()\n            block_number = made_order.raw_receipt.blockNumber\n\n            # and when\n            time_travel_by(self.web3, 10)\n            assert len(self.otc.get_orders_by_maker(maker1)) == 1\n            self.otc.kill(1).transact(gas=4000000)\n            assert len(self.otc.get_orders_by_maker(maker1)) == 0\n\n            # then\n            time_travel_by(self.web3, 10)\n            assert len(self.otc.get_orders(token1_val, token2_val, block_number)) == 1\n\n\n    def test_order_comparison(self):\n\n\n        if isinstance(self.otc, MatchingMarket):\n            token1_val = self.token1_tokenclass\n            token2_val = self.token2_tokenclass\n\n        else:\n            token1_val = self.token1.address\n            token2_val = self.token2.address\n\n        # when\n        self.otc.approve([self.token1], directly())\n        self.otc.make(token1_val, Wad.from_number(1),\n                      token2_val, Wad.from_number(2)).transact()\n\n        # and\n        self.otc.make(token1_val, Wad.from_number(3),\n                      token2_val, Wad.from_number(4)).transact()\n\n        # then\n        assert self.otc.get_last_order_id() == 2\n        assert self.otc.get_order(1) == self.otc.get_order(1)\n        assert self.otc.get_order(1) != self.otc.get_order(2)\n\n    def test_order_hashable(self):\n\n        if isinstance(self.otc, MatchingMarket):\n            pay_val = self.token1_tokenclass\n            buy_val = self.token2_tokenclass\n        else:\n            pay_val = self.token1.address\n            buy_val = self.token2.address\n\n        # given\n        self.otc.approve([self.token1], directly())\n        self.otc.make(pay_val, Wad.from_number(1),\n                      buy_val, Wad.from_number(2)).transact()\n\n        # expect\n        assert is_hashable(self.otc.get_order(1))\n\n    def test_take_partial(self):\n\n        if isinstance(self.otc, MatchingMarket):\n            pay_val = self.token1_tokenclass\n            buy_val = self.token2_tokenclass\n        else:\n            pay_val = self.token1.address\n            buy_val = self.token2.address\n\n        # given\n        self.otc.approve([self.token1], directly())\n        self.otc.make(pay_val, Wad.from_number(1),\n                      buy_val, Wad.from_number(2)).transact()\n\n        # when\n        self.otc.approve([self.token2], directly())\n        self.otc.take(1, Wad.from_number(0.25)).transact()\n\n        # then\n        assert self.otc.get_order(1).pay_amount == Wad.from_number(0.75)\n        assert self.otc.get_order(1).buy_amount == Wad.from_number(1.5)\n        assert self.otc.get_orders() == [self.otc.get_order(1)]\n        assert self.otc.get_last_order_id() == 1\n\n    def test_remaining_sell_and_buy_amounts(self):\n\n        if isinstance(self.otc, MatchingMarket):\n            pay_val = self.token1_tokenclass\n            buy_val = self.token2_tokenclass\n        else:\n            pay_val = self.token1.address\n            buy_val = self.token2.address\n\n        # given\n        self.otc.approve([self.token1], directly())\n        self.otc.make(pay_val, Wad.from_number(1),\n                      buy_val, Wad.from_number(2)).transact()\n\n        # and\n        assert self.otc.get_order(1).remaining_sell_amount == Wad.from_number(1)\n        assert self.otc.get_order(1).remaining_buy_amount == Wad.from_number(2)\n\n        # when\n        self.otc.approve([self.token2], directly())\n        self.otc.take(1, Wad.from_number(0.25)).transact()\n\n        # then\n        assert self.otc.get_order(1).remaining_sell_amount == Wad.from_number(0.75)\n        assert self.otc.get_order(1).remaining_buy_amount == Wad.from_number(1.5)\n\n    def test_take_complete(self):\n\n        if isinstance(self.otc, MatchingMarket):\n            pay_val = self.token1_tokenclass\n            buy_val = self.token2_tokenclass\n        else:\n            pay_val = self.token1.address\n            buy_val = self.token2.address\n\n        # given\n        self.otc.approve([self.token1], directly())\n        self.otc.make(pay_val, Wad.from_number(1),\n                      buy_val, Wad.from_number(2)).transact()\n\n        # when\n        self.otc.approve([self.token2], directly())\n        self.otc.take(1, Wad.from_number(1)).transact()\n\n        # then\n        assert self.otc.get_order(1) is None\n        assert self.otc.get_orders() == []\n        assert self.otc.get_last_order_id() == 1\n\n    def test_kill(self):\n\n        if isinstance(self.otc, MatchingMarket):\n            pay_val = self.token1_tokenclass\n            buy_val = self.token2_tokenclass\n        else:\n            pay_val = self.token1.address\n            buy_val = self.token2.address\n\n        # given\n        self.otc.approve([self.token1], directly())\n        self.otc.make(pay_val, Wad.from_number(1),\n                      buy_val, Wad.from_number(2)).transact()\n\n        # when\n        self.otc.kill(1).transact(gas=4000000)\n\n        # then\n        assert self.otc.get_order(1) is None\n        assert self.otc.get_orders() == []\n        assert self.otc.get_last_order_id() == 1\n\n    def test_no_past_events_on_startup(self):\n        assert self.otc.past_make(PAST_BLOCKS) == []\n        assert self.otc.past_bump(PAST_BLOCKS) == []\n        assert self.otc.past_take(PAST_BLOCKS) == []\n        assert self.otc.past_kill(PAST_BLOCKS) == []\n\n    def test_past_make(self):\n\n        if isinstance(self.otc, MatchingMarket):\n            pay_val = self.token1_tokenclass\n            buy_val = self.token2_tokenclass\n        else:\n            pay_val = self.token1.address\n            buy_val = self.token2.address\n\n        # when\n        self.otc.approve([self.token1], directly())\n        self.otc.make(pay_val, Wad.from_number(1),\n                      buy_val, Wad.from_number(2)).transact()\n\n        # then\n        past_make = self.otc.past_make(PAST_BLOCKS)\n        assert len(past_make) == 1\n        assert past_make[0].order_id == 1\n        assert past_make[0].maker == self.our_address\n        assert past_make[0].pay_token == self.token1.address\n        assert past_make[0].pay_amount == Wad.from_number(1)\n        assert past_make[0].buy_token == self.token2.address\n        assert past_make[0].buy_amount == Wad.from_number(2)\n        assert past_make[0].timestamp != 0\n        assert past_make[0].raw['blockNumber'] > 0\n\n    def test_past_bump(self):\n\n        if isinstance(self.otc, MatchingMarket):\n            pay_val = self.token1_tokenclass\n            buy_val = self.token2_tokenclass\n        else:\n            pay_val = self.token1.address\n            buy_val = self.token2.address\n\n        # when\n        self.otc.approve([self.token1], directly())\n        self.otc.make(pay_val, Wad.from_number(1),\n                      buy_val, Wad.from_number(2)).transact()\n        self.otc.bump(1).transact()\n\n        # then\n        past_bump = self.otc.past_bump(PAST_BLOCKS)\n        assert len(past_bump) == 1\n        assert past_bump[0].order_id == 1\n        assert past_bump[0].maker == self.our_address\n        assert past_bump[0].pay_token == self.token1.address\n        assert past_bump[0].pay_amount == Wad.from_number(1)\n        assert past_bump[0].buy_token == self.token2.address\n        assert past_bump[0].buy_amount == Wad.from_number(2)\n        assert past_bump[0].timestamp != 0\n        assert past_bump[0].raw['blockNumber'] > 0\n\n    def test_past_take(self):\n\n        if isinstance(self.otc, MatchingMarket):\n            pay_val = self.token1_tokenclass\n            buy_val = self.token2_tokenclass\n        else:\n            pay_val = self.token1.address\n            buy_val = self.token2.address\n\n        # when\n        self.otc.approve([self.token1], directly())\n        self.otc.make(pay_val, Wad.from_number(1),\n                      buy_val, Wad.from_number(2)).transact()\n\n        # and\n        self.otc.approve([self.token2], directly())\n        self.otc.take(1, Wad.from_number(0.5)).transact()\n\n        # then\n        past_take = self.otc.past_take(PAST_BLOCKS)\n        assert len(past_take) == 1\n        assert past_take[0].order_id == 1\n        assert past_take[0].maker == self.our_address\n        assert past_take[0].taker == self.our_address\n        assert past_take[0].pay_token == self.token1.address\n        assert past_take[0].buy_token == self.token2.address\n        assert past_take[0].take_amount == Wad.from_number(0.5)\n        assert past_take[0].give_amount == Wad.from_number(1)\n        assert past_take[0].timestamp != 0\n        assert past_take[0].raw['blockNumber'] > 0\n\n    def test_past_take_with_filter(self):\n\n        if isinstance(self.otc, MatchingMarket):\n            pay_val = self.token1_tokenclass\n            buy_val = self.token2_tokenclass\n        else:\n            pay_val = self.token1.address\n            buy_val = self.token2.address\n\n        # when\n        self.otc.approve([self.token1], directly())\n        self.otc.make(pay_val, Wad.from_number(1),\n                      buy_val, Wad.from_number(2)).transact()\n\n        # and\n        self.otc.approve([self.token2], directly())\n        self.otc.take(1, Wad.from_number(0.5)).transact()\n\n        # then\n        assert len(self.otc.past_take(PAST_BLOCKS, {'maker': self.our_address.address})) == 1\n        assert len(self.otc.past_take(PAST_BLOCKS, {'taker': self.our_address.address})) == 1\n        assert len(self.otc.past_take(PAST_BLOCKS, {'maker': '0x0101010101020202020203030303030404040404'})) == 0\n        assert len(self.otc.past_take(PAST_BLOCKS, {'taker': '0x0101010101020202020203030303030404040404'})) == 0\n\n    def test_past_kill(self):\n\n        if isinstance(self.otc, MatchingMarket):\n            pay_val = self.token1_tokenclass\n            buy_val = self.token2_tokenclass\n        else:\n            pay_val = self.token1.address\n            buy_val = self.token2.address\n\n        # when\n        self.otc.approve([self.token1], directly())\n        self.otc.make(pay_val, Wad.from_number(1),\n                      buy_val, Wad.from_number(2)).transact()\n\n        # and\n        self.otc.kill(1).transact()\n\n        # then\n        past_kill = self.otc.past_kill(PAST_BLOCKS)\n        assert len(past_kill) == 1\n        assert past_kill[0].order_id == 1\n        assert past_kill[0].maker == self.our_address\n        assert past_kill[0].pay_token == self.token1.address\n        assert past_kill[0].pay_amount == Wad.from_number(1)\n        assert past_kill[0].buy_token == self.token2.address\n        assert past_kill[0].buy_amount == Wad.from_number(2)\n        assert past_kill[0].timestamp != 0\n        assert past_kill[0].raw['blockNumber'] > 0\n\n\nclass TestSimpleMarket(GeneralMarketTest):\n    def setup_method(self):\n        GeneralMarketTest.setup_method(self)\n        self.otc = SimpleMarket.deploy(self.web3)\n\n    def test_fail_when_no_contract_under_that_address(self):\n        # expect\n        with pytest.raises(Exception):\n            SimpleMarket(web3=self.web3, address=Address('0xdeadadd1e5500000000000000000000000000000'))\n\n    def test_should_have_printable_representation(self):\n        assert repr(self.otc) == f\"SimpleMarket('{self.otc.address}')\"\n\n\nclass TestMatchingMarket(GeneralMarketTest):\n    def setup_method(self):\n        GeneralMarketTest.setup_method(self)\n        self.otc = MatchingMarket.deploy(self.web3, self.token1.address, Wad(0), self.price_oracle.address)\n        self.otc.add_token_pair_whitelist(self.token1.address, self.token2.address).transact()\n        self.otc.add_token_pair_whitelist(self.token1.address, self.token3.address).transact()\n        self.otc.add_token_pair_whitelist(self.token2.address, self.token3.address).transact()\n\n    def test_fail_when_no_contract_under_that_address(self):\n        # expect\n        with pytest.raises(Exception):\n            MatchingMarket(web3=self.web3, address=Address('0xdeadadd1e5500000000000000000000000000000'))\n\n    def test_simple_matching(self):\n\n        # given\n        self.otc.approve([self.token1, self.token2], directly())\n        self.otc.make(p_token=self.token1_tokenclass, pay_amount=Wad.from_number(1),\n                      b_token=self.token2_tokenclass, buy_amount=Wad.from_number(2)).transact()\n\n        # when\n        self.otc.make(p_token=self.token2_tokenclass, pay_amount=Wad.from_number(2.5),\n                      b_token=self.token1_tokenclass, buy_amount=Wad.from_number(1)).transact()\n\n        # then\n        assert self.otc.get_order(1) is None\n        assert self.otc.get_order(2) is None\n\n        # and\n        assert self.otc.get_last_order_id() == 1\n\n    def test_advanced_matching(self):\n        # given\n        self.otc.approve([self.token1, self.token2], directly())\n        self.otc.make(p_token=self.token1_tokenclass, pay_amount=Wad.from_number(1),\n                      b_token=self.token2_tokenclass, buy_amount=Wad.from_number(2)).transact()\n        self.otc.make(p_token=self.token1_tokenclass, pay_amount=Wad.from_number(1),\n                      b_token=self.token2_tokenclass, buy_amount=Wad.from_number(2.2)).transact()\n        self.otc.make(p_token=self.token1_tokenclass, pay_amount=Wad.from_number(1),\n                      b_token=self.token2_tokenclass, buy_amount=Wad.from_number(1.8)).transact()\n        self.otc.make(p_token=self.token1_tokenclass, pay_amount=Wad.from_number(1),\n                      b_token=self.token2_tokenclass, buy_amount=Wad.from_number(2.1)).transact()\n        self.otc.make(p_token=self.token1_tokenclass, pay_amount=Wad.from_number(1),\n                      b_token=self.token2_tokenclass, buy_amount=Wad.from_number(1.9)).transact()\n\n        # when\n        self.otc.make(p_token=self.token2_tokenclass, pay_amount=Wad.from_number(20.1),\n                      b_token=self.token1_tokenclass, buy_amount=Wad.from_number(10)).transact(gas=4000000)\n\n        # then\n        assert self.otc.get_order(1) is None\n        assert self.otc.get_order(2) is not None\n        assert self.otc.get_order(3) is None\n        assert self.otc.get_order(4) is not None\n        assert self.otc.get_order(5) is None\n\n        # and\n        assert self.otc.get_last_order_id() == 6\n\n        # and\n        assert self.otc.get_order(6).order_id == 6\n        assert self.otc.get_order(6).pay_token == self.token2.address\n        assert self.otc.get_order(6).pay_amount == Wad.from_number(14.07)\n        assert self.otc.get_order(6).buy_token == self.token1.address\n        assert self.otc.get_order(6).buy_amount == Wad.from_number(7)\n        assert self.otc.get_order(6).maker == self.our_address\n        assert self.otc.get_order(6).timestamp != 0\n\n    def test_should_have_printable_representation(self):\n        assert repr(self.otc) == f\"MatchingMarket('{self.otc.address}')\"\n\n\nclass TestMatchingMarketWithSupportContract(TestMatchingMarket):\n    def setup_method(self):\n        GeneralMarketTest.setup_method(self)\n\n        support_abi = Contract._load_abi(__name__, '../pymaker/abi/MakerOtcSupportMethods.abi')\n        support_bin = Contract._load_bin(__name__, '../pymaker/abi/MakerOtcSupportMethods.bin')\n        support_address = Contract._deploy(self.web3, support_abi, support_bin, [])\n\n        self.price_oracle = OasisMockPriceOracle.deploy(self.web3)\n        self.otc = MatchingMarket.deploy(self.web3, self.token1.address, Wad(0), self.price_oracle.address, support_address)\n        self.otc.add_token_pair_whitelist(self.token1.address, self.token2.address).transact()\n        self.otc.add_token_pair_whitelist(self.token1.address, self.token3.address).transact()\n        self.otc.add_token_pair_whitelist(self.token2.address, self.token3.address).transact()\n\n    def test_fail_when_no_support_contract_under_that_address(self):\n        # expect\n        with pytest.raises(Exception):\n            MatchingMarket(web3=self.web3,\n                           address=self.otc.address,\n                           support_address=Address('0xdeadadd1e5500000000000000000000000000000'))\n\n\nclass TestMatchingMarketDecimal:\n    def setup_method(self):\n        self.web3 = Web3(HTTPProvider(\"http://localhost:8555\"))\n        self.web3.eth.defaultAccount = self.web3.eth.accounts[0]\n        self.our_address = Address(self.web3.eth.defaultAccount)\n        self.token1 = DSToken.deploy(self.web3, 'AAA')\n        self.token1_tokenclass = Token('AAA', self.token1.address, 18)\n        self.token1.mint(Wad.from_number(10000)).transact()\n        self.token2 = DSToken.deploy(self.web3, 'BBB')\n        self.token2_tokenclass = Token('BBB', self.token2.address, 6)\n        self.token2.mint(Wad.from_number(10000)).transact()\n\n        support_abi = Contract._load_abi(__name__, '../pymaker/abi/MakerOtcSupportMethods.abi')\n        support_bin = Contract._load_bin(__name__, '../pymaker/abi/MakerOtcSupportMethods.bin')\n        support_address = Contract._deploy(self.web3, support_abi, support_bin, [])\n\n        price_oracle = OasisMockPriceOracle.deploy(self.web3)\n        self.otc = MatchingMarket.deploy(self.web3, self.token1.address, Wad(0), price_oracle.address, support_address)\n        self.otc.add_token_pair_whitelist(self.token1.address, self.token2.address).transact()\n        self.otc.approve([self.token1, self.token2], directly())\n\n    def test_get_orders(self):\n        buy_amount_order1 = Wad.from_number(5.124988526145090209)\n        pay_amount_order1 = Wad.from_number(5.024999999999999500)\n\n        buy_amount_order2 = Wad.from_number(5.102550000000000000)\n        pay_amount_order2 = Wad.from_number(5.000000000000000000)\n\n        # given\n        self.otc.make(p_token=self.token2_tokenclass, pay_amount=self.token2_tokenclass.unnormalize_amount(pay_amount_order1),\n                      b_token=self.token1_tokenclass, buy_amount=buy_amount_order1).transact()\n\n        self.otc.make(p_token=self.token1_tokenclass, pay_amount=pay_amount_order2,\n                      b_token=self.token2_tokenclass, buy_amount=self.token2_tokenclass.unnormalize_amount(buy_amount_order2)).transact()\n\n        # then\n        assert self.otc.get_orders(self.token1_tokenclass, self.token2_tokenclass)[0].buy_amount == buy_amount_order2\n        assert self.token2_tokenclass.unnormalize_amount(self.otc.get_orders(self.token2_tokenclass, self.token1_tokenclass)[0].pay_amount) == self.token2_tokenclass.unnormalize_amount(pay_amount_order1)\n\nclass TestMatchingMarketPosition:\n    def setup_method(self):\n        self.web3 = Web3(HTTPProvider(\"http://localhost:8555\"))\n        self.web3.eth.defaultAccount = self.web3.eth.accounts[0]\n        self.our_address = Address(self.web3.eth.defaultAccount)\n        self.token1 = DSToken.deploy(self.web3, 'AAA')\n        self.token1.mint(Wad.from_number(10000)).transact()\n        self.token1_tokenclass = Token('AAA', self.token1.address, 18)\n        self.token2 = DSToken.deploy(self.web3, 'BBB')\n        self.token2.mint(Wad.from_number(10000)).transact()\n        self.token2_tokenclass = Token('BBB', self.token2.address, 18)\n        price_oracle = OasisMockPriceOracle.deploy(self.web3)\n        self.otc = MatchingMarket.deploy(self.web3, self.token1.address, Wad(0), price_oracle.address)\n        self.otc.add_token_pair_whitelist(self.token1.address, self.token2.address).transact()\n        self.otc.approve([self.token1, self.token2], directly())\n        for amount in [11,55,44,34,36,21,45,51,15]:\n            self.otc.make(p_token=self.token1_tokenclass, pay_amount=Wad.from_number(1),\n                          b_token=self.token2_tokenclass, buy_amount=Wad.from_number(amount)).transact()\n\n    def test_should_calculate_correct_order_position(self):\n        # expect\n        assert self.otc.position(p_token=self.token1_tokenclass, pay_amount=Wad.from_number(1),\n                                 b_token=self.token2_tokenclass, buy_amount=Wad.from_number(35)) == 4\n\n    @pytest.mark.skip(reason=\"Works unreliably with ganache-cli\")\n    def test_should_use_correct_order_position_by_default(self):\n        # when\n        explicit_position = self.otc.position(p_token=self.token1_tokenclass, pay_amount=Wad.from_number(1),\n                                              b_token=self.token2_tokenclass, buy_amount=Wad.from_number(35))\n        explicit_receipt = self.otc.make(p_token=self.token1_tokenclass, pay_amount=Wad.from_number(1),\n                                         b_token=self.token2_tokenclass, buy_amount=Wad.from_number(35),\n                                         pos=explicit_position).transact()\n        explicit_gas_used = explicit_receipt.gas_used\n\n        # and\n        self.setup_method()\n        implicit_receipt = self.otc.make(p_token=self.token1_tokenclass, pay_amount=Wad.from_number(1),\n                                         b_token=self.token2_tokenclass, buy_amount=Wad.from_number(35)).transact()\n        implicit_gas_used = implicit_receipt.gas_used\n\n        # then\n        assert explicit_gas_used == implicit_gas_used\n\n    @pytest.mark.skip(reason=\"Stopped working as expected after recent `maker-otc` upgrade\")\n    def test_calculated_order_position_should_bring_gas_savings(self):\n        # when\n        position = self.otc.position(p_token=self.token1_tokenclass, pay_amount=Wad.from_number(1),\n                                     b_token=self.token2_tokenclass, buy_amount=Wad.from_number(35))\n        gas_used_optimal = self.otc.make(p_token=self.token1_tokenclass, pay_amount=Wad.from_number(1),\n                                         b_token=self.token2_tokenclass, buy_amount=Wad.from_number(35),\n                                         pos=position).transact().gas_used\n\n        # and\n        self.setup_method()\n        gas_used_minus_1 = self.otc.make(p_token=self.token1_tokenclass, pay_amount=Wad.from_number(1),\n                                         b_token=self.token2_tokenclass, buy_amount=Wad.from_number(35),\n                                         pos=position-1).transact().gas_used\n\n        # and\n        self.setup_method()\n        gas_used_plus_1 = self.otc.make(p_token=self.token1_tokenclass, pay_amount=Wad.from_number(1),\n                                        b_token=self.token2_tokenclass, buy_amount=Wad.from_number(35),\n                                        pos=position+1).transact().gas_used\n\n        # then\n        assert gas_used_optimal < gas_used_minus_1\n        assert gas_used_optimal < gas_used_plus_1\n"
  },
  {
    "path": "tests/test_proxy.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2018,2019 bargst\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nimport pytest\nfrom web3 import Web3\n\nfrom pymaker import Address, Calldata\nfrom pymaker.proxy import DSProxyCache, DSProxy, DSProxyFactory, LogCreated\n\n\n@pytest.fixture(scope=\"session\")\ndef web3():\n    web3 = Web3(Web3.HTTPProvider(\"http://localhost:8555\"))\n    web3.eth.defaultAccount = web3.eth.accounts[0]\n    return web3\n\n\n@pytest.fixture(scope=\"session\")\ndef our_address(web3):\n    return Address(web3.eth.accounts[0])\n\n\n@pytest.fixture(scope=\"session\")\ndef other_address(web3):\n    return Address(web3.eth.accounts[1])\n\n\n@pytest.fixture(scope=\"session\")\ndef proxy_cache(web3):\n    return DSProxyCache.deploy(web3=web3)\n\n\n@pytest.fixture(scope=\"session\")\ndef proxy_factory(web3):\n    return DSProxyFactory.deploy(web3=web3)\n\n\n@pytest.fixture(scope=\"session\")\ndef proxy(web3, proxy_cache):\n    return DSProxy.deploy(web3=web3, cache=proxy_cache.address)\n\n\nclass TestProxyCache:\n    \"\"\" `DSProxyCache` class testing\"\"\"\n\n    def test_read(self, proxy_cache: DSProxyCache):\n        assert proxy_cache.read('0x001122') == None\n\n    def test_write_invalid(self, proxy_cache: DSProxyCache):\n        # when\n        address = proxy_cache.write('0x001122').transact()\n\n        # then\n        assert address is None\n        assert proxy_cache.read('0x001122') == None\n\n    def test_write(self, proxy_cache: DSProxyCache):\n        # when\n        proxy_cache.write(DSProxyCache.bin).transact()\n\n        # then\n        assert proxy_cache.read(DSProxyCache.bin) is not None\n\n\nclass TestProxyFactory:\n    \"\"\" `DSProxyFactory` class testing\"\"\"\n\n    def test_build(self, proxy_factory: DSProxyFactory):\n        assert proxy_factory.build().transact()\n\n    def test_past_build(self, proxy_factory: DSProxyFactory, our_address):\n        # given\n        past_build = proxy_factory.past_build(proxy_factory.web3.eth.blockNumber)\n        past_build_count = len(past_build)\n\n        # when\n        assert proxy_factory.build().transact()\n\n        # then\n        past_build = proxy_factory.past_build(1)\n        assert past_build\n        assert len(past_build) == past_build_count + 1\n\n        past_build: LogCreated = past_build[0]\n        assert past_build.owner == our_address\n        assert past_build.sender == our_address\n        assert past_build.cache == proxy_factory.cache()\n        assert proxy_factory.is_proxy(past_build.proxy)\n\n    def test_build_for(self, proxy_factory: DSProxyFactory, other_address):\n        # given\n        assert proxy_factory.is_proxy(other_address) is False\n\n        # when\n        receipt = proxy_factory.build_for(other_address).transact()\n        assert receipt\n\n        build_event = proxy_factory.log_created(receipt)[0]\n\n        # then\n        assert build_event.owner == other_address\n        assert proxy_factory.is_proxy(build_event.proxy)\n\n    def test_cache(self, proxy_factory: DSProxyFactory, other_address):\n        assert proxy_factory.cache() is not None\n\n\nclass TestProxy:\n    \"\"\" `DSProxy` class testing\"\"\"\n\n    def test_execute(self, proxy: DSProxy):\n        assert proxy.execute(DSProxyFactory.bin, Calldata.from_signature(proxy.web3, \"build()\", [])).transact()\n\n    def test_execute_at(self, proxy: DSProxy):\n        # given\n        proxy_cache = DSProxyCache(proxy.web3, proxy.cache())\n        proxy_cache.write(DSProxyFactory.bin).transact()\n        new_factory_addr = proxy_cache.read(DSProxyFactory.bin)\n        assert new_factory_addr\n\n        # when\n        receipt = proxy.execute_at(new_factory_addr, Calldata.from_signature(proxy.web3, \"build(address)\",\n                                                                             [proxy.address.address])).transact()\n        assert receipt\n        build_event = DSProxyFactory.log_created(receipt)[0]\n\n        # then\n        assert build_event.owner == proxy.address\n\n    def test_call(self, proxy: DSProxy):\n        # when\n        calldata = Calldata.from_signature(proxy.web3, \"isProxy(address)\", [Address(40*'0').address])\n        target, response = proxy.call(DSProxyFactory.bin, calldata)\n\n        # then\n        assert target != Address(40*'0')\n        assert Web3.toInt(response) == 0\n\n    def test_call_at(self, proxy: DSProxy):\n        # given\n        proxy_cache = DSProxyCache(proxy.web3, proxy.cache())\n        proxy_cache.write(DSProxyFactory.bin).transact()\n        new_factory_addr = proxy_cache.read(DSProxyFactory.bin)\n        receipt = proxy.execute_at(new_factory_addr, Calldata.from_signature(proxy.web3,\n                                                                             \"build(address)\",\n                                                                             [proxy.address.address])).transact()\n        log_created: LogCreated = DSProxyFactory.log_created(receipt)[0]\n\n        # when\n        calldata = Calldata.from_signature(proxy.web3, \"isProxy(address)\", [log_created.proxy.address])\n        response = proxy.call_at(new_factory_addr, calldata)\n\n        # then\n        assert Web3.toInt(response) == 1\n"
  },
  {
    "path": "tests/test_reloadable_config.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2020 MikeHathaway\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nfrom unittest.mock import MagicMock\n\nfrom pymaker.reloadable_config import ReloadableConfig\n\n\nclass TestReloadableConfig:\n    @staticmethod\n    def write_sample_config(tmpdir):\n        file = tmpdir.join(\"sample_config.json\")\n        file.write(\"\"\"{\"a\": \"b\"}\"\"\")\n        return str(file)\n\n    @staticmethod\n    def write_advanced_config(tmpdir, value):\n        file = tmpdir.join(\"advanced_config.json\")\n        file.write(\"\"\"{\"a\": \\\"\"\"\" + value + \"\"\"\\\", \"c\": self.a}\"\"\")\n        return str(file)\n\n    @staticmethod\n    def write_global_config(tmpdir, val1, val2):\n        global_file = tmpdir.join(\"global_config.json\")\n        global_file.write(\"\"\"{\n            \"firstValue\": \"\"\" + str(val1) + \"\"\",\n            \"secondValue\": \"\"\" + str(val2) + \"\"\"\n        }\"\"\")\n\n    @staticmethod\n    def write_importing_config(tmpdir):\n        file = tmpdir.join(\"importing_config.json\")\n        file.write(\"\"\"{\n            local globals = import \"./global_config.json\",\n\n            \"firstValueMultiplied\": globals.firstValue * 2,\n            \"secondValueMultiplied\": globals.secondValue * 3\n        }\"\"\")\n        return str(file)\n\n    def test_should_read_simple_file(self, tmpdir):\n        # when\n        config = ReloadableConfig(self.write_sample_config(tmpdir)).get_config()\n\n        # then\n        assert len(config) == 1\n        assert config[\"a\"] == \"b\"\n\n    def test_should_read_advanced_file(self, tmpdir):\n        # when\n        config = ReloadableConfig(self.write_advanced_config(tmpdir, \"b\")).get_config()\n\n        # then\n        assert len(config) == 2\n        assert config[\"a\"] == \"b\"\n        assert config[\"c\"] == \"b\"\n\n    def test_should_read_file_again_if_changed(self, tmpdir):\n        # given\n        reloadable_config = ReloadableConfig(self.write_advanced_config(tmpdir, \"b\"))\n        reloadable_config.logger = MagicMock()\n\n        # when\n        config = reloadable_config.get_config()\n\n        # then\n        assert config[\"a\"] == \"b\"\n\n        # and\n        # [a log message that the config was loaded gets generated]\n        assert reloadable_config.logger.info.call_count == 1\n\n        # when\n        self.write_advanced_config(tmpdir, \"z\")\n        config = reloadable_config.get_config()\n\n        # then\n        assert config[\"a\"] == \"z\"\n\n        # and\n        # [a log message that the config was reloaded gets generated]\n        assert reloadable_config.logger.info.call_count == 2\n\n    def test_should_import_other_config_file(self, tmpdir):\n        # when\n        self.write_global_config(tmpdir, 17.0, 11.0)\n        config = ReloadableConfig(self.write_importing_config(tmpdir)).get_config()\n\n        # then\n        assert len(config) == 2\n        assert config[\"firstValueMultiplied\"] == 34.0\n        assert config[\"secondValueMultiplied\"] == 33.0\n\n    def test_should_reevaluate_if_other_config_file_changed(self, tmpdir):\n        # given\n        reloadable_config = ReloadableConfig(self.write_importing_config(tmpdir))\n\n        # when\n        self.write_global_config(tmpdir, 17.0, 11.0)\n        config = reloadable_config.get_config()\n\n        # then\n        assert len(config) == 2\n        assert config[\"firstValueMultiplied\"] == 34.0\n        assert config[\"secondValueMultiplied\"] == 33.0\n\n        # when\n        self.write_global_config(tmpdir, 18.0, 3.0)\n        config = reloadable_config.get_config()\n\n        # then\n        assert len(config) == 2\n        assert config[\"firstValueMultiplied\"] == 36.0\n        assert config[\"secondValueMultiplied\"] == 9.0\n"
  },
  {
    "path": "tests/test_sai.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2017-2018 reverendus\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nimport pytest\n\nfrom pymaker import Address\nfrom pymaker.deployment import Deployment\nfrom pymaker.feed import DSValue\nfrom pymaker.numeric import Wad, Ray\nfrom pymaker.sai import Tub, Tap, Top, Vox\nfrom tests.helpers import time_travel_by\n\n\nclass TestTub:\n    def test_fail_when_no_contract_under_that_address(self, deployment: Deployment):\n        # expect\n        with pytest.raises(Exception):\n            Tub(web3=deployment.web3, address=Address('0xdeadadd1e5500000000000000000000000000000'))\n\n    def test_tap(self, deployment: Deployment):\n        assert deployment.tub.tap() == deployment.tap.address\n\n    @pytest.mark.skip(reason=\"flaky test failures due to underlying node issue with evm_mine\")\n    def test_era(self, deployment: Deployment):\n        # when\n        era = deployment.tub.era()\n        deployment.web3.manager.request_blocking(\"evm_mine\", [])\n\n        # then\n        assert era == deployment.web3.eth.getBlock('latest').timestamp\n\n    def test_join_and_pie_and_exit(self, deployment: Deployment):\n        # given\n        assert deployment.skr.balance_of(deployment.our_address) == Wad(0)\n        assert deployment.skr.total_supply() == Wad(0)\n\n        # when\n        deployment.tub.join(Wad.from_number(5)).transact()\n\n        # then\n        assert deployment.skr.balance_of(deployment.our_address) == Wad.from_number(5)\n        assert deployment.skr.total_supply() == Wad.from_number(5)\n        assert deployment.tub.pie() == Wad.from_number(5)\n\n        # when\n        deployment.tub.exit(Wad.from_number(4)).transact()\n\n        # then\n        assert deployment.skr.balance_of(deployment.our_address) == Wad.from_number(1)\n        assert deployment.skr.total_supply() == Wad.from_number(1)\n        assert deployment.tub.pie() == Wad.from_number(1)\n\n    def test_mold_cap_and_cap(self, deployment: Deployment):\n        # given\n        assert deployment.tub.cap() == Wad(0)\n\n        # when\n        deployment.tub.mold_cap(Wad.from_number(150000)).transact()\n\n        # then\n        assert deployment.tub.cap() == Wad.from_number(150000)\n\n    def test_mold_tax_and_tax(self, deployment: Deployment):\n        # given\n        assert deployment.tub.tax() == Ray.from_number(1)\n\n        # when\n        deployment.tub.mold_tax(Ray(1000000000000000020000000000)).transact()\n\n        # then\n        assert deployment.tub.tax() == Ray(1000000000000000020000000000)\n\n    def test_mold_mat_and_mat(self, deployment: Deployment):\n        # given\n        assert deployment.tub.mat() == Ray.from_number(1)\n\n        # when\n        deployment.tub.mold_mat(Ray.from_number(1.5)).transact()\n\n        # then\n        assert deployment.tub.mat() == Ray.from_number(1.5)\n\n    def test_mold_axe_and_axe(self, deployment: Deployment):\n        # given\n        assert deployment.tub.axe() == Ray.from_number(1)\n        deployment.tub.mold_mat(Ray.from_number(1.5)).transact()\n\n        # when\n        deployment.tub.mold_axe(Ray.from_number(1.2)).transact()\n\n        # then\n        assert deployment.tub.axe() == Ray.from_number(1.2)\n\n    def test_sai(self, deployment: Deployment):\n        assert deployment.tub.sai() == deployment.sai.address\n\n    def test_sin(self, deployment: Deployment):\n        assert deployment.tub.sin() == deployment.sin.address\n\n    def test_gem(self, deployment: Deployment):\n        assert deployment.tub.gem() == deployment.gem.address\n\n    def test_skr(self, deployment: Deployment):\n        assert deployment.tub.skr() == deployment.skr.address\n\n    def test_gov(self, deployment: Deployment):\n        assert deployment.tub.gov() == deployment.gov.address\n\n    def test_vox(self, deployment: Deployment):\n        assert deployment.tub.vox() == deployment.vox.address\n\n    def test_pip_and_pep(self, deployment: Deployment):\n        assert isinstance(deployment.tub.pip(), Address)\n        assert isinstance(deployment.tub.pep(), Address)\n\n    def test_pit(self, deployment: Deployment):\n        assert isinstance(deployment.tub.pit(), Address)\n\n    def test_per(self, deployment: Deployment):\n        assert deployment.tub.per() == Ray.from_number(1.0)\n\n    def test_tag(self, deployment: Deployment):\n        # when\n        DSValue(web3=deployment.web3, address=deployment.tub.pip()).poke_with_int(Wad.from_number(250.45).value).transact()\n\n        # then\n        assert deployment.tub.tag() == Ray.from_number(250.45)\n\n    def test_drip_and_chi_and_rho(self, deployment: Deployment):\n        # given\n        deployment.tub.mold_tax(Ray(1000000000000000020000000000)).transact()\n        old_chi = deployment.tub.chi()\n        old_rho = deployment.tub.rho()\n\n        # when\n        time_travel_by(deployment.web3, 1000)\n        deployment.tub.drip().transact()\n\n        # then\n        assert deployment.tub.chi() > old_chi\n        assert deployment.tub.rho() > old_rho\n\n    def test_open_and_cupi(self, deployment: Deployment):\n        # when\n        deployment.tub.open().transact()\n\n        # then\n        assert deployment.tub.cupi() == 1\n\n    def test_cups(self, deployment: Deployment):\n        # when\n        deployment.tub.open().transact()\n\n        # then\n        assert deployment.tub.cups(1).art == Wad(0)\n        assert deployment.tub.cups(1).ink == Wad(0)\n        assert deployment.tub.cups(1).lad == deployment.our_address\n\n    def test_not_empty_cups(self, deployment: Deployment):\n        # given\n        deployment.tub.join(Wad.from_number(10)).transact()\n        deployment.tub.mold_cap(Wad.from_number(100000)).transact()\n        DSValue(web3=deployment.web3, address=deployment.tub.pip()).poke_with_int(Wad.from_number(250.45).value).transact()\n\n        # and\n        deployment.tub.open().transact()\n        deployment.tub.lock(1, Wad.from_number(3)).transact()\n\n        # when\n        deployment.tub.draw(1, Wad.from_number(50)).transact()\n\n        # then\n        assert deployment.tub.cups(1).art == Wad.from_number(50)\n        assert deployment.tub.cups(1).ink == Wad.from_number(3)\n\n    def test_safe(self, deployment: Deployment):\n        # given\n        deployment.tub.mold_mat(Ray.from_number(1.5)).transact()\n        deployment.tub.mold_axe(Ray.from_number(1.2)).transact()\n        DSValue(web3=deployment.web3, address=deployment.tub.pip()).poke_with_int(Wad.from_number(250).value).transact()\n\n        # when\n        deployment.tub.open().transact()\n\n        # then\n        assert deployment.tub.safe(1)\n\n    def test_ink(self, deployment: Deployment):\n        # when\n        deployment.tub.open().transact()\n\n        # then\n        assert deployment.tub.ink(1) == Wad(0)\n\n    def test_lad(self, deployment: Deployment):\n        # when\n        deployment.tub.open().transact()\n\n        # then\n        assert deployment.tub.lad(1) == deployment.our_address\n\n    def test_give(self, deployment: Deployment):\n        # given\n        deployment.tub.open().transact()\n\n        # when\n        deployment.tub.give(1, Address('0x0101010101020202020203030303030404040404')).transact()\n\n        # then\n        assert deployment.tub.lad(1) == Address('0x0101010101020202020203030303030404040404')\n\n    def test_shut(self, deployment: Deployment):\n        # given\n        DSValue(web3=deployment.web3, address=deployment.tub.pip()).poke_with_int(Wad.from_number(250).value).transact()\n        deployment.tub.open().transact()\n\n        # when\n        deployment.tub.shut(1).transact()\n\n        # then\n        assert deployment.tub.lad(1) == Address('0x0000000000000000000000000000000000000000')\n\n    def test_lock_and_free(self, deployment: Deployment):\n        # given\n        DSValue(web3=deployment.web3, address=deployment.tub.pip()).poke_with_int(Wad.from_number(250).value).transact()\n        deployment.tub.open().transact()\n        deployment.tub.join(Wad.from_number(10)).transact()\n\n        # when\n        print(deployment.tub.cupi())\n        deployment.tub.lock(1, Wad.from_number(5)).transact()\n\n        # then\n        assert deployment.tub.ink(1) == Wad.from_number(5)\n        assert deployment.tub.air() == Wad.from_number(5)\n\n        # when\n        deployment.tub.free(1, Wad.from_number(3)).transact()\n\n        # then\n        assert deployment.tub.ink(1) == Wad.from_number(2)\n        assert deployment.tub.air() == Wad.from_number(2)\n\n    def test_draw_and_tab_and_din_and_wipe(self, deployment: Deployment):\n        # given\n        deployment.tub.join(Wad.from_number(10)).transact()\n        deployment.tub.mold_cap(Wad.from_number(100000)).transact()\n        DSValue(web3=deployment.web3, address=deployment.tub.pip()).poke_with_int(Wad.from_number(250.45).value).transact()\n\n        # and\n        deployment.tub.open().transact()\n        deployment.tub.lock(1, Wad.from_number(5)).transact()\n\n        # when\n        deployment.tub.draw(1, Wad.from_number(50)).transact()\n\n        # then\n        assert deployment.sai.balance_of(deployment.our_address) == Wad.from_number(50)\n        assert deployment.tub.tab(1) == Wad.from_number(50)\n        assert deployment.tub.din() == Wad.from_number(50)\n\n        # when\n        deployment.tub.wipe(1, Wad.from_number(30)).transact()\n\n        # then\n        assert deployment.sai.balance_of(deployment.our_address) == Wad.from_number(20)\n        assert deployment.tub.tab(1) == Wad.from_number(20)\n        assert deployment.tub.din() == Wad.from_number(20)\n\n    def test_bite_and_safe(self, deployment: Deployment):\n        # given\n        deployment.tub.join(Wad.from_number(10)).transact()\n        deployment.tub.mold_cap(Wad.from_number(100000)).transact()\n        DSValue(web3=deployment.web3, address=deployment.tub.pip()).poke_with_int(Wad.from_number(250).value).transact()\n\n        # when\n        deployment.tub.open().transact()\n        deployment.tub.lock(1, Wad.from_number(4)).transact()\n        deployment.tub.draw(1, Wad.from_number(1000)).transact()\n\n        # then\n        assert deployment.tub.safe(1)\n\n        # when\n        DSValue(web3=deployment.web3, address=deployment.tub.pip()).poke_with_int(Wad.from_number(150).value).transact()\n\n        # then\n        assert not deployment.tub.safe(1)\n\n        # when\n        deployment.tub.bite(1).transact()\n\n        # then\n        assert deployment.tub.safe(1)\n\n    def test_mold_gap_and_gap(self, deployment: Deployment):\n        # given\n        assert deployment.tub.gap() == Wad.from_number(1)\n\n        # when\n        deployment.tub.mold_gap(Wad.from_number(1.05)).transact()\n\n        # then\n        assert deployment.tub.gap() == Wad.from_number(1.05)\n\n    def test_bid_and_ask(self, deployment: Deployment):\n        # when\n        deployment.tub.mold_gap(Wad.from_number(1.05)).transact()\n\n        # then\n        assert deployment.tub.bid(Wad.from_number(2)) == Wad.from_number(0.95)*Wad.from_number(2)\n        assert deployment.tub.ask(Wad.from_number(2)) == Wad.from_number(1.05)*Wad.from_number(2)\n\n    def test_comparison(self, deployment: Deployment):\n        # expect\n        assert deployment.tub == deployment.tub\n        assert deployment.tub == Tub(web3=deployment.web3, address=deployment.tub.address)\n\n\nclass TestTap:\n    def test_fail_when_no_contract_under_that_address(self, deployment: Deployment):\n        # expect\n        with pytest.raises(Exception):\n            Tap(web3=deployment.web3, address=Address('0xdeadadd1e5500000000000000000000000000000'))\n\n    def test_tub(self, deployment: Deployment):\n        assert deployment.tap.tub() == deployment.tub.address\n\n    def test_sai(self, deployment: Deployment):\n        assert deployment.tap.sai() == deployment.sai.address\n\n    def test_sin(self, deployment: Deployment):\n        assert deployment.tap.sin() == deployment.sin.address\n\n    def test_skr(self, deployment: Deployment):\n        assert deployment.tap.skr() == deployment.skr.address\n\n    def test_mold_gap_and_gap(self, deployment: Deployment):\n        # given\n        assert deployment.tap.gap() == Wad.from_number(1)\n\n        # when\n        deployment.tap.mold_gap(Wad.from_number(1.05)).transact()\n\n        # then\n        assert deployment.tap.gap() == Wad.from_number(1.05)\n\n    def test_woe(self, deployment: Deployment):\n        assert deployment.tap.woe() == Wad(0)\n\n    def test_fog(self, deployment: Deployment):\n        assert deployment.tap.fog() == Wad(0)\n\n    def test_joy(self, deployment: Deployment):\n        assert deployment.tap.joy() == Wad(0)\n\n    def test_s2s_and_bid_and_ask(self, deployment: Deployment):\n        # when\n        DSValue(web3=deployment.web3, address=deployment.tub.pip()).poke_with_int(Wad.from_number(500).value).transact()\n        deployment.tap.mold_gap(Wad.from_number(1.05)).transact()\n\n        # then\n        assert deployment.tap.bid(Wad.from_number(2)) == Wad.from_number(475)*Wad.from_number(2)\n        assert deployment.tap.s2s() == Ray.from_number(500)\n        assert deployment.tap.ask(Wad.from_number(2)) == Wad.from_number(525)*Wad.from_number(2)\n\n    def test_joy_and_boom(self, deployment: Deployment):\n        # given\n        deployment.tub.join(Wad.from_number(10)).transact()\n        deployment.tub.mold_cap(Wad.from_number(100000)).transact()\n        deployment.tub.mold_tax(Ray(1000100000000000000000000000)).transact()\n        DSValue(web3=deployment.web3, address=deployment.tub.pip()).poke_with_int(Wad.from_number(250).value).transact()\n\n        # and\n        deployment.tub.open().transact()\n        deployment.tub.lock(1, Wad.from_number(4)).transact()\n        deployment.tub.draw(1, Wad.from_number(1000)).transact()\n\n        # and\n        assert deployment.tap.joy() == Wad(0)\n\n        # when\n        time_travel_by(deployment.web3, 3600)\n        deployment.tub.drip().transact()\n\n        # then\n        assert deployment.skr.balance_of(deployment.our_address) == Wad.from_number(6)\n        assert deployment.tap.joy() > Wad(0)\n        prev_joy = deployment.tap.joy()\n\n        # when\n        deployment.tap.boom(Wad.from_number(1)).transact()\n\n        # then\n        assert deployment.skr.balance_of(deployment.our_address) == Wad.from_number(5)\n        assert Wad(0) < deployment.tap.joy() < prev_joy\n\n    def test_fog_and_woe_and_bust(self, deployment: Deployment):\n        # given\n        deployment.tub.join(Wad.from_number(10)).transact()\n        deployment.tub.mold_cap(Wad.from_number(100000)).transact()\n        DSValue(web3=deployment.web3, address=deployment.tub.pip()).poke_with_int(Wad.from_number(250).value).transact()\n\n        # and\n        deployment.tub.open().transact()\n        deployment.tub.lock(1, Wad.from_number(4)).transact()\n        deployment.tub.draw(1, Wad.from_number(1000)).transact()\n\n        # and\n        DSValue(web3=deployment.web3, address=deployment.tub.pip()).poke_with_int(Wad.from_number(150).value).transact()\n\n        # when\n        deployment.tub.bite(1).transact()\n\n        # then\n        assert deployment.tap.fog() == Wad.from_number(4)\n        assert deployment.tap.woe() == Wad.from_number(1000)\n        assert deployment.skr.balance_of(deployment.our_address) == Wad.from_number(6)\n        assert deployment.sai.balance_of(deployment.our_address) == Wad.from_number(1000)\n\n        # when\n        deployment.tap.bust(Wad.from_number(2)).transact()\n        assert deployment.tap.fog() == Wad.from_number(2)\n        assert deployment.tap.woe() == Wad.from_number(700)\n        assert deployment.skr.balance_of(deployment.our_address) == Wad.from_number(8)\n        assert deployment.sai.balance_of(deployment.our_address) == Wad.from_number(700)\n\n    def test_cash(self, deployment: Deployment):\n        # given\n        deployment.tub.join(Wad.from_number(10)).transact()\n        deployment.tub.mold_cap(Wad.from_number(100000)).transact()\n        DSValue(web3=deployment.web3, address=deployment.tub.pip()).poke_with_int(Wad.from_number(250).value).transact()\n\n        # and\n        deployment.tub.open().transact()\n        deployment.tub.lock(1, Wad.from_number(4)).transact()\n        deployment.tub.draw(1, Wad.from_number(1000)).transact()\n\n        # and\n        gem_before = deployment.gem.balance_of(deployment.our_address)\n\n        # when\n        deployment.top.cage().transact()\n        deployment.tap.cash(Wad.from_number(500)).transact()\n\n        # then\n        gem_after = deployment.gem.balance_of(deployment.our_address)\n        assert gem_after - gem_before == Wad.from_number(2)\n\n        # when\n        deployment.tap.cash(Wad.from_number(500)).transact()\n\n        # then\n        gem_after = deployment.gem.balance_of(deployment.our_address)\n        assert gem_after - gem_before == Wad.from_number(4)\n\n    def test_mock(self, deployment: Deployment):\n        # given\n        deployment.tub.join(Wad.from_number(10)).transact()\n        deployment.tub.mold_cap(Wad.from_number(100000)).transact()\n        DSValue(web3=deployment.web3, address=deployment.tub.pip()).poke_with_int(Wad.from_number(250).value).transact()\n\n        # and\n        deployment.tub.open().transact()\n        deployment.tub.lock(1, Wad.from_number(4)).transact()\n        deployment.tub.draw(1, Wad.from_number(1000)).transact()\n\n        # and\n        gem_before = deployment.gem.balance_of(deployment.our_address)\n\n        # when\n        deployment.top.cage().transact()\n        deployment.tap.cash(Wad.from_number(500)).transact()\n        deployment.tap.mock(Wad.from_number(250)).transact()\n\n        # then\n        gem_after = deployment.gem.balance_of(deployment.our_address)\n        assert gem_after - gem_before == Wad.from_number(1)\n\n    def test_comparison(self, deployment: Deployment):\n        # expect\n        assert deployment.tap == deployment.tap\n        assert deployment.tap == Tap(web3=deployment.web3, address=deployment.tap.address)\n        assert deployment.tap != Tap(web3=deployment.web3, address=deployment.tub.address)\n\n\nclass TestTop:\n    def test_fail_when_no_contract_under_that_address(self, deployment: Deployment):\n        # expect\n        with pytest.raises(Exception):\n            Top(web3=deployment.web3, address=Address('0xdeadadd1e5500000000000000000000000000000'))\n\n    def test_comparison(self, deployment: Deployment):\n        # expect\n        assert deployment.top == deployment.top\n        assert deployment.top == Top(web3=deployment.web3, address=deployment.top.address)\n        assert deployment.top != Top(web3=deployment.web3, address=deployment.tub.address)\n\n    def test_default_fix(self, deployment: Deployment):\n        # expect\n        assert deployment.top.fix() == Ray.from_number(0)\n\n    def test_cage(self, deployment: Deployment):\n        # given\n        deployment.tub.join(Wad.from_number(10)).transact()\n        deployment.tub.mold_cap(Wad.from_number(100000)).transact()\n        DSValue(web3=deployment.web3, address=deployment.tub.pip()).poke_with_int(Wad.from_number(250).value).transact()\n\n        # and\n        deployment.tub.open().transact()\n        deployment.tub.lock(1, Wad.from_number(4)).transact()\n        deployment.tub.draw(1, Wad.from_number(1000)).transact()\n\n        # when\n        deployment.top.cage().transact()\n\n        # then\n        assert deployment.top.fix() == Ray.from_number(0.004)\n\n\nclass TestVox:\n    def test_fail_when_no_contract_under_that_address(self, deployment: Deployment):\n        # expect\n        with pytest.raises(Exception):\n            Vox(web3=deployment.web3, address=Address('0xdeadadd1e5500000000000000000000000000000'))\n\n    def test_comparison(self, deployment: Deployment):\n        # expect\n        assert deployment.vox == deployment.vox\n        assert deployment.vox == Vox(web3=deployment.web3, address=deployment.vox.address)\n        assert deployment.vox != Vox(web3=deployment.web3, address=deployment.top.address)\n\n    @pytest.mark.skip(reason=\"flaky test failures due to underlying node issue with evm_mine\")\n    def test_era(self, deployment: Deployment):\n        # when\n        era = deployment.vox.era()\n        deployment.web3.manager.request_blocking(\"evm_mine\", [])\n\n        # then\n        assert era == deployment.web3.eth.getBlock('latest').timestamp\n\n    def test_default_par(self, deployment: Deployment):\n        # expect\n        assert deployment.vox.par() == Ray.from_number(1)\n"
  },
  {
    "path": "tests/test_savings.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2019 grandizzy\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nimport pytest\n\nfrom pymaker import Address\nfrom pymaker.deployment import DssDeployment\nfrom pymaker.dsr import Dsr\nfrom pymaker.numeric import Wad, Ray, Rad\n\nfrom tests.test_dss import wrap_eth, frob\n\n\n@pytest.fixture\ndef dsr(our_address: Address, mcd: DssDeployment) -> Dsr:\n    return Dsr(mcd, our_address)\n\n\n@pytest.mark.dependency()\ndef test_proxy(dsr):\n    assert dsr.has_proxy() is False\n\n    dsr.build_proxy().transact()\n    assert dsr.has_proxy() is True\n\n\n@pytest.mark.dependency(depends=['test_proxy'])\ndef test_join_and_exit(dsr):\n    proxy = dsr.get_proxy()\n    assert dsr.get_balance(proxy.address) == Wad.from_number(0)\n\n    mcd = dsr.mcd\n\n    # create a vault\n    collateral = mcd.collaterals['ETH-C']\n    wrap_eth(mcd, dsr.owner, Wad.from_number(2))\n    collateral.approve(dsr.owner)\n    assert collateral.adapter.join(dsr.owner, Wad.from_number(2)).transact(from_address=dsr.owner)\n    frob(mcd, collateral, dsr.owner, dink=Wad.from_number(2), dart=Wad(0))\n    dart = Wad.from_number(100)\n    frob(mcd, collateral, dsr.owner, dink=Wad(0), dart=dart)\n\n    # mint and withdraw all the Dai\n    mcd.approve_dai(dsr.owner)\n    assert mcd.dai_adapter.exit(dsr.owner, dart).transact(from_address=dsr.owner)\n    assert mcd.dai.balance_of(dsr.owner) == dart\n\n    initial_dai_balance = mcd.dai.balance_of(dsr.owner)\n    assert initial_dai_balance >= Wad.from_number(100)\n    assert dsr.get_balance(proxy.address) == Wad.from_number(0)\n\n    # approve Proxy to use 100 DAI from account\n    mcd.dai.approve(proxy.address, Wad.from_number(100)).transact(from_address=dsr.owner)\n\n    # join 100 DAI in DSR\n    assert dsr.join(Wad.from_number(100), proxy).transact(from_address=dsr.owner)\n    assert mcd.dai.balance_of(dsr.owner) == initial_dai_balance - Wad.from_number(100)\n    assert round(dsr.get_balance(proxy.address)) == Wad.from_number(100)\n    assert mcd.pot.drip().transact()\n\n    # exit 33 DAI from DSR\n    assert dsr.exit(Wad.from_number(33), proxy).transact(from_address=dsr.owner)\n    assert round(mcd.dai.balance_of(dsr.owner)) == round(initial_dai_balance) - Wad.from_number(100) + Wad.from_number(33)\n    assert round(dsr.get_balance(proxy.address)) == Wad.from_number(67)\n    assert mcd.pot.drip().transact()\n\n    # exit remaining DAI from DSR and join to vat\n    assert dsr.exit_all(proxy).transact(from_address=dsr.owner)\n    assert round(mcd.dai.balance_of(dsr.owner)) == round(initial_dai_balance)\n    assert dsr.get_balance(proxy.address) == Wad.from_number(0)\n    assert mcd.dai_adapter.join(dsr.owner, mcd.dai.balance_of(dsr.owner)).transact(from_address=dsr.owner)\n\n    # repay the vault\n    assert collateral.ilk.dust == Rad(0)\n    wipe: Wad = mcd.vat.get_wipe_all_dart(collateral.ilk, dsr.owner)\n    frob(mcd, collateral, dsr.owner, dink=Wad(0), dart=wipe*-1)\n"
  },
  {
    "path": "tests/test_shutdown.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2019 EdNoepel\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n\nimport pytest\nfrom datetime import datetime, timedelta\n\nfrom pymaker import Address\nfrom pymaker.approval import directly\nfrom pymaker.deployment import Collateral, DssDeployment\nfrom pymaker.numeric import Wad, Ray, Rad\nfrom pymaker.shutdown import ShutdownModule, End\n\nfrom tests.helpers import time_travel_by\nfrom tests.test_auctions import create_surplus\nfrom tests.test_dss import mint_mkr, wrap_eth, frob\n\n\ndef open_cdp(mcd: DssDeployment, collateral: Collateral, address: Address):\n    assert isinstance(mcd, DssDeployment)\n    assert isinstance(collateral, Collateral)\n    assert isinstance(address, Address)\n\n    collateral.approve(address)\n    wrap_eth(mcd, address, Wad.from_number(10))\n    assert collateral.adapter.join(address, Wad.from_number(10)).transact(from_address=address)\n    frob(mcd, collateral, address, Wad.from_number(10), Wad.from_number(20))\n\n    assert mcd.vat.debt() >= Rad(Wad.from_number(20))\n    assert mcd.vat.dai(address) >= Rad.from_number(10)\n\n\ndef create_flap_auction(mcd: DssDeployment, deployment_address: Address, our_address: Address):\n    assert isinstance(mcd, DssDeployment)\n    assert isinstance(deployment_address, Address)\n    assert isinstance(our_address, Address)\n\n    flapper = mcd.flapper\n    create_surplus(mcd, flapper, deployment_address)\n    joy = mcd.vat.dai(mcd.vow.address)\n    assert joy > mcd.vat.sin(mcd.vow.address) + mcd.vow.bump() + mcd.vow.hump()\n    assert (mcd.vat.sin(mcd.vow.address) - mcd.vow.sin()) - mcd.vow.ash() == Rad(0)\n    assert mcd.vow.flap().transact()\n\n    mint_mkr(mcd.mkr, our_address, Wad.from_number(10))\n    flapper.approve(mcd.mkr.address, directly(from_address=our_address))\n    bid = Wad.from_number(0.001)\n    assert mcd.mkr.balance_of(our_address) > bid\n    assert flapper.tend(flapper.kicks(), mcd.vow.bump(), bid).transact(from_address=our_address)\n\n\nnobody = Address(\"0x0000000000000000000000000000000000000000\")\n\n\nclass TestShutdownModule:\n    \"\"\"This test must be run after other MCD tests because it will leave the testchain `cage`d.\"\"\"\n\n    def test_init(self, mcd, deployment_address, our_address):\n        assert mcd.esm is not None\n        assert isinstance(mcd.esm, ShutdownModule)\n        assert isinstance(mcd.esm.address, Address)\n        assert mcd.esm.sum() == Wad(0)\n        assert mcd.esm.min() > Wad(0)\n        assert mcd.end.live()\n\n        joy = mcd.vat.dai(mcd.vow.address)\n        awe = mcd.vat.sin(mcd.vow.address)\n        # If `test_shutdown.py` is run in isolation, create a flap auction to exercise `yank`\n        if joy == Rad(0) and awe == Rad(0):\n            create_flap_auction(mcd, deployment_address, our_address)\n\n    def test_join(self, mcd, our_address):\n        assert mcd.mkr.approve(mcd.esm.address).transact()\n\n        # This should have no effect yet succeed regardless\n        assert mcd.esm.join(Wad(0)).transact()\n        assert mcd.esm.sum() == Wad(0)\n        assert mcd.esm.sum_of(our_address) == Wad(0)\n\n        # Ensure the appropriate amount of MKR can be joined\n        mint_mkr(mcd.mkr, our_address, mcd.esm.min())\n        assert mcd.esm.join(mcd.esm.min()).transact()\n        assert mcd.esm.sum() == mcd.esm.min()\n\n        # Joining extra MKR should succeed yet have no effect\n        mint_mkr(mcd.mkr, our_address, Wad(153))\n        assert mcd.esm.join(Wad(153)).transact()\n        assert mcd.esm.sum() == mcd.esm.min() + Wad(153)\n        assert mcd.esm.sum_of(our_address) == mcd.esm.sum()\n\n    def test_fire(self, mcd, our_address):\n        open_cdp(mcd, mcd.collaterals['ETH-A'], our_address)\n        assert mcd.end.live()\n        assert mcd.esm.fire().transact()\n        assert not mcd.end.live()\n\n\nclass TestEnd:\n    \"\"\"This test must be run after TestShutdownModule, which calls `esm.fire`.\"\"\"\n\n    def test_init(self, mcd):\n        assert mcd.end is not None\n        assert isinstance(mcd.end, End)\n        assert isinstance(mcd.esm.address, Address)\n\n    def test_getters(self, mcd):\n        assert not mcd.end.live()\n        assert datetime.utcnow() - timedelta(minutes=5) < mcd.end.when() < datetime.utcnow()\n        assert mcd.end.wait() >= 0\n        assert mcd.end.debt() >= Rad(0)\n\n        for collateral in mcd.collaterals.values():\n            ilk = collateral.ilk\n            assert mcd.end.tag(ilk) == Ray(0)\n            assert mcd.end.gap(ilk) == Wad(0)\n            assert mcd.end.art(ilk) == Wad(0)\n            assert mcd.end.fix(ilk) == Ray(0)\n\n    def test_cage(self, mcd):\n        collateral = mcd.collaterals['ETH-A']\n        ilk = collateral.ilk\n\n        assert mcd.end.cage(ilk).transact()\n        assert mcd.end.art(ilk) > Wad(0)\n        assert mcd.end.tag(ilk) > Ray(0)\n\n    def test_yank(self, mcd):\n        last_flap = mcd.flapper.bids(mcd.flapper.kicks())\n        last_flop = mcd.flopper.bids(mcd.flopper.kicks())\n        if last_flap.end > 0 and last_flap.guy is not nobody:\n            auction = mcd.flapper\n        elif last_flop.end > 0 and last_flop.guy is not nobody:\n            auction = mcd.flopper\n        else:\n            auction = None\n\n        if auction:\n            print(f\"active {auction} auction: {auction.bids(auction.kicks())}\")\n            assert not auction.live()\n            kick = auction.kicks()\n            assert auction.yank(kick).transact()\n            assert auction.bids(kick).guy == nobody\n\n    def test_skim(self, mcd, our_address):\n        ilk = mcd.collaterals['ETH-A'].ilk\n\n        urn = mcd.vat.urn(ilk, our_address)\n        owe = Ray(urn.art) * mcd.vat.ilk(ilk.name).rate * mcd.end.tag(ilk)\n        assert owe > Ray(0)\n        wad = min(Ray(urn.ink), owe)\n        print(f\"owe={owe} wad={wad}\")\n\n        assert mcd.end.skim(ilk, our_address).transact()\n        assert mcd.vat.urn(ilk, our_address).art == Wad(0)\n        assert mcd.vat.urn(ilk, our_address).ink > Wad(0)\n        assert mcd.vat.sin(mcd.vow.address) > Rad(0)\n\n        assert mcd.vat.debt() > Rad(0)\n        assert mcd.vat.vice() > Rad(0)\n\n    @pytest.mark.skip(reason=\"have to heal system before calling thaw\")\n    def test_close_cdp(self, web3, mcd, our_address):\n        collateral = mcd.collaterals['ETH-A']\n        ilk = collateral.ilk\n\n        assert mcd.end.free(ilk).transact()\n        assert mcd.vat.urn(ilk, our_address).ink == Wad(0)\n        assert mcd.vat.gem(ilk, our_address) > Wad(0)\n        assert collateral.adapter.exit(our_address, mcd.vat.gem(ilk, our_address)).transact()\n\n        assert mcd.end.wait() == 5\n        time_travel_by(web3, 5)\n        assert mcd.end.thaw().transact()\n        assert mcd.end.flow(ilk).transact()\n        assert mcd.end.fix(ilk) > Ray(0)\n\n    @pytest.mark.skip(reason=\"unable to add dai to the `bag`\")\n    def test_pack(self, mcd, our_address):\n        assert mcd.end.bag(our_address) == Wad(0)\n        assert mcd.end.debt() > Rad(0)\n        assert mcd.dai.approve(mcd.end.address).transact()\n        assert mcd.vat.dai(our_address) >= Rad.from_number(10)\n        # FIXME: `pack` fails, possibly because we're passing 0 to `vat.flux`\n        assert mcd.end.pack(Wad.from_number(10)).transact()\n        assert mcd.end.bag(our_address) == Wad.from_number(10)\n"
  },
  {
    "path": "tests/test_sign.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2017-2018 reverendus\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nimport pkg_resources\nfrom web3 import Web3, HTTPProvider\n\nfrom pymaker import Address\nfrom pymaker.keys import register_key_file\nfrom pymaker.sign import eth_sign\n\n\ndef test_signing():\n    # given\n    web3 = Web3(HTTPProvider(\"http://localhost:8555\"))\n    web3.eth.defaultAccount = web3.eth.accounts[0]\n\n    # and\n    text = \"abc\"\n    msg = bytes(text, 'utf-8')\n\n    # expect\n    assert eth_sign(msg, web3).startswith(\"0x\")\n\n\ndef test_signing_with_key_and_rpc_should_return_same_result():\n    # given\n    web3 = Web3(HTTPProvider(\"http://localhost:8555\"))\n    web3.eth.defaultAccount = web3.eth.accounts[0]\n\n    assert Address(web3.eth.defaultAccount) == Address('0x9596c16d7bf9323265c2f2e22f43e6c80eb3d943')\n\n    # and\n    text = \"abc\"\n    msg = bytes(text, 'utf-8')\n\n    rpc_signature = eth_sign(msg, web3)\n\n    # when\n    keyfile_path = pkg_resources.resource_filename(__name__, \"accounts/0_0x9596c16d7bf9323265c2f2e22f43e6c80eb3d943.json\")\n    passfile_path = pkg_resources.resource_filename(__name__, \"accounts/pass\")\n\n    register_key_file(web3, keyfile_path, passfile_path)\n\n    # and\n    # [we do this in order to make sure that the message was signed using the local key]\n    # [with `request_blocking` set to `None` any http requests will basically fail]\n    web3.manager.request_blocking = None\n\n    # and\n    local_signature = eth_sign(msg, web3)\n\n    # then\n    assert rpc_signature == local_signature\n"
  },
  {
    "path": "tests/test_token.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2017-2018 reverendus\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nimport pytest\nfrom pymaker import Address\nfrom pymaker.numeric import Wad\nfrom pymaker.util import synchronize\nfrom web3 import HTTPProvider\nfrom web3 import Web3\n\nfrom pymaker.token import DSToken, DSEthToken, ERC20Token\n\n\nclass TestERC20Token:\n    def setup_method(self):\n        self.web3 = Web3(HTTPProvider(\"http://localhost:8555\"))\n        self.web3.eth.defaultAccount = self.web3.eth.accounts[0]\n        self.our_address = Address(self.web3.eth.defaultAccount)\n        self.second_address = Address(self.web3.eth.accounts[1])\n        self.third_address = Address(self.web3.eth.accounts[2])\n        self.token = DSToken.deploy(self.web3, 'ABC')\n        self.token.mint(Wad(1000000)).transact()\n\n    def test_fail_when_no_token_with_that_address(self):\n        with pytest.raises(Exception):\n            ERC20Token(web3=self.web3, address=Address('0x0123456789012345678901234567890123456789'))\n\n    def test_symbol_for_dstoken_which_returns_bytes32(self):\n        assert self.token.symbol() == 'ABC'\n\n    def test_total_supply(self):\n        assert self.token.total_supply() == Wad(1000000)\n\n    def test_balance_of(self):\n        assert self.token.balance_of(self.our_address) == Wad(1000000)\n        assert self.token.balance_of(self.second_address) == Wad(0)\n\n    def test_balance_at_block(self):\n        \"\"\" This test relies on ganache creating a new block for every transaction by default \"\"\"\n\n        starting_block = int(self.web3.eth.getBlock('latest')['number'])\n\n        # check balance before minting\n        assert self.token.balance_at_block(self.our_address, starting_block - 1) == Wad(0)\n\n        # check balance after minting\n        assert self.token.balance_at_block(self.our_address, starting_block) == Wad(1000000)\n\n        self.token.transfer(self.second_address, Wad(500)).transact()\n\n        # check balance after additional transfer\n        assert self.token.balance_at_block(self.our_address, starting_block + 1) == Wad(999500)\n        assert self.token.balance_at_block(self.our_address, starting_block) == Wad(1000000)\n\n    def test_transfer(self):\n        # when\n        receipt = self.token.transfer(self.second_address, Wad(500)).transact()\n\n        # then\n        assert receipt is not None\n        assert self.token.balance_of(self.our_address) == Wad(999500)\n        assert self.token.balance_of(self.second_address) == Wad(500)\n\n    def test_transfer_async(self):\n        # when\n        receipt = synchronize([self.token.transfer(self.second_address, Wad(750)).transact_async()])\n\n        # then\n        assert receipt is not None\n        assert self.token.balance_of(self.our_address) == Wad(999250)\n        assert self.token.balance_of(self.second_address) == Wad(750)\n\n    def test_transfer_failed(self):\n        # when\n        receipt = self.token.transfer(self.second_address, Wad(5000000)).transact()\n\n        # then\n        assert receipt is None\n        assert self.token.balance_of(self.our_address) == Wad(1000000)\n        assert self.token.balance_of(self.second_address) == Wad(0)\n\n    def test_transfer_failed_async(self):\n        # when\n        receipt = synchronize([self.token.transfer(self.second_address, Wad(5000000)).transact_async()])[0]\n\n        # then\n        assert receipt is None\n        assert self.token.balance_of(self.our_address) == Wad(1000000)\n        assert self.token.balance_of(self.second_address) == Wad(0)\n\n    def test_transfer_out_of_gas(self):\n        # when\n        with pytest.raises(Exception):\n            self.token.transfer(self.second_address, Wad(500)).transact(gas=26000)\n\n        # then\n        assert self.token.balance_of(self.our_address) == Wad(1000000)\n        assert self.token.balance_of(self.second_address) == Wad(0)\n\n    def test_transfer_out_of_gas_async(self):\n        # when\n        with pytest.raises(Exception):\n            synchronize([self.token.transfer(self.second_address, Wad(500)).transact_async(gas=26000)])[0]\n\n        # then\n        assert self.token.balance_of(self.our_address) == Wad(1000000)\n        assert self.token.balance_of(self.second_address) == Wad(0)\n\n    def test_transfer_generates_transfer(self):\n        # when\n        receipt = self.token.transfer(self.second_address, Wad(500)).transact()\n\n        # then\n        assert len(receipt.transfers) == 1\n        assert receipt.transfers[0].token_address == self.token.address\n        assert receipt.transfers[0].from_address == self.our_address\n        assert receipt.transfers[0].to_address == self.second_address\n        assert receipt.transfers[0].value == Wad(500)\n\n    def test_transfer_from(self):\n        # given\n        self.token.approve(self.second_address).transact()\n\n        # when\n        self.token.transfer_from(self.our_address, self.third_address, Wad(500)).transact(from_address=self.second_address)\n\n        # then\n        assert self.token.balance_of(self.third_address) == Wad(500)\n\n    def test_allowance_of(self):\n        assert self.token.allowance_of(self.our_address, self.second_address) == Wad(0)\n\n    def test_approve(self):\n        # when\n        self.token.approve(self.second_address, Wad(2000)).transact()\n\n        # then\n        assert self.token.allowance_of(self.our_address, self.second_address) == Wad(2000)\n\n    def test_equals(self):\n        # given\n        token1 = DSToken.deploy(self.web3, 'ABC')\n        token2 = DSToken.deploy(self.web3, 'DEF')\n        token2b = ERC20Token(web3=self.web3, address=token2.address)\n\n        # expect\n        assert token1 == token1\n        assert token2 == token2b\n        assert not token1 == token2\n        assert not token1 == token2b\n\n    def test_should_have_printable_representation(self):\n        erc20token = ERC20Token(web3=self.web3, address=self.token.address)\n        assert repr(erc20token) == f\"ERC20Token('{erc20token.address}')\"\n\n\nclass TestDSToken:\n    def setup_method(self):\n        self.web3 = Web3(HTTPProvider(\"http://localhost:8555\"))\n        self.web3.eth.defaultAccount = self.web3.eth.accounts[0]\n        self.our_address = Address(self.web3.eth.defaultAccount)\n        self.second_address = Address(self.web3.eth.accounts[1])\n        self.dstoken = DSToken.deploy(self.web3, 'ABC')\n\n    def test_fail_when_no_contract_under_that_address(self):\n        # expect\n        with pytest.raises(Exception):\n            DSToken(web3=self.web3, address=Address('0xdeadadd1e5500000000000000000000000000000'))\n\n    def test_authority(self):\n        # given\n        some_address = Address('0x4545454545676767676789898989890101010101')\n\n        # when\n        self.dstoken.set_authority(some_address).transact()\n\n        # then\n        assert self.dstoken.authority() == some_address\n\n    def test_mint(self):\n        # when\n        self.dstoken.mint(Wad(100000)).transact()\n\n        # then\n        assert self.dstoken.balance_of(self.our_address) == Wad(100000)\n\n    def test_mint_to_other_address(self):\n        # when\n        self.dstoken.mint_to(self.second_address, Wad(100000)).transact()\n\n        # then\n        assert self.dstoken.balance_of(self.second_address) == Wad(100000)\n\n    def test_mint_generates_transfer(self):\n        # when\n        receipt = self.dstoken.mint(Wad(100000)).transact()\n\n        # then\n        assert len(receipt.transfers) == 1\n        assert receipt.transfers[0].token_address == self.dstoken.address\n        assert receipt.transfers[0].from_address == Address('0x0000000000000000000000000000000000000000')\n        assert receipt.transfers[0].to_address == self.our_address\n        assert receipt.transfers[0].value == Wad(100000)\n\n    def test_burn(self):\n        # given\n        self.dstoken.mint(Wad(100000)).transact()\n\n        # when\n        self.dstoken.burn(Wad(40000)).transact()\n\n        # then\n        assert self.dstoken.balance_of(self.our_address) == Wad(60000)\n\n    def test_burn_from_other_address(self):\n        # given\n        self.dstoken.mint_to(self.second_address, Wad(100000)).transact()\n\n        # when\n        self.dstoken.approve(self.our_address).transact(from_address=self.second_address)\n        self.dstoken.burn_from(self.second_address, Wad(40000)).transact()\n\n        # then\n        assert self.dstoken.balance_of(self.second_address) == Wad(60000)\n\n    def test_burn_generates_transfer(self):\n        # given\n        self.dstoken.mint(Wad(100000)).transact()\n\n        # when\n        receipt = self.dstoken.burn(Wad(40000)).transact()\n\n        # then\n        assert len(receipt.transfers) == 1\n        assert receipt.transfers[0].token_address == self.dstoken.address\n        assert receipt.transfers[0].from_address == self.our_address\n        assert receipt.transfers[0].to_address == Address('0x0000000000000000000000000000000000000000')\n        assert receipt.transfers[0].value == Wad(40000)\n\n    def test_should_have_printable_representation(self):\n        assert repr(self.dstoken) == f\"DSToken('{self.dstoken.address}')\"\n\n\nclass TestDSEthToken:\n    def setup_method(self):\n        self.web3 = Web3(HTTPProvider(\"http://localhost:8555\"))\n        self.web3.eth.defaultAccount = self.web3.eth.accounts[0]\n        self.our_address = Address(self.web3.eth.defaultAccount)\n        self.dsethtoken = DSEthToken.deploy(self.web3)\n\n    def test_fail_when_no_contract_under_that_address(self):\n        # expect\n        with pytest.raises(Exception):\n            DSEthToken(web3=self.web3, address=Address('0xdeadadd1e5500000000000000000000000000000'))\n\n    def test_deposit(self):\n        # when\n        self.dsethtoken.deposit(Wad(100000)).transact()\n\n        # then\n        assert self.dsethtoken.balance_of(self.our_address) == Wad(100000)\n\n    def test_withdraw(self):\n        # given\n        self.dsethtoken.deposit(Wad(100000)).transact()\n\n        # when\n        self.dsethtoken.withdraw(Wad(40000)).transact()\n\n        # then\n        assert self.dsethtoken.balance_of(self.our_address) == Wad(60000)\n\n    def test_should_have_printable_representation(self):\n        assert repr(self.dsethtoken) == f\"DSEthToken('{self.dsethtoken.address}')\"\n"
  },
  {
    "path": "tests/test_transactional.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2017-2018 reverendus\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nimport pytest\nfrom web3 import Web3, HTTPProvider\n\nfrom pymaker import Address\nfrom pymaker.approval import directly\nfrom pymaker.numeric import Wad\nfrom pymaker.token import DSToken\nfrom pymaker.transactional import TxManager\n\n\nclass TestTxManager:\n    def setup_method(self):\n        self.web3 = Web3(HTTPProvider(\"http://localhost:8555\"))\n        self.web3.eth.defaultAccount = self.web3.eth.accounts[0]\n        self.our_address = Address(self.web3.eth.defaultAccount)\n        self.other_address = Address(self.web3.eth.accounts[1])\n        self.tx = TxManager.deploy(self.web3)\n        self.token1 = DSToken.deploy(self.web3, 'ABC')\n        self.token1.mint(Wad.from_number(1000000)).transact()\n        self.token2 = DSToken.deploy(self.web3, 'DEF')\n        self.token2.mint(Wad.from_number(1000000)).transact()\n\n    def test_fail_when_no_contract_under_that_address(self):\n        # expect\n        with pytest.raises(Exception):\n            TxManager(web3=self.web3, address=Address('0xdeadadd1e5500000000000000000000000000000'))\n\n    def test_owner(self):\n        assert self.tx.owner() == self.our_address\n\n    def test_approve(self):\n        # given\n        assert self.token1.allowance_of(self.our_address, self.tx.address) == Wad(0)\n        assert self.token2.allowance_of(self.our_address, self.tx.address) == Wad(0)\n\n        # when\n        self.tx.approve([self.token1, self.token2], directly())\n\n        # then\n        assert self.token1.allowance_of(self.our_address, self.tx.address) == Wad(2**256-1)\n        assert self.token2.allowance_of(self.our_address, self.tx.address) == Wad(2**256-1)\n\n    def test_execute_zero_calls(self):\n        # given\n        self.tx.approve([self.token1], directly())\n\n        # when\n        res = self.tx.execute([self.token1.address], []).transact()\n\n        # then\n        assert res.successful\n\n    def test_execute_one_call(self):\n        # given\n        self.tx.approve([self.token1], directly())\n\n        # when\n        res = self.tx.execute([self.token1.address],\n                              [self.token1.transfer(self.other_address, Wad.from_number(500)).invocation()]).transact()\n\n        # then\n        assert res.successful\n        assert self.token1.balance_of(self.our_address) == Wad.from_number(999500)\n        assert self.token1.balance_of(self.other_address) == Wad.from_number(500)\n\n    def test_execute_one_call_fails_if_no_approval(self):\n        # given\n        # [no approval]\n\n        # when\n        res = self.tx.execute([self.token1.address],\n                              [self.token1.transfer(self.other_address, Wad.from_number(500)).invocation()]).transact()\n\n        # then\n        assert res is None\n        assert self.token1.balance_of(self.our_address) == Wad.from_number(1000000)\n        assert self.token1.balance_of(self.other_address) == Wad.from_number(0)\n\n    def test_execute_multiple_calls_with_multiple_tokens(self):\n        # given\n        self.tx.approve([self.token1, self.token2], directly())\n\n        # when\n        res = self.tx.execute([self.token1.address, self.token2.address],\n                              [self.token1.transfer(self.other_address, Wad.from_number(500)).invocation(),\n                               self.token1.transfer(self.other_address, Wad.from_number(200)).invocation(),\n                               self.token2.transfer(self.other_address, Wad.from_number(150)).invocation()]).transact()\n\n        # then\n        assert res.successful\n        assert self.token1.balance_of(self.our_address) == Wad.from_number(999300)\n        assert self.token1.balance_of(self.other_address) == Wad.from_number(700)\n        assert self.token2.balance_of(self.our_address) == Wad.from_number(999850)\n        assert self.token2.balance_of(self.other_address) == Wad.from_number(150)\n\n    def test_should_have_printable_representation(self):\n        assert repr(self.tx) == f\"TxManager('{self.tx.address}')\"\n"
  },
  {
    "path": "tests/test_util.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2017-2018 reverendus\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nimport asyncio\nimport time\nfrom unittest.mock import Mock, call\n\nimport pytest\nfrom web3 import Web3\n\nfrom pymaker import Address\nfrom pymaker.util import synchronize, int_to_bytes32, bytes_to_int, bytes_to_hexstring, hexstring_to_bytes, \\\n    AsyncCallback, chain\n\n\nasync def async_return(result):\n    return result\n\n\nasync def async_exception():\n    await asyncio.sleep(0.1)\n    raise Exception(\"Exception to be passed further down\")\n\n\ndef mocked_web3(block_0_hash: str) -> Web3:\n    def side_effect(block_number):\n        if block_number == 0:\n            return {'hash': block_0_hash}\n        else:\n            raise Exception(\"Unknown block number queried\")\n\n    web3 = Mock(Web3)\n    web3.eth = Mock()\n    web3.eth.getBlock = Mock(side_effect=side_effect)\n    return web3\n\n\ndef test_chain_should_recognize_ethlive():\n    # given\n    web3 = mocked_web3(block_0_hash=\"0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3\")\n\n    # expect\n    assert chain(web3) == \"ethlive\"\n\n\ndef test_chain_should_recognize_kovan():\n    # given\n    web3 = mocked_web3(block_0_hash=\"0xa3c565fc15c7478862d50ccd6561e3c06b24cc509bf388941c25ea985ce32cb9\")\n\n    # expect\n    assert chain(web3) == \"kovan\"\n\n\ndef test_chain_should_recognize_ropsten():\n    # given\n    web3 = mocked_web3(block_0_hash=\"0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d\")\n\n    # expect\n    assert chain(web3) == \"ropsten\"\n\n\ndef test_chain_should_recognize_morden():\n    # given\n    web3 = mocked_web3(block_0_hash=\"0x0cd786a2425d16f152c658316c423e6ce1181e15c3295826d7c9904cba9ce303\")\n\n    # expect\n    assert chain(web3) == \"morden\"\n\n\ndef test_chain_should_report_unknown_chains_as_unknown():\n    # given\n    web3 = mocked_web3(block_0_hash=\"0x0000000000011111111222222333333333333555555555555666666444444333\")\n\n    # expect\n    assert chain(web3) == \"unknown\"\n\n\ndef mocked_web3_transaction_count(address: Address, latest: int, pending: int) -> Web3:\n    def side_effect(param_address, param_mode):\n        assert param_address == address.address\n        if param_mode == 'latest':\n            return latest\n        elif param_mode == 'pending':\n            return pending\n        else:\n            raise Exception(\"Unknown mode\")\n\n    web3 = Mock(Web3)\n    web3.eth = Mock()\n    web3.eth.getTransactionCount = Mock(side_effect=side_effect)\n    return web3\n\n\ndef test_synchronize_should_return_empty_list_for_no_futures():\n    assert synchronize([]) == []\n\n\ndef test_synchronize_should_return_results_of_all_async_calls():\n    assert synchronize([async_return(1)]) == [1]\n    assert synchronize([async_return(1), async_return(2)]) == [1, 2]\n    assert synchronize([async_return(1), async_return(2), async_return(3)]) == [1, 2, 3]\n\n\ndef test_synchronize_should_pass_exceptions():\n    with pytest.raises(Exception):\n        synchronize([async_return(1), async_exception(), async_return(3)])\n\n\ndef test_int_to_bytes32():\n    assert int_to_bytes32(0) == bytes([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n                                       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n                                       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n                                       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])\n\n    assert int_to_bytes32(1) == bytes([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n                                       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n                                       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n                                       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01])\n\n    assert int_to_bytes32(512) == bytes([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n                                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n                                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n                                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00])\n    \n    assert int_to_bytes32(2**256-1) == bytes([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,\n                                              0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,\n                                              0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,\n                                              0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff])\n\n\ndef test_bytes_to_int():\n    assert bytes_to_int(bytes([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n                               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n                               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n                               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])) == 0\n\n    assert bytes_to_int(bytes([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n                               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n                               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n                               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01])) == 1\n\n    assert bytes_to_int(bytes([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n                               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n                               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n                               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01])) == 257\n\n    assert bytes_to_int(bytes([0x00])) == 0\n\n    assert bytes_to_int(bytes([0x01, 0x01])) == 257\n\n    assert bytes_to_int(bytes([0x00, 0x01, 0x01])) == 257\n\n    assert bytes_to_int(bytes([0x00, 0x00, 0x01, 0x01])) == 257\n\n    assert bytes_to_int(bytes([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,\n                               0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,\n                               0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,\n                               0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff])) == 2**256-1\n\n\ndef test_bytes_to_int_from_string():\n    assert bytes_to_int('\\x00') == 0\n    assert bytes_to_int('\\x01') == 1\n    assert bytes_to_int('\\x01\\x01') == 257\n    assert bytes_to_int('\\x00\\x01\\x01') == 257\n    assert bytes_to_int('\\x00\\x00\\x01\\x01') == 257\n\n\ndef test_bytes_to_int_from_int_should_fail():\n    with pytest.raises(AssertionError):\n        bytes_to_int(0)\n\n\ndef test_bytes_to_hexstring():\n    assert bytes_to_hexstring(bytes([0x00])) == '0x00'\n    assert bytes_to_hexstring(bytes([0x01, 0x02, 0x03])) == '0x010203'\n    assert bytes_to_hexstring(bytes([0xff, 0xff])) == '0xffff'\n\n\ndef test_hexstring_to_bytes():\n    assert hexstring_to_bytes('0x00') == bytes([0x00])\n    assert hexstring_to_bytes('0x010203') == bytes([0x01, 0x02, 0x03])\n    assert hexstring_to_bytes('0xffff') == bytes([0xff, 0xff])\n\n\nclass TestAsyncCallback:\n    @pytest.fixture\n    def callbacks(self):\n        class Callbacks:\n            counter = 0\n\n            def short_running_callback(self):\n                self.counter += 1\n\n            def long_running_callback(self):\n                time.sleep(1)\n                self.counter += 1\n\n        return Callbacks()\n\n    def test_should_call_callback(self, callbacks):\n        # given\n        async_callback = AsyncCallback(callbacks.short_running_callback)\n\n        # when\n        result = async_callback.trigger()\n\n        # then\n        assert result\n        assert callbacks.counter == 1\n\n    def test_should_not_call_callback_if_previous_one_is_still_running(self, callbacks):\n        # given\n        async_callback = AsyncCallback(callbacks.long_running_callback)\n\n        # when\n        result1 = async_callback.trigger()\n        result2 = async_callback.trigger()\n\n        # and\n        time.sleep(2)\n\n        # then\n        assert result1\n        assert not result2\n        assert callbacks.counter == 1\n\n    def test_should_call_callback_again_if_previous_one_is_finished(self, callbacks):\n        # given\n        async_callback = AsyncCallback(callbacks.long_running_callback)\n\n        # when\n        result1 = async_callback.trigger()\n        time.sleep(2)\n\n        # and\n        result2 = async_callback.trigger()\n        time.sleep(2)\n\n        # then\n        assert result1\n        assert result2\n        assert callbacks.counter == 2\n\n    def test_should_wait_for_the_callback_to_finish(self, callbacks):\n        # given\n        async_callback = AsyncCallback(callbacks.long_running_callback)\n        async_callback.trigger()\n        assert callbacks.counter == 0\n\n        # when\n        async_callback.wait()\n\n        # then\n        assert callbacks.counter == 1\n\n    def test_should_call_on_start_and_on_finish_before_and_after_the_callback(self, callbacks):\n        # given\n        mock = Mock()\n        on_start = mock.on_start\n        callback = mock.callback\n        on_finish = mock.on_finish\n\n        # when\n        async_callback = AsyncCallback(callback)\n        async_callback.trigger(on_start, on_finish)\n\n        # then\n        assert mock.mock_calls == [call.on_start(), call.callback(), call.on_finish()]\n"
  },
  {
    "path": "tests/test_vault.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2017-2018 reverendus\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nimport pytest\nfrom web3 import Web3, HTTPProvider\n\nfrom pymaker import Address\nfrom pymaker.vault import DSVault\n\n\nclass TestDSVault:\n    def setup_method(self):\n        self.web3 = Web3(HTTPProvider(\"http://localhost:8555\"))\n        self.web3.eth.defaultAccount = self.web3.eth.accounts[0]\n        self.our_address = Address(self.web3.eth.defaultAccount)\n        self.dsvault = DSVault.deploy(self.web3)\n\n    def test_fail_when_no_contract_under_that_address(self):\n        # expect\n        with pytest.raises(Exception):\n            DSVault(web3=self.web3, address=Address('0xdeadadd1e5500000000000000000000000000000'))\n\n    def test_authority(self):\n        # given\n        some_address = Address('0x0000000000111111111122222222223333333333')\n\n        # when\n        self.dsvault.set_authority(some_address).transact()\n\n        # then\n        assert self.dsvault.authority() == some_address\n\n    def test_should_have_printable_representation(self):\n        assert repr(self.dsvault) == f\"DSVault('{self.dsvault.address}')\"\n"
  },
  {
    "path": "tests/test_zrx.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2017-2018 reverendus\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nimport json\n\nimport pkg_resources\nimport pytest\nfrom mock import Mock\nfrom web3 import Web3, HTTPProvider\n\nfrom pymaker import Address\nfrom pymaker.approval import directly\nfrom pymaker.deployment import deploy_contract\nfrom pymaker.numeric import Wad\nfrom pymaker.token import DSToken, ERC20Token\nfrom pymaker.zrx import ZrxExchange, Order, ZrxRelayerApi\nfrom tests.helpers import is_hashable, wait_until_mock_called\n\nPAST_BLOCKS = 100\n\n\nclass TestZrx:\n    def setup_method(self):\n        self.web3 = Web3(HTTPProvider(\"http://localhost:8555\"))\n        self.web3.eth.defaultAccount = self.web3.eth.accounts[0]\n        self.our_address = Address(self.web3.eth.defaultAccount)\n        self.zrx_token = ERC20Token(web3=self.web3, address=deploy_contract(self.web3, 'ZRXToken'))\n        self.token_transfer_proxy_address = deploy_contract(self.web3, 'TokenTransferProxy')\n        self.exchange = ZrxExchange.deploy(self.web3, self.zrx_token.address, self.token_transfer_proxy_address)\n        token_proxy_abi = json.loads(pkg_resources.resource_string('pymaker.deployment', f'abi/TokenTransferProxy.abi'))\n        self.web3.eth.contract(abi=token_proxy_abi)\\\n            (address=self.token_transfer_proxy_address.address).functions.addAuthorizedAddress(\n            self.exchange.address.address).transact()\n        self.token1 = DSToken.deploy(self.web3, 'AAA')\n        self.token1.mint(Wad.from_number(100)).transact()\n        self.token2 = DSToken.deploy(self.web3, 'BBB')\n        self.token2.mint(Wad.from_number(100)).transact()\n\n    def test_fail_when_no_contract_under_that_address(self):\n        # expect\n        with pytest.raises(Exception):\n            ZrxExchange(web3=self.web3, address=Address('0xdeadadd1e5500000000000000000000000000000'))\n\n    def test_correct_deployment(self):\n        # expect\n        assert self.exchange is not None\n        assert self.exchange.address is not None\n        assert self.exchange.zrx_token() == self.zrx_token.address\n        assert self.exchange.token_transfer_proxy() == self.token_transfer_proxy_address\n\n    def test_approval(self):\n        # given\n        assert self.token1.allowance_of(self.our_address, self.token_transfer_proxy_address) == Wad(0)\n        assert self.zrx_token.allowance_of(self.our_address, self.token_transfer_proxy_address) == Wad(0)\n\n        # when\n        self.exchange.approve([self.token1], directly())\n\n        # then\n        assert self.token1.allowance_of(self.our_address, self.token_transfer_proxy_address) > Wad(0)\n        assert self.zrx_token.allowance_of(self.our_address, self.token_transfer_proxy_address) > Wad(0)\n\n    def test_create_order(self):\n        # when\n        order = self.exchange.create_order(pay_token=Address(\"0x0202020202020202020202020202020202020202\"),\n                                           pay_amount=Wad.from_number(100),\n                                           buy_token=Address(\"0x0101010101010101010101010101010101010101\"),\n                                           buy_amount=Wad.from_number(2.5), expiration=1763920792)\n\n        # then\n        assert order.maker == Address(self.web3.eth.defaultAccount)\n        assert order.taker == Address(\"0x0000000000000000000000000000000000000000\")\n        assert order.pay_token == Address(\"0x0202020202020202020202020202020202020202\")\n        assert order.pay_amount == Wad.from_number(100)\n        assert order.buy_token == Address(\"0x0101010101010101010101010101010101010101\")\n        assert order.buy_amount == Wad.from_number(2.5)\n        assert order.salt >= 0\n        assert order.expiration == 1763920792\n        assert order.exchange_contract_address == self.exchange.address\n\n        # and\n        # [fees should be zero by default]\n        assert order.maker_fee == Wad.from_number(0)\n        assert order.taker_fee == Wad.from_number(0)\n        assert order.fee_recipient == Address(\"0x0000000000000000000000000000000000000000\")\n\n    def test_get_order_hash(self):\n        # given\n        order = self.exchange.create_order(pay_token=Address(\"0x0202020202020202020202020202020202020202\"),\n                                           pay_amount=Wad.from_number(100),\n                                           buy_token=Address(\"0x0101010101010101010101010101010101010101\"),\n                                           buy_amount=Wad.from_number(2.5), expiration=1763920792)\n\n        # when\n        order_hash = self.exchange.get_order_hash(order)\n\n        # then\n        assert order_hash.startswith('0x')\n        assert len(order_hash) == 66\n\n    def test_sign_order(self):\n        # given\n        order = self.exchange.create_order(pay_token=Address(\"0x0202020202020202020202020202020202020202\"),\n                                           pay_amount=Wad.from_number(100),\n                                           buy_token=Address(\"0x0101010101010101010101010101010101010101\"),\n                                           buy_amount=Wad.from_number(2.5), expiration=1763920792)\n\n        # when\n        signed_order = self.exchange.sign_order(order)\n\n        # then\n        assert signed_order.ec_signature_r.startswith('0x')\n        assert len(signed_order.ec_signature_r) == 66\n        assert signed_order.ec_signature_s.startswith('0x')\n        assert len(signed_order.ec_signature_s) == 66\n        assert signed_order.ec_signature_v in [27, 28]\n\n    def test_cancel_order(self):\n        # given\n        self.exchange.approve([self.token1, self.token2], directly())\n\n        # when\n        order = self.exchange.create_order(pay_token=self.token1.address, pay_amount=Wad.from_number(10),\n                                           buy_token=self.token2.address, buy_amount=Wad.from_number(4),\n                                           expiration=1763920792)\n        # and\n        signed_order = self.exchange.sign_order(order)\n\n        # then\n        assert self.exchange.get_unavailable_buy_amount(signed_order) == Wad(0)\n\n        # when\n        self.exchange.cancel_order(signed_order).transact()\n\n        # then\n        assert self.exchange.get_unavailable_buy_amount(signed_order) == Wad.from_number(4)\n\n    def test_fill_order(self):\n        # given\n        self.exchange.approve([self.token1, self.token2], directly())\n\n        # when\n        order = self.exchange.create_order(pay_token=self.token1.address, pay_amount=Wad.from_number(10),\n                                           buy_token=self.token2.address, buy_amount=Wad.from_number(4),\n                                           expiration=1763920792)\n        # and\n        signed_order = self.exchange.sign_order(order)\n\n        # then\n        assert self.exchange.get_unavailable_buy_amount(signed_order) == Wad(0)\n\n        # when\n        self.exchange.fill_order(signed_order, Wad.from_number(3.5)).transact()\n\n        # then\n        assert self.exchange.get_unavailable_buy_amount(signed_order) == Wad.from_number(3.5)\n\n    def test_remaining_buy_amount_and_remaining_sell_amount(self):\n        # given\n        self.exchange.approve([self.token1, self.token2], directly())\n\n        # when\n        order = self.exchange.create_order(pay_token=self.token1.address, pay_amount=Wad.from_number(10),\n                                           buy_token=self.token2.address, buy_amount=Wad.from_number(4),\n                                           expiration=1763920792)\n        # and\n        signed_order = self.exchange.sign_order(order)\n\n        # then\n        assert signed_order.remaining_sell_amount == Wad.from_number(10)\n        assert signed_order.remaining_buy_amount == Wad.from_number(4)\n\n        # when\n        self.exchange.fill_order(signed_order, Wad.from_number(3.5)).transact()\n\n        # then\n        assert signed_order.remaining_sell_amount == Wad.from_number(1.25)\n        assert signed_order.remaining_buy_amount == Wad.from_number(0.5)\n\n    def test_past_fill(self):\n        # given\n        self.exchange.approve([self.token1, self.token2], directly())\n\n        # when\n        order = self.exchange.create_order(pay_token=self.token1.address, pay_amount=Wad.from_number(10),\n                                           buy_token=self.token2.address, buy_amount=Wad.from_number(4),\n                                           expiration=1763920792)\n        # and\n        self.exchange.fill_order(self.exchange.sign_order(order), Wad.from_number(3)).transact()\n\n        # then\n        past_fill = self.exchange.past_fill(PAST_BLOCKS)\n        assert len(past_fill) == 1\n        assert past_fill[0].maker == self.our_address\n        assert past_fill[0].taker == self.our_address\n        assert past_fill[0].fee_recipient == Address(\"0x0000000000000000000000000000000000000000\")\n        assert past_fill[0].pay_token == self.token1.address\n        assert past_fill[0].buy_token == self.token2.address\n        assert past_fill[0].filled_pay_amount == Wad.from_number(7.5)\n        assert past_fill[0].filled_buy_amount == Wad.from_number(3)\n        assert past_fill[0].paid_maker_fee == Wad.from_number(0)\n        assert past_fill[0].paid_taker_fee == Wad.from_number(0)\n        assert past_fill[0].tokens.startswith('0x')\n        assert past_fill[0].order_hash == self.exchange.get_order_hash(self.exchange.sign_order(order))\n        assert past_fill[0].raw['blockNumber'] > 0\n\n    def test_past_cancel(self):\n        # given\n        self.exchange.approve([self.token1, self.token2], directly())\n\n        # when\n        order = self.exchange.create_order(pay_token=self.token1.address, pay_amount=Wad.from_number(10),\n                                           buy_token=self.token2.address, buy_amount=Wad.from_number(4),\n                                           expiration=1763920792)\n        # and\n        self.exchange.cancel_order(self.exchange.sign_order(order)).transact()\n\n        # then\n        past_cancel = self.exchange.past_cancel(PAST_BLOCKS)\n        assert len(past_cancel) == 1\n        assert past_cancel[0].maker == self.our_address\n        assert past_cancel[0].fee_recipient == Address(\"0x0000000000000000000000000000000000000000\")\n        assert past_cancel[0].pay_token == self.token1.address\n        assert past_cancel[0].cancelled_pay_amount == Wad.from_number(10)\n        assert past_cancel[0].buy_token == self.token2.address\n        assert past_cancel[0].cancelled_buy_amount == Wad.from_number(4)\n        assert past_cancel[0].tokens.startswith('0x')\n        assert past_cancel[0].order_hash == self.exchange.get_order_hash(self.exchange.sign_order(order))\n        assert past_cancel[0].raw['blockNumber'] > 0\n\n    def test_should_have_printable_representation(self):\n        assert repr(self.exchange) == f\"ZrxExchange('{self.exchange.address}')\"\n\n\nclass TestOrder:\n    def test_should_be_comparable(self):\n        # given\n        order1 = Order(exchange=None,\n                       maker=Address(\"0x9e56625509c2f60af937f23b7b532600390e8c8b\"),\n                       taker=Address(\"0x0000000000000000000000000000000000000000\"),\n                       maker_fee=Wad.from_number(123),\n                       taker_fee=Wad.from_number(456),\n                       pay_token=Address(\"0x323b5d4c32345ced77393b3530b1eed0f346429d\"),\n                       pay_amount=Wad(10000000000000000),\n                       buy_token=Address(\"0xef7fff64389b814a946f3e92105513705ca6b990\"),\n                       buy_amount=Wad(20000000000000000),\n                       salt=67006738228878699843088602623665307406148487219438534730168799356281242528500,\n                       fee_recipient=Address('0x6666666666666666666666666666666666666666'),\n                       expiration=42,\n                       exchange_contract_address=Address(\"0x12459c951127e0c374ff9105dda097662a027093\"),\n                       ec_signature_r=\"0xf9f6a3b67b52d40c16387df2cd6283bbdbfc174577743645dd6f4bd828c7dbc3\",\n                       ec_signature_s=\"0x15baf69f6c3cc8ac0f62c89264d73accf1ae165cce5d6e2a0b6325c6e4bab964\",\n                       ec_signature_v=28)\n\n        order2 = Order(exchange=None,\n                       maker=Address(\"0x9e56625509c2f60af937f23b7b532600390e8c8b\"),\n                       taker=Address(\"0x0000000000000000000000000000000000000000\"),\n                       maker_fee=Wad.from_number(123),\n                       taker_fee=Wad.from_number(456),\n                       pay_token=Address(\"0x323b5d4c32345ced77393b3530b1eed0f346429d\"),\n                       pay_amount=Wad(10000000000000000),\n                       buy_token=Address(\"0xef7fff64389b814a946f3e92105513705ca6b990\"),\n                       buy_amount=Wad(20000000000000000),\n                       salt=67006738228878699843088602623665307406148487219438534730168799356281242528500,\n                       fee_recipient=Address('0x6666666666666666666666666666666666666666'),\n                       expiration=42,\n                       exchange_contract_address=Address(\"0x12459c951127e0c374ff9105dda097662a027093\"),\n                       ec_signature_r=\"0xf9f6a3b67b52d40c16387df2cd6283bbdbfc174577743645dd6f4bd828c7dbc3\",\n                       ec_signature_s=\"0x15baf69f6c3cc8ac0f62c89264d73accf1ae165cce5d6e2a0b6325c6e4bab964\",\n                       ec_signature_v=28)\n\n        # expect\n        assert order1 == order2\n\n        # when\n        order2.maker_fee = Wad.from_number(124)\n\n        # then\n        assert order1 != order2\n\n        # when\n        order1.maker_fee = Wad.from_number(124)\n\n        # then\n        assert order1 == order2\n\n    def test_should_be_hashable(self):\n        # given\n        order = Order(exchange=None,\n                      maker=Address(\"0x9e56625509c2f60af937f23b7b532600390e8c8b\"),\n                      taker=Address(\"0x0000000000000000000000000000000000000000\"),\n                      maker_fee=Wad.from_number(123),\n                      taker_fee=Wad.from_number(456),\n                      pay_token=Address(\"0x323b5d4c32345ced77393b3530b1eed0f346429d\"),\n                      pay_amount=Wad(10000000000000000),\n                      buy_token=Address(\"0xef7fff64389b814a946f3e92105513705ca6b990\"),\n                      buy_amount=Wad(20000000000000000),\n                      salt=67006738228878699843088602623665307406148487219438534730168799356281242528500,\n                      fee_recipient=Address('0x6666666666666666666666666666666666666666'),\n                      expiration=42,\n                      exchange_contract_address=Address(\"0x12459c951127e0c374ff9105dda097662a027093\"),\n                      ec_signature_r=\"0xf9f6a3b67b52d40c16387df2cd6283bbdbfc174577743645dd6f4bd828c7dbc3\",\n                      ec_signature_s=\"0x15baf69f6c3cc8ac0f62c89264d73accf1ae165cce5d6e2a0b6325c6e4bab964\",\n                      ec_signature_v=28)\n\n        # expect\n        assert is_hashable(order)\n\n    def test_parse_signed_json_order(self):\n        # given\n        json_order = json.loads(\"\"\"{\n            \"orderHash\": \"0x02266a4887256fdf16b47ca13e3f2cca76f93724842f3f7ddf55d92fb6601b6f\",\n            \"exchangeContractAddress\": \"0x12459c951127e0c374ff9105dda097662a027093\",\n            \"maker\": \"0x0046cac6668bef45b517a1b816a762f4f8add2a9\",\n            \"taker\": \"0x0000000000000000000000000000000000000000\",\n            \"makerTokenAddress\": \"0x59adcf176ed2f6788a41b8ea4c4904518e62b6a4\",\n            \"takerTokenAddress\": \"0x2956356cd2a2bf3202f771f50d3d14a367b48070\",\n            \"feeRecipient\": \"0xa258b39954cef5cb142fd567a46cddb31a670124\",\n            \"makerTokenAmount\": \"11000000000000000000\",\n            \"takerTokenAmount\": \"30800000000000000\",\n            \"makerFee\": \"0\",\n            \"takerFee\": \"0\",\n            \"expirationUnixTimestampSec\": \"1511988904\",\n            \"salt\": \"50626048444772008084444062440502087868712695090943879708059561407114509847312\",\n            \"ecSignature\": {\n                \"r\": \"0xf9f6a3b67b52d40c16387df2cd6283bbdbfc174577743645dd6f4bd828c7dbc3\",\n                \"s\": \"0x15baf69f6c3cc8ac0f62c89264d73accf1ae165cce5d6e2a0b6325c6e4bab964\",\n                \"v\": 28\n            }\n        }\"\"\")\n\n        # when\n        order = Order.from_json(None, json_order)\n\n        # then\n        assert order.exchange_contract_address == Address(\"0x12459c951127e0c374ff9105dda097662a027093\")\n        assert order.maker == Address(\"0x0046cac6668bef45b517a1b816a762f4f8add2a9\")\n        assert order.taker == Address(\"0x0000000000000000000000000000000000000000\")\n        assert order.pay_token == Address(\"0x59adcf176ed2f6788a41b8ea4c4904518e62b6a4\")\n        assert order.buy_token == Address(\"0x2956356cd2a2bf3202f771f50d3d14a367b48070\")\n        assert order.fee_recipient == Address(\"0xa258b39954cef5cb142fd567a46cddb31a670124\")\n        assert order.pay_amount == Wad.from_number(11)\n        assert order.buy_amount == Wad.from_number(0.0308)\n        assert order.maker_fee == Wad.from_number(0)\n        assert order.taker_fee == Wad.from_number(0)\n        assert order.expiration == 1511988904\n        assert order.salt == 50626048444772008084444062440502087868712695090943879708059561407114509847312\n        assert order.ec_signature_r == \"0xf9f6a3b67b52d40c16387df2cd6283bbdbfc174577743645dd6f4bd828c7dbc3\"\n        assert order.ec_signature_s == \"0x15baf69f6c3cc8ac0f62c89264d73accf1ae165cce5d6e2a0b6325c6e4bab964\"\n        assert order.ec_signature_v == 28\n\n    def test_parse_unsigned_json_order(self):\n        # given\n        json_order = json.loads(\"\"\"{\n            \"orderHash\": \"0x02266a4887256fdf16b47ca13e3f2cca76f93724842f3f7ddf55d92fb6601b6f\",\n            \"exchangeContractAddress\": \"0x12459c951127e0c374ff9105dda097662a027093\",\n            \"maker\": \"0x0046cac6668bef45b517a1b816a762f4f8add2a9\",\n            \"taker\": \"0x0000000000000000000000000000000000000000\",\n            \"makerTokenAddress\": \"0x59adcf176ed2f6788a41b8ea4c4904518e62b6a4\",\n            \"takerTokenAddress\": \"0x2956356cd2a2bf3202f771f50d3d14a367b48070\",\n            \"feeRecipient\": \"0xa258b39954cef5cb142fd567a46cddb31a670124\",\n            \"makerTokenAmount\": \"11000000000000000000\",\n            \"takerTokenAmount\": \"30800000000000000\",\n            \"makerFee\": \"0\",\n            \"takerFee\": \"0\",\n            \"expirationUnixTimestampSec\": \"1511988904\",\n            \"salt\": \"50626048444772008084444062440502087868712695090943879708059561407114509847312\"\n        }\"\"\")\n\n        # when\n        order = Order.from_json(None, json_order)\n\n        # then\n        assert order.exchange_contract_address == Address(\"0x12459c951127e0c374ff9105dda097662a027093\")\n        assert order.maker == Address(\"0x0046cac6668bef45b517a1b816a762f4f8add2a9\")\n        assert order.taker == Address(\"0x0000000000000000000000000000000000000000\")\n        assert order.pay_token == Address(\"0x59adcf176ed2f6788a41b8ea4c4904518e62b6a4\")\n        assert order.buy_token == Address(\"0x2956356cd2a2bf3202f771f50d3d14a367b48070\")\n        assert order.fee_recipient == Address(\"0xa258b39954cef5cb142fd567a46cddb31a670124\")\n        assert order.pay_amount == Wad.from_number(11)\n        assert order.buy_amount == Wad.from_number(0.0308)\n        assert order.maker_fee == Wad.from_number(0)\n        assert order.taker_fee == Wad.from_number(0)\n        assert order.expiration == 1511988904\n        assert order.salt == 50626048444772008084444062440502087868712695090943879708059561407114509847312\n        assert order.ec_signature_r is None\n        assert order.ec_signature_s is None\n        assert order.ec_signature_v is None\n\n    def test_serialize_order_to_json_without_fees(self):\n        # given\n        order = Order(exchange=None,\n                      maker=Address(\"0x9e56625509c2f60af937f23b7b532600390e8c8b\"),\n                      taker=Address(\"0x0000000000000000000000000000000000000000\"),\n                      maker_fee=Wad.from_number(123),\n                      taker_fee=Wad.from_number(456),\n                      pay_token=Address(\"0x323b5d4c32345ced77393b3530b1eed0f346429d\"),\n                      pay_amount=Wad(10000000000000000),\n                      buy_token=Address(\"0xef7fff64389b814a946f3e92105513705ca6b990\"),\n                      buy_amount=Wad(20000000000000000),\n                      salt=67006738228878699843088602623665307406148487219438534730168799356281242528500,\n                      fee_recipient=Address('0x6666666666666666666666666666666666666666'),\n                      expiration=42,\n                      exchange_contract_address=Address(\"0x12459c951127e0c374ff9105dda097662a027093\"),\n                      ec_signature_r=\"0xde21c90d3db3abdc8bdc5fafb1f5432a1dede4d621508e7d96fb2ebc15d7eb2f\",\n                      ec_signature_s=\"0x74f3cb421f75727b78ae98157ddce6a77b46c8714f5848d70f6da083527e1719\",\n                      ec_signature_v=28)\n\n        # when\n        json_order = order.to_json_without_fees()\n\n        # then\n        assert json_order == json.loads(\"\"\"{\n            \"exchangeContractAddress\": \"0x12459c951127e0c374ff9105dda097662a027093\",\n            \"maker\": \"0x9e56625509c2f60af937f23b7b532600390e8c8b\",\n            \"taker\": \"0x0000000000000000000000000000000000000000\",\n            \"makerTokenAddress\": \"0x323b5d4c32345ced77393b3530b1eed0f346429d\",\n            \"takerTokenAddress\": \"0xef7fff64389b814a946f3e92105513705ca6b990\",\n            \"makerTokenAmount\": \"10000000000000000\",\n            \"takerTokenAmount\": \"20000000000000000\",\n            \"expirationUnixTimestampSec\": \"42\",\n            \"salt\": \"67006738228878699843088602623665307406148487219438534730168799356281242528500\"\n        }\"\"\")\n\n    def test_serialize_order_to_json(self):\n        # given\n        order = Order(exchange=None,\n                      maker=Address(\"0x9e56625509c2f60af937f23b7b532600390e8c8b\"),\n                      taker=Address(\"0x0000000000000000000000000000000000000000\"),\n                      maker_fee=Wad.from_number(123),\n                      taker_fee=Wad.from_number(456),\n                      pay_token=Address(\"0x323b5d4c32345ced77393b3530b1eed0f346429d\"),\n                      pay_amount=Wad(10000000000000000),\n                      buy_token=Address(\"0xef7fff64389b814a946f3e92105513705ca6b990\"),\n                      buy_amount=Wad(20000000000000000),\n                      salt=67006738228878699843088602623665307406148487219438534730168799356281242528500,\n                      fee_recipient=Address('0x6666666666666666666666666666666666666666'),\n                      expiration=42,\n                      exchange_contract_address=Address(\"0x12459c951127e0c374ff9105dda097662a027093\"),\n                      ec_signature_r=\"0xde21c90d3db3abdc8bdc5fafb1f5432a1dede4d621508e7d96fb2ebc15d7eb2f\",\n                      ec_signature_s=\"0x74f3cb421f75727b78ae98157ddce6a77b46c8714f5848d70f6da083527e1719\",\n                      ec_signature_v=28)\n\n        # when\n        json_order = order.to_json()\n\n        # then\n        assert json_order == json.loads(\"\"\"{\n            \"exchangeContractAddress\": \"0x12459c951127e0c374ff9105dda097662a027093\",\n            \"maker\": \"0x9e56625509c2f60af937f23b7b532600390e8c8b\",\n            \"taker\": \"0x0000000000000000000000000000000000000000\",\n            \"makerTokenAddress\": \"0x323b5d4c32345ced77393b3530b1eed0f346429d\",\n            \"takerTokenAddress\": \"0xef7fff64389b814a946f3e92105513705ca6b990\",\n            \"feeRecipient\": \"0x6666666666666666666666666666666666666666\",\n            \"makerTokenAmount\": \"10000000000000000\",\n            \"takerTokenAmount\": \"20000000000000000\",\n            \"makerFee\": \"123000000000000000000\",\n            \"takerFee\": \"456000000000000000000\",\n            \"expirationUnixTimestampSec\": \"42\",\n            \"salt\": \"67006738228878699843088602623665307406148487219438534730168799356281242528500\",\n            \"ecSignature\": {\n                \"r\": \"0xde21c90d3db3abdc8bdc5fafb1f5432a1dede4d621508e7d96fb2ebc15d7eb2f\",\n                \"s\": \"0x74f3cb421f75727b78ae98157ddce6a77b46c8714f5848d70f6da083527e1719\",\n                \"v\": 28\n            }\n        }\"\"\")\n"
  },
  {
    "path": "tests/test_zrxv2.py",
    "content": "# This file is part of Maker Keeper Framework.\n#\n# Copyright (C) 2017-2018 reverendus\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nimport json\n\nimport pkg_resources\nimport pytest\nfrom eth_abi import encode_single\nfrom mock import Mock\nfrom web3 import EthereumTesterProvider, Web3, HTTPProvider\n\nfrom pymaker import Address\nfrom pymaker.approval import directly\nfrom pymaker.deployment import deploy_contract\nfrom pymaker.numeric import Wad\nfrom pymaker.token import DSToken, ERC20Token\nfrom pymaker.util import bytes_to_hexstring\nfrom pymaker.zrxv2 import ZrxExchangeV2, Order, ZrxRelayerApiV2, ERC20Asset\nfrom tests.helpers import is_hashable, wait_until_mock_called\n\nPAST_BLOCKS = 100\n\n\nclass TestZrxV2:\n    def setup_method(self):\n        self.web3 = Web3(HTTPProvider(\"http://localhost:8555\"))\n        self.web3.eth.defaultAccount = self.web3.eth.accounts[0]\n        self.our_address = Address(self.web3.eth.defaultAccount)\n        self.zrx_token = ERC20Token(web3=self.web3, address=deploy_contract(self.web3, 'ZRXToken'))\n\n        self.asset_proxy = deploy_contract(self.web3, 'ExchangeV2-ERC20Proxy')\n        self.exchange = ZrxExchangeV2.deploy(self.web3, None)  #\"0xf47261b0\" + self.zrx_token.address.address - unused yet\n        self.exchange._contract.functions.registerAssetProxy(self.asset_proxy.address).transact()\n\n        token_proxy_abi = json.loads(pkg_resources.resource_string('pymaker.deployment', f'abi/ExchangeV2-ERC20Proxy.abi'))\n        asset_proxy_contract = self.web3.eth.contract(abi=token_proxy_abi)(address=self.asset_proxy.address)\n        asset_proxy_contract.functions.addAuthorizedAddress(self.exchange.address.address).transact()\n\n        self.token1 = DSToken.deploy(self.web3, 'AAA')\n        self.token1.mint(Wad.from_number(100)).transact()\n        self.token2 = DSToken.deploy(self.web3, 'BBB')\n        self.token2.mint(Wad.from_number(100)).transact()\n\n    def test_fail_when_no_contract_under_that_address(self):\n        # expect\n        with pytest.raises(Exception):\n            ZrxExchangeV2(web3=self.web3, address=Address('0xdeadadd1e5500000000000000000000000000000'))\n\n    def test_correct_deployment(self):\n        # expect\n        assert self.exchange is not None\n        assert self.exchange.address is not None\n        assert self.exchange.zrx_asset() == \"0xf47261b0000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498\"\n        assert self.exchange.zrx_token() == Address(\"0xe41d2489571d322189246dafa5ebde1f4699f498\")\n        assert self.exchange.asset_transfer_proxy(ERC20Asset.ID) == self.asset_proxy\n\n    def test_approval(self):\n        # given\n        assert self.token1.allowance_of(self.our_address, self.asset_proxy) == Wad(0)\n        assert self.zrx_token.allowance_of(self.our_address, self.asset_proxy) == Wad(0)\n\n        # when\n        self.exchange.approve([self.token1], directly())\n\n        # then\n        assert self.token1.allowance_of(self.our_address, self.asset_proxy) > Wad(0)\n        #TODO commented out until we figure out how to handle the 0x token\n        # assert self.zrx_token.allowance_of(self.our_address, self.asset_proxy) > Wad(0)\n\n    def test_create_order(self):\n        # when\n        order = self.exchange.create_order(pay_asset=ERC20Asset(Address(\"0x0202020202020202020202020202020202020202\")),\n                                           pay_amount=Wad.from_number(100),\n                                           buy_asset=ERC20Asset(Address(\"0x0101010101010101010101010101010101010101\")),\n                                           buy_amount=Wad.from_number(2.5), expiration=1763920792)\n\n        # then\n        assert order.maker == Address(self.web3.eth.defaultAccount)\n        assert order.taker == Address(\"0x0000000000000000000000000000000000000000\")\n        assert order.pay_asset == ERC20Asset(Address(\"0x0202020202020202020202020202020202020202\"))\n        assert order.pay_amount == Wad.from_number(100)\n        assert order.buy_asset == ERC20Asset(Address(\"0x0101010101010101010101010101010101010101\"))\n        assert order.buy_amount == Wad.from_number(2.5)\n        assert order.salt >= 0\n        assert order.expiration == 1763920792\n        assert order.exchange_contract_address == self.exchange.address\n\n        # and\n        # [fees should be zero by default]\n        assert order.maker_fee == Wad.from_number(0)\n        assert order.taker_fee == Wad.from_number(0)\n        assert order.fee_recipient == Address(\"0x0000000000000000000000000000000000000000\")\n\n    def test_get_order_hash(self):\n        # given\n        order = self.exchange.create_order(pay_asset=ERC20Asset(Address(\"0x0202020202020202020202020202020202020202\")),\n                                           pay_amount=Wad.from_number(100),\n                                           buy_asset=ERC20Asset(Address(\"0x0101010101010101010101010101010101010101\")),\n                                           buy_amount=Wad.from_number(2.5), expiration=1763920792)\n\n        # when\n        order_hash = self.exchange.get_order_hash(order)\n\n        # then\n        assert order_hash.startswith('0x')\n        assert len(order_hash) == 66\n\n    def test_sign_order(self):\n        # given\n        order = self.exchange.create_order(pay_asset=ERC20Asset(Address(\"0x0202020202020202020202020202020202020202\")),\n                                           pay_amount=Wad.from_number(100),\n                                           buy_asset=ERC20Asset(Address(\"0x0101010101010101010101010101010101010101\")),\n                                           buy_amount=Wad.from_number(2.5), expiration=1763920792)\n\n        # when\n        signed_order = self.exchange.sign_order(order)\n\n        # then\n        assert signed_order.signature.startswith('0x')\n        assert signed_order.signature.endswith('03')\n        assert len(signed_order.signature) == 134\n\n    def test_cancel_order(self):\n        # given\n        self.exchange.approve([self.token1, self.token2], directly())\n\n        # when\n        order = self.exchange.create_order(pay_asset=ERC20Asset(self.token1.address), pay_amount=Wad.from_number(10),\n                                           buy_asset=ERC20Asset(self.token2.address), buy_amount=Wad.from_number(4),\n                                           expiration=1763920792)\n        # and\n        signed_order = self.exchange.sign_order(order)\n\n        # then\n        assert self.exchange.get_unavailable_buy_amount(signed_order) == Wad(0)\n\n        # when\n        self.exchange.cancel_order(signed_order).transact()\n\n        # then\n        assert self.exchange.get_unavailable_buy_amount(signed_order) == Wad.from_number(4)\n\n    def test_fill_order(self):\n        # given\n        self.exchange.approve([self.token1, self.token2], directly())\n\n        # when\n        order = self.exchange.create_order(pay_asset=ERC20Asset(self.token1.address), pay_amount=Wad.from_number(10),\n                                           buy_asset=ERC20Asset(self.token2.address), buy_amount=Wad.from_number(4),\n                                           expiration=1763920792)\n        # and\n        signed_order = self.exchange.sign_order(order)\n\n        # then\n        assert self.exchange.get_unavailable_buy_amount(signed_order) == Wad(0)\n\n        # when\n        self.exchange.fill_order(signed_order, Wad.from_number(3.5)).transact()\n\n        # then\n        assert self.exchange.get_unavailable_buy_amount(signed_order) == Wad.from_number(3.5)\n\n    def test_remaining_buy_amount_and_remaining_sell_amount(self):\n        # given\n        self.exchange.approve([self.token1, self.token2], directly())\n\n        # when\n        order = self.exchange.create_order(pay_asset=ERC20Asset(self.token1.address), pay_amount=Wad.from_number(10),\n                                           buy_asset=ERC20Asset(self.token2.address), buy_amount=Wad.from_number(4),\n                                           expiration=1763920792)\n        # and\n        signed_order = self.exchange.sign_order(order)\n\n        # then\n        assert signed_order.remaining_sell_amount == Wad.from_number(10)\n        assert signed_order.remaining_buy_amount == Wad.from_number(4)\n\n        # when\n        self.exchange.fill_order(signed_order, Wad.from_number(3.5)).transact()\n\n        # then\n        assert signed_order.remaining_sell_amount == Wad.from_number(1.25)\n        assert signed_order.remaining_buy_amount == Wad.from_number(0.5)\n\n    def test_past_fill(self):\n        # given\n        self.exchange.approve([self.token1, self.token2], directly())\n\n        # when\n        order = self.exchange.create_order(pay_asset=ERC20Asset(self.token1.address), pay_amount=Wad.from_number(10),\n                                           buy_asset=ERC20Asset(self.token2.address), buy_amount=Wad.from_number(4),\n                                           expiration=1763920792)\n        # and\n        self.exchange.fill_order(self.exchange.sign_order(order), Wad.from_number(3)).transact()\n\n        # then\n        past_fill = self.exchange.past_fill(PAST_BLOCKS)\n        assert len(past_fill) == 1\n        assert past_fill[0].sender == self.our_address\n        assert past_fill[0].maker == self.our_address\n        assert past_fill[0].taker == self.our_address\n        assert past_fill[0].fee_recipient == Address(\"0x0000000000000000000000000000000000000000\")\n        assert past_fill[0].pay_asset == ERC20Asset(self.token1.address)\n        assert past_fill[0].buy_asset == ERC20Asset(self.token2.address)\n        assert past_fill[0].filled_pay_amount == Wad.from_number(7.5)\n        assert past_fill[0].filled_buy_amount == Wad.from_number(3)\n        assert past_fill[0].paid_maker_fee == Wad.from_number(0)\n        assert past_fill[0].paid_taker_fee == Wad.from_number(0)\n        assert past_fill[0].order_hash == self.exchange.get_order_hash(self.exchange.sign_order(order))\n        assert past_fill[0].raw['blockNumber'] > 0\n\n    def test_past_cancel(self):\n        # given\n        self.exchange.approve([self.token1, self.token2], directly())\n\n        # when\n        order = self.exchange.create_order(pay_asset=ERC20Asset(self.token1.address), pay_amount=Wad.from_number(10),\n                                           buy_asset=ERC20Asset(self.token2.address), buy_amount=Wad.from_number(4),\n                                           expiration=1763920792)\n        # and\n        self.exchange.cancel_order(self.exchange.sign_order(order)).transact()\n\n        # then\n        past_cancel = self.exchange.past_cancel(PAST_BLOCKS)\n        assert len(past_cancel) == 1\n        assert past_cancel[0].maker == self.our_address\n        assert past_cancel[0].fee_recipient == Address(\"0x0000000000000000000000000000000000000000\")\n        assert past_cancel[0].sender == self.our_address\n        assert past_cancel[0].pay_asset == ERC20Asset(self.token1.address)\n        assert past_cancel[0].buy_asset == ERC20Asset(self.token2.address)\n        assert past_cancel[0].order_hash == self.exchange.get_order_hash(self.exchange.sign_order(order))\n        assert past_cancel[0].raw['blockNumber'] > 0\n\n    def test_should_have_printable_representation(self):\n        assert repr(self.exchange) == f\"ZrxExchangeV2('{self.exchange.address}')\"\n\n\nclass TestOrder:\n    def test_should_be_comparable(self):\n        # given\n        order1 = Order(exchange=None,\n                       sender=Address(\"0x0000000000000000000000000000000000000000\"),\n                       maker=Address(\"0x9e56625509c2f60af937f23b7b532600390e8c8b\"),\n                       taker=Address(\"0x0000000000000000000000000000000000000000\"),\n                       maker_fee=Wad.from_number(123),\n                       taker_fee=Wad.from_number(456),\n                       pay_asset=ERC20Asset(Address(\"0x323b5d4c32345ced77393b3530b1eed0f346429d\")),\n                       pay_amount=Wad(10000000000000000),\n                       buy_asset=ERC20Asset(Address(\"0xef7fff64389b814a946f3e92105513705ca6b990\")),\n                       buy_amount=Wad(20000000000000000),\n                       salt=67006738228878699843088602623665307406148487219438534730168799356281242528500,\n                       fee_recipient=Address('0x6666666666666666666666666666666666666666'),\n                       expiration=42,\n                       exchange_contract_address=Address(\"0x12459c951127e0c374ff9105dda097662a027093\"),\n                       signature=\"0x1bf9f6a3b67b52d40c16387df2cd6283bbdbfc174577743645dd6f4bd828c7dbc315baf69f6c3cc8ac0f62c89264d73accf1ae165cce5d6e2a0b6325c6e4bab96403\")\n\n        order2 = Order(exchange=None,\n                       sender=Address(\"0x0000000000000000000000000000000000000000\"),\n                       maker=Address(\"0x9e56625509c2f60af937f23b7b532600390e8c8b\"),\n                       taker=Address(\"0x0000000000000000000000000000000000000000\"),\n                       maker_fee=Wad.from_number(123),\n                       taker_fee=Wad.from_number(456),\n                       pay_asset=ERC20Asset(Address(\"0x323b5d4c32345ced77393b3530b1eed0f346429d\")),\n                       pay_amount=Wad(10000000000000000),\n                       buy_asset=ERC20Asset(Address(\"0xef7fff64389b814a946f3e92105513705ca6b990\")),\n                       buy_amount=Wad(20000000000000000),\n                       salt=67006738228878699843088602623665307406148487219438534730168799356281242528500,\n                       fee_recipient=Address('0x6666666666666666666666666666666666666666'),\n                       expiration=42,\n                       exchange_contract_address=Address(\"0x12459c951127e0c374ff9105dda097662a027093\"),\n                       signature=\"0x1bf9f6a3b67b52d40c16387df2cd6283bbdbfc174577743645dd6f4bd828c7dbc315baf69f6c3cc8ac0f62c89264d73accf1ae165cce5d6e2a0b6325c6e4bab96403\")\n\n        # expect\n        assert order1 == order2\n\n        # when\n        order2.maker_fee = Wad.from_number(124)\n\n        # then\n        assert order1 != order2\n\n        # when\n        order1.maker_fee = Wad.from_number(124)\n\n        # then\n        assert order1 == order2\n\n    def test_should_be_hashable(self):\n        # given\n        order = Order(exchange=None,\n                      sender=Address(\"0x0000000000000000000000000000000000000000\"),\n                      maker=Address(\"0x9e56625509c2f60af937f23b7b532600390e8c8b\"),\n                      taker=Address(\"0x0000000000000000000000000000000000000000\"),\n                      maker_fee=Wad.from_number(123),\n                      taker_fee=Wad.from_number(456),\n                      pay_asset=ERC20Asset(Address(\"0x323b5d4c32345ced77393b3530b1eed0f346429d\")),\n                      pay_amount=Wad(10000000000000000),\n                      buy_asset=ERC20Asset(Address(\"0xef7fff64389b814a946f3e92105513705ca6b990\")),\n                      buy_amount=Wad(20000000000000000),\n                      salt=67006738228878699843088602623665307406148487219438534730168799356281242528500,\n                      fee_recipient=Address('0x6666666666666666666666666666666666666666'),\n                      expiration=42,\n                      exchange_contract_address=Address(\"0x12459c951127e0c374ff9105dda097662a027093\"),\n                      signature=\"0x1bf9f6a3b67b52d40c16387df2cd6283bbdbfc174577743645dd6f4bd828c7dbc315baf69f6c3cc8ac0f62c89264d73accf1ae165cce5d6e2a0b6325c6e4bab96403\")\n\n        # expect\n        assert is_hashable(order)\n\n    def test_parse_signed_json_order(self):\n        # given\n        json_order = json.loads(\"\"\"{\n            \"orderHash\": \"0x02266a4887256fdf16b47ca13e3f2cca76f93724842f3f7ddf55d92fb6601b6f\",\n            \"exchangeAddress\": \"0x12459C951127e0c374FF9105DdA097662A027093\",\n            \"senderAddress\": \"0x0000000000000000000000000000000000000000\",\n            \"makerAddress\": \"0x0046cac6668bef45b517a1b816a762f4f8add2a9\",\n            \"takerAddress\": \"0x0000000000000000000000000000000000000000\",\n            \"makerAssetData\": \"0xf47261b059adcf176ed2f6788a41b8ea4c4904518e62b6a4\",\n            \"takerAssetData\": \"0xf47261b02956356cd2a2bf3202f771f50d3d14a367b48070\",\n            \"feeRecipientAddress\": \"0xa258b39954cef5cb142fd567a46cddb31a670124\",\n            \"makerAssetAmount\": \"11000000000000000000\",\n            \"takerAssetAmount\": \"30800000000000000\",\n            \"makerFee\": \"0\",\n            \"takerFee\": \"0\",\n            \"expirationTimeSeconds\": \"1511988904\",\n            \"salt\": \"50626048444772008084444062440502087868712695090943879708059561407114509847312\",\n            \"signature\": \"0x1bf9f6a3b67b52d40c16387df2cd6283bbdbfc174577743645dd6f4bd828c7dbc315baf69f6c3cc8ac0f62c89264d73accf1ae165cce5d6e2a0b6325c6e4bab96403\"\n        }\"\"\")\n\n        # when\n        order = Order.from_json(None, json_order)\n\n        # then\n        assert order.exchange_contract_address == Address(\"0x12459c951127e0c374ff9105dda097662a027093\")\n        assert order.sender == Address(\"0x0000000000000000000000000000000000000000\")\n        assert order.maker == Address(\"0x0046cac6668bef45b517a1b816a762f4f8add2a9\")\n        assert order.taker == Address(\"0x0000000000000000000000000000000000000000\")\n        assert order.pay_asset == ERC20Asset(Address(\"0x59adcf176ed2f6788a41b8ea4c4904518e62b6a4\"))\n        assert order.buy_asset == ERC20Asset(Address(\"0x2956356cd2a2bf3202f771f50d3d14a367b48070\"))\n        assert order.fee_recipient == Address(\"0xa258b39954cef5cb142fd567a46cddb31a670124\")\n        assert order.pay_amount == Wad.from_number(11)\n        assert order.buy_amount == Wad.from_number(0.0308)\n        assert order.maker_fee == Wad.from_number(0)\n        assert order.taker_fee == Wad.from_number(0)\n        assert order.expiration == 1511988904\n        assert order.salt == 50626048444772008084444062440502087868712695090943879708059561407114509847312\n        assert order.signature == \"0x1bf9f6a3b67b52d40c16387df2cd6283bbdbfc174577743645dd6f4bd828c7dbc315baf69f6c3cc8ac0f62c89264d73accf1ae165cce5d6e2a0b6325c6e4bab96403\"\n\n    def test_parse_unsigned_json_order(self):\n        # given\n        json_order = json.loads(\"\"\"{\n            \"orderHash\": \"0x02266a4887256fdf16b47ca13e3f2cca76f93724842f3f7ddf55d92fb6601b6f\",\n            \"exchangeAddress\": \"0x12459C951127e0c374FF9105DdA097662A027093\",\n            \"senderAddress\": \"0x0000000000000000000000000000000000000000\",\n            \"makerAddress\": \"0x0046cac6668bef45b517a1b816a762f4f8add2a9\",\n            \"takerAddress\": \"0x0000000000000000000000000000000000000000\",\n            \"makerAssetData\": \"0xf47261b059adcf176ed2f6788a41b8ea4c4904518e62b6a4\",\n            \"takerAssetData\": \"0xf47261b02956356cd2a2bf3202f771f50d3d14a367b48070\",\n            \"feeRecipientAddress\": \"0xa258b39954cef5cb142fd567a46cddb31a670124\",\n            \"makerAssetAmount\": \"11000000000000000000\",\n            \"takerAssetAmount\": \"30800000000000000\",\n            \"makerFee\": \"0\",\n            \"takerFee\": \"0\",\n            \"expirationTimeSeconds\": \"1511988904\",\n            \"salt\": \"50626048444772008084444062440502087868712695090943879708059561407114509847312\"\n        }\"\"\")\n\n        # when\n        order = Order.from_json(None, json_order)\n\n        # then\n        assert order.exchange_contract_address == Address(\"0x12459c951127e0c374ff9105dda097662a027093\")\n        assert order.maker == Address(\"0x0046cac6668bef45b517a1b816a762f4f8add2a9\")\n        assert order.taker == Address(\"0x0000000000000000000000000000000000000000\")\n        assert order.pay_asset == ERC20Asset(Address(\"0x59adcf176ed2f6788a41b8ea4c4904518e62b6a4\"))\n        assert order.buy_asset == ERC20Asset(Address(\"0x2956356cd2a2bf3202f771f50d3d14a367b48070\"))\n        assert order.fee_recipient == Address(\"0xa258b39954cef5cb142fd567a46cddb31a670124\")\n        assert order.pay_amount == Wad.from_number(11)\n        assert order.buy_amount == Wad.from_number(0.0308)\n        assert order.maker_fee == Wad.from_number(0)\n        assert order.taker_fee == Wad.from_number(0)\n        assert order.expiration == 1511988904\n        assert order.salt == 50626048444772008084444062440502087868712695090943879708059561407114509847312\n        assert order.signature is None\n\n    def test_serialize_order_to_json_without_fees(self):\n        # given\n        order = Order(exchange=None,\n                      sender=Address(\"0x0000000000000000000000000000000000000000\"),\n                      maker=Address(\"0x9e56625509c2f60af937f23b7b532600390e8c8b\"),\n                      taker=Address(\"0x0000000000000000000000000000000000000000\"),\n                      maker_fee=Wad.from_number(123),\n                      taker_fee=Wad.from_number(456),\n                      pay_asset=ERC20Asset(Address(\"0x323b5d4c32345ced77393b3530b1eed0f346429d\")),\n                      pay_amount=Wad(10000000000000000),\n                      buy_asset=ERC20Asset(Address(\"0xef7fff64389b814a946f3e92105513705ca6b990\")),\n                      buy_amount=Wad(20000000000000000),\n                      salt=67006738228878699843088602623665307406148487219438534730168799356281242528500,\n                      fee_recipient=Address('0x6666666666666666666666666666666666666666'),\n                      expiration=42,\n                      exchange_contract_address=Address(\"0x12459C951127e0c374FF9105DdA097662A027093\"),\n                      signature=\"0x1bf9f6a3b67b52d40c16387df2cd6283bbdbfc174577743645dd6f4bd828c7dbc315baf69f6c3cc8ac0f62c89264d73accf1ae165cce5d6e2a0b6325c6e4bab96403\")\n\n        # when\n        json_order = order.to_json_without_fees()\n\n        # then\n        assert json_order == json.loads(\"\"\"{\n            \"exchangeAddress\": \"0x12459c951127e0c374ff9105dda097662a027093\",\n            \"makerAddress\": \"0x9e56625509c2f60af937f23b7b532600390e8c8b\",\n            \"takerAddress\": \"0x0000000000000000000000000000000000000000\",\n            \"makerAssetData\": \"0xf47261b0000000000000000000000000323b5d4c32345ced77393b3530b1eed0f346429d\",\n            \"takerAssetData\": \"0xf47261b0000000000000000000000000ef7fff64389b814a946f3e92105513705ca6b990\",\n            \"makerAssetAmount\": \"10000000000000000\",\n            \"takerAssetAmount\": \"20000000000000000\",\n            \"expirationTimeSeconds\": \"42\"\n        }\"\"\")\n\n    def test_serialize_order_to_json(self):\n        # given\n        order = Order(exchange=None,\n                      sender=Address(\"0x0000000000000000000000000000000000000000\"),\n                      maker=Address(\"0x9e56625509c2f60af937f23b7b532600390e8c8b\"),\n                      taker=Address(\"0x0000000000000000000000000000000000000000\"),\n                      maker_fee=Wad.from_number(123),\n                      taker_fee=Wad.from_number(456),\n                      pay_asset=ERC20Asset(Address(\"0x323b5d4c32345ced77393b3530b1eed0f346429d\")),\n                      pay_amount=Wad(10000000000000000),\n                      buy_asset=ERC20Asset(Address(\"0xef7fff64389b814a946f3e92105513705ca6b990\")),\n                      buy_amount=Wad(20000000000000000),\n                      salt=67006738228878699843088602623665307406148487219438534730168799356281242528500,\n                      fee_recipient=Address('0x6666666666666666666666666666666666666666'),\n                      expiration=42,\n                      exchange_contract_address=Address(\"0x12459c951127e0c374ff9105dda097662a027093\"),\n                      signature=\"0x1bf9f6a3b67b52d40c16387df2cd6283bbdbfc174577743645dd6f4bd828c7dbc315baf69f6c3cc8ac0f62c89264d73accf1ae165cce5d6e2a0b6325c6e4bab96403\")\n\n        # when\n        json_order = order.to_json()\n\n        # then\n        assert json_order == json.loads(\"\"\"{\n            \"exchangeAddress\": \"0x12459c951127e0c374ff9105dda097662a027093\",\n            \"senderAddress\": \"0x0000000000000000000000000000000000000000\",\n            \"makerAddress\": \"0x9e56625509c2f60af937f23b7b532600390e8c8b\",\n            \"takerAddress\": \"0x0000000000000000000000000000000000000000\",\n            \"makerAssetData\": \"0xf47261b0000000000000000000000000323b5d4c32345ced77393b3530b1eed0f346429d\",\n            \"takerAssetData\": \"0xf47261b0000000000000000000000000ef7fff64389b814a946f3e92105513705ca6b990\",\n            \"makerAssetAmount\": \"10000000000000000\",\n            \"takerAssetAmount\": \"20000000000000000\",\n            \"feeRecipientAddress\": \"0x6666666666666666666666666666666666666666\",\n            \"makerFee\": \"123000000000000000000\",\n            \"takerFee\": \"456000000000000000000\",\n            \"expirationTimeSeconds\": \"42\",\n            \"salt\": \"67006738228878699843088602623665307406148487219438534730168799356281242528500\",\n            \"signature\": \"0x1bf9f6a3b67b52d40c16387df2cd6283bbdbfc174577743645dd6f4bd828c7dbc315baf69f6c3cc8ac0f62c89264d73accf1ae165cce5d6e2a0b6325c6e4bab96403\"\n        }\"\"\")\n"
  },
  {
    "path": "utils/etherdelta-client/.gitignore",
    "content": "node_modules/"
  },
  {
    "path": "utils/etherdelta-client/main.js",
    "content": "/*!\n * This file is part of Maker Keeper Framework.\n *\n * Copyright (C) 2017-2018 reverendus\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\nvar args = require('minimist')(process.argv.slice(2));\nconst order = args['_'].join(\" \");\nconst url = args['url'];\nconst retryInterval = args['retry-interval'];\nconst timeout = args['timeout'];\n\nfunction publishOrder() {\n  socket.emit('message', JSON.parse(order));\n  console.log('Order sent');\n}\n\nconsole.log(\"Sending order '\" + order + \"' to \" + url);\n\nconst io = require('socket.io-client');\nconst socket = io.connect(url, { transports: ['websocket'] });\n\nsocket.on('connect', () => {\n  console.log(\"Connected to socket\");\n  publishOrder();\n});\n\nsocket.on('messageResult', (messageResult) => {\n  console.log(\"Response received: \", messageResult);\n\nif (messageResult[0] === 'Added/updated order.') {\n    console.log(\"Order placed successfully\");\n    socket.disconnect();\n    setTimeout(() => process.exit(0), 2500);\n  }\n  else {\n    console.log(\"Order placement failed\");\n    setTimeout(publishOrder, retryInterval*1000);\n  }\n});\n\n\nsocket.on('disconnect', () => {\n  console.log('Disconnected from socket');\n});\n\nsocket.on('reconnect', () => {\n  console.log('Reconnected to socket');\n});\n\nsetTimeout(() => {\n  console.log('Timed out');\n  process.exit(-1);\n}, timeout*1000);\n"
  },
  {
    "path": "utils/etherdelta-client/package.json",
    "content": "{\n  \"name\": \"etherdelta-client\",\n  \"version\": \"1.0.0\",\n  \"description\": \"\",\n  \"main\": \"main.js\",\n  \"scripts\": {\n    \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n  },\n  \"author\": \"\",\n  \"license\": \"AGPL\",\n  \"dependencies\": {\n    \"minimist\": \"^1.2.0\",\n    \"socket.io-client\": \"^2.0.4\"\n  }\n}\n"
  }
]