[
  {
    "path": ".dockerignore",
    "content": ".git\n.github\n.gitignore\n.pre-commit*\n.env\n.docker_env\n.env_sample\ndocker-compose.yaml"
  },
  {
    "path": ".env_sample",
    "content": "\n# Proper nodes are required to run ethtx, provide connection strings for chains which will be used.\nMAINNET_NODE_URL=https://geth-erigon-node:8545\n# KOVAN_NODE_URL=\n# RINKEBY_NODE_URL=\n\n# EthTx supports multiple nodes, if one is unavailable, it will use others. You only need to specify them with a comma.\n# Example: MAINNET_NODE_URL=https://geth-erigon-node:8545,https://geth1-erigon-node:8545\n\n# Etherscan API is used to get contract source code, required for decoding process\n# You can get free key here https://etherscan.io/apis\nETHERSCAN_KEY=\n\n# Optional. Those represent data required for connecting to mongoDB. It's used for caching semantics\n# used in decoding process. But, it's not neccessary for running, If you don't want to use permanent\n# db or setup mongo, leave those values, mongomock package is used to simulate in-memory mongo.\nMONGO_CONNECTION_STRING=mongomock://localhost/ethtx\n\n# Optional. Credentials for accessing semantics editor page, available under '/semantics/<str:address>'\nETHTX_ADMIN_USERNAME=admin\nETHTX_ADMIN_PASSWORD=admin\n\n# Optional. Api key used for securing decoding API\nAPI_KEY=\n\n# Optional. Valid values are ['production', 'staging', 'development']. Those mainly\n# dictate what options are used for flask debugging and logging\nENV=development\n"
  },
  {
    "path": ".gitignore",
    "content": "# Ignore pipenv's lock\nPipfile.lock\n# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\n\n# C extensions\n*.so\n\n# Distribution / packaging\n.Python\nbuild/\ndevelop-eggs/\ndist/\ndownloads/\neggs/\n.eggs/\nlib/\nlib64/\nparts/\nsdist/\nvar/\nwheels/\npip-wheel-metadata/\nshare/python-wheels/\n*.egg-info/\n.installed.cfg\n*.egg\nMANIFEST\n\n# PyInstaller\n#  Usually these files are written by a python script from a template\n#  before PyInstaller builds the exe, so as to inject date/other infos into it.\n*.manifest\n*.spec\n\n# Installer logs\npip-log.txt\npip-delete-this-directory.txt\n\n# Unit test / coverage reports\nhtmlcov/\n.tox/\n.nox/\n.coverage\n.coverage.*\n.cache\nnosetests.xml\ncoverage.xml\n*.cover\n*.py,cover\n.hypothesis/\n.pytest_cache/\ncover/\n\n# Translations\n*.mo\n*.pot\n\n# Django stuff:\n*.log\nlocal_settings.py\ndb.sqlite3\ndb.sqlite3-journal\n\n# Flask stuff:\ninstance/\n.webassets-cache\n\n# Scrapy stuff:\n.scrapy\n\n# Sphinx documentation\ndocs/_build/\n\n# PyBuilder\ntarget/\n\n# Jupyter Notebook\n.ipynb_checkpoints\n\n# IPython\nprofile_default/\nipython_config.py\n\n# pyenv\n#   For a library or package, you might want to ignore these files since the code is\n#   intended to run in multiple environments; otherwise, check them in:\n# .python-version\n\n# pipenv\n#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.\n#   However, in case of collaboration, if having platform-specific dependencies or dependencies\n#   having no cross-platform support, pipenv may install dependencies that don't work, or not\n#   install all needed dependencies.\n#Pipfile.lock\n\n# PEP 582; used by e.g. github.com/David-OConnor/pyflow\n__pypackages__/\n\n# Celery stuff\ncelerybeat-schedule\ncelerybeat.pid\n\n# SageMath parsed files\n*.sage.py\n\n# Environments\n.venv\nenv/\nvenv/\nENV/\nenv.bak/\nvenv.bak/\n\n# Spyder project settings\n.spyderproject\n.spyproject\n\n# Rope project settings\n.ropeproject\n\n# mkdocs documentation\n/site\n\n# mypy\n.mypy_cache/\n.dmypy.json\ndmypy.json\n\n# Pyre type checker\n.pyre/\n\n# pytype static type analyzer\n.pytype/\n\n# Editors\n.idea\n.vscode\n\n\n# env\n.env\n.docker_env\n\ntmp/\n*.sqlite\n"
  },
  {
    "path": ".pre-commit-config.yaml",
    "content": "repos:\n  - repo: https://github.com/psf/black\n    rev: 22.3.0\n    hooks:\n      - id: black\n        language_version: python3.9\n        name: EthTx_ce:black\n        alias: ethtx_ce-black\n\n  - repo: https://gitlab.com/pycqa/flake8\n    rev: 4.0.1\n    hooks:\n      - id: flake8\n        language_version: python3.9\n        name: EthTx_ce:flake8\n        alias: ethtx_ce-flake8\n        args: [ --config=ethtx_ce/.flake8 ]\n\n  - repo: https://github.com/pre-commit/pre-commit-hooks\n    rev: v4.0.1\n    hooks:\n    - id: trailing-whitespace\n    - id: check-ast\n    - id: check-docstring-first\n    - id: check-merge-conflict\n\n  - repo: local\n    hooks:\n      - id: pytest\n        files: ./ethtx_ce/tests/\n        name: pytest\n        language: system\n        entry: make test\n        pass_filenames: false\n        always_run: true"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Changelog\nAll notable changes to this project will be documented in this file.\n\n\n## 0.2.16 - 2022-11-25\n### Changed\n- Removed `.js` files from the sources and pulling them instead from `cdnjs` [#123](https://github.com/EthTx/ethtx_ce/pull/123)\n- Removed `static` ressources from the frontend of the app [#129](https://github.com/EthTx/ethtx_ce/pull/129)\n- Removed `gthread` for better stability [#135](https://github.com/EthTx/ethtx_ce/pull/135)\n- Removed support of `Rinkeby` [#139](https://github.com/EthTx/ethtx_ce/pull/139)\n- Changed the `README.md` to include the `development` dependencies [#140](https://github.com/EthTx/ethtx_ce/pull/140)\n\n### Added\n- Added flexible chains rendering [#139](https://github.com/EthTx/ethtx_ce/pull/139)\n- Bumped `EthTx` to `0.3.20` and the `web3` depencency to `5.28.0` [#143](https://github.com/EthTx/ethtx_ce/pull/143)\n\n\n## 0.2.15 - 2022-07-06\n### Changed\n- Removed `Tokne Flow` branding\n\n### Added\n- Added redecode semantics functionality\n- Bumped `EthTx` to `0.3.16` [#116](https://github.com/EthTx/ethtx_ce/pull/116)\n- From now on the value of `transfer.value` is formatted on the frontend [#116](https://github.com/EthTx/ethtx_ce/pull/116)\n\n### Fixed\n- Fixed wrong function name (`get_semantics`) [#118](https://github.com/EthTx/ethtx_ce/pull/118)\n\n\n## 0.2.14 - 2022-05-18\n### Added\n- Added `info` endpoint with ethtx/ethtx_ce version [#113](https://github.com/EthTx/ethtx_ce/pull/113)\n- added `get_latest_ethtx_version` function (get version from Pypi) [#113](https://github.com/EthTx/ethtx_ce/pull/113)\n\n### Changed\n- Refactored `deps` [#113](https://github.com/EthTx/ethtx_ce/pull/113)\n- Updated `README.md` [#113](https://github.com/EthTx/ethtx_ce/pull/113)\n- Updated `black` version [#113](https://github.com/EthTx/ethtx_ce/pull/113)\n- Changed the application name for each component [#113](https://github.com/EthTx/ethtx_ce/pull/113)\n\n\n## 0.2.13 - 2022-04-22\n### Changed\n- Extended *.gitignore* [#110](https://github.com/EthTx/ethtx_ce/pull/110)\n- Updated `black` pre-commit version [#110](https://github.com/EthTx/ethtx_ce/pull/110)\n\n### Fixed\n- Fixed mongodb semantics remove [#110](https://github.com/EthTx/ethtx_ce/pull/110)\n\n\n## 0.2.12 - 2022-04-06\n### Changed\n- Bumped `EthTx` to `0.3.14` [#105](https://github.com/EthTx/ethtx_ce/pull/105)\n\n\n## 0.2.11 - 2022-03-16\n### Changed\n- New project structure [#97](https://github.com/EthTx/ethtx_ce/pull/97)\n- Updated docker, docker-compose [#97](https://github.com/EthTx/ethtx_ce/pull/97)\n- Removed logs, useless text [#99](https://github.com/EthTx/ethtx_ce/pull/99)\n- Changed space between *tx_hash* and *chain_id* (transaction page) [#99](https://github.com/EthTx/ethtx_ce/pull/99)\n\n### Added\n- Added gunicorn configuration [#97](https://github.com/EthTx/ethtx_ce/pull/97)\n- Added `entrypoint.sh` and other scripts [#97](https://github.com/EthTx/ethtx_ce/pull/97)\n\n\n## 0.2.10 - 2022-03-03\n### Changed\n- Bumped `EthTx` to `0.3.10` [#93](https://github.com/EthTx/ethtx_ce/pull/93)\n\n\n## 0.2.9 - 2022-02-07\n### Fixed\n- Typo in `README.md` [#75](https://github.com/EthTx/ethtx_ce/pull/75)\n- API serialization [#75](https://github.com/EthTx/ethtx_ce/pull/75)\n- Fixed semantics editor [#75](https://github.com/EthTx/ethtx_ce/pull/75)\n- Fixed `.env_sample` mongo connection string [#83](https://github.com/EthTx/ethtx_ce/pull/83)\n\n### Added\n- Added new route `reload`\n- Added `Reolad semantics` button, which allows to reload the semantics (removes from the database and downloads\n  again) [#80](https://github.com/EthTx/ethtx_ce/pull/80)\n- Added `get_eth_price`. Transaction page displays current **ETH** price taken from *coinbase*\n  API [#88](https://github.com/EthTx/ethtx_ce/pull/88)\n\n### Changed\n- Removed duplicated environment variables from `docker-compose.yml` [#83](https://github.com/EthTx/ethtx_ce/pull/83)\n- Bumped python to `3.9` [#87](https://github.com/EthTx/ethtx_ce/pull/87)\n- From now on, `EthTx` will be used with a static version (due to dynamic\n  development) [#87](https://github.com/EthTx/ethtx_ce/pull/87)\n- Updated requirements [#88](https://github.com/EthTx/ethtx_ce/pull/88)\n- Install dev dependencies [#89](https://github.com/EthTx/ethtx_ce/pull/89)\n\n\n## 0.2.8 - 2021-10-29\n### Changed\n- Updated **README** and **.env_sample** [#67](https://github.com/EthTx/ethtx_ce/pull/67)\n- `Web3ConnectionException` is not supported anymore. From now on, a general exception `NodeConnectionException`\n  is caught for node connection errors  [#67](https://github.com/EthTx/ethtx_ce/pull/67)\n- Guessed functions and events are detected using the guessed variable in the\n  model [#67](https://github.com/EthTx/ethtx_ce/pull/67)\n\n\n## 0.2.7 - 2021-10-14\n### Changed\n- Changed [EthTx](https://github.com/EthTx/ethtx) version - >=0.3.0,<\n  0.4.0 [#62](https://github.com/EthTx/ethtx_ce/pull/62)\n- Deleted usage of mongodb variable [#61](https://github.com/EthTx/ethtx_ce/pull/61)\n\n### Fixed\n- Fixed colored guessed events with tuple arg [#65](https://github.com/EthTx/ethtx_ce/pull/65)\n\n\n## 0.2.6 - 2021-10-01\n### Changed\n- Changed the position of the logo [#59](https://github.com/EthTx/ethtx_ce/pull/59)\n\n\n## 0.2.5 - 2021-09-30\n### Fixed\n- Fixed colored guessed functions with nested args [#58](https://github.com/EthTx/ethtx_ce/pull/58)\n\n\n## 0.2.4 - 2021-09-29\n### Added\n- Added `.env_sample` file with example environment variables [#57](https://github.com/EthTx/ethtx_ce/pull/57)\n\n### Fixed\n- Fixed `make run-local` [#57](https://github.com/EthTx/ethtx_ce/pull/57)\n\n### Changed\n- Changed the docker configuration to make it easier to start [#57](https://github.com/EthTx/ethtx_ce/pull/57)\n- Updated **README** [#57](https://github.com/EthTx/ethtx_ce/pull/57)\n\n\n## 0.2.3 - 2021-09-23\n### Added\n- Color guessed functions and events [#56](https://github.com/EthTx/ethtx_ce/pull/56)\n\n\n## 0.2.2 - 2021-09-20\n### Fixed\n- Fixed `tx hash` regexp extracting from request [#53](https://github.com/EthTx/ethtx_ce/pull/53)\n\n\n## 0.2.1 - 2021-09-17\n### Fixed\n- Fixed `Decode now` button state [#50](https://github.com/EthTx/ethtx_ce/pull/50)\n\n\n## 0.2.0 - 2021-09-14\n### Added - [#44](https://github.com/EthTx/ethtx_ce/pull/44)\n- Added new error page.\n- Added [Token Flow](https://tokenflow.live) logo.\n- Added input hash validator.\n\n### Changed - [#44](https://github.com/EthTx/ethtx_ce/pull/44)\n- Changed footer style.\n- Removed **ToS** and **PP** and replaced them with `Token Flow` pages.\n- Removed old tests.\n- Added **Fathom** analytics tool.\n- Updated links.\n\n### Fixed - [#44](https://github.com/EthTx/ethtx_ce/pull/44)\n- Fixed frontend styles.\n\n\n## 0.1.10 - 2021-08-20\n### Added\n- Added *preload* to links.\n\n\n## 0.1.9 - 2021-08-18\n### Added\n- Added new footer.\n- Added `Rinkeby` support.\n\n### Changed\n- Changed [EthTx](https://github.com/EthTx/ethtx) version - >=0.2.0,<0.3.0.\n\n### Fixed\n- Etherscan links fixed for testnets.\n\n\n## 0.1.8 - 2021-08-11\n### Added\n- Added `Goerli` support.\n\n### Changed\n- Changed [EthTx](https://github.com/EthTx/ethtx) version - >=0.2.0,<0.3.0.\n\n## 0.1.7 - 2021-08-05\n### Added\n- Added link to PyPi.\n\n\n## 0.1.6 - 2021-08-04\n### Added\n- Added information about the `EthTx` and `EthTx Ce` version to the frontend.\n\n### Changed\n- Removed `Pipfile.lock`\n\n### Fixed\n- Fixed application dependencies.\n\n\n## 0.1.5 - 2021-08-02\n### Changed\n- Removed the banner that was about the new version of `ethtx_ce`.\n\n\n## 0.1.4 - 2021-07-29\n### Changed\n- Changed semantics save functions.\n- Changed [EthTx](https://github.com/EthTx/ethtx) version - 0.1.7.\n\n\n## 0.1.3 - 2021-07-28\n### Changed\n- Changed [EthTx](https://github.com/EthTx/ethtx) version - 0.1.6.\n\n\n## 0.1.2 - 2021-07-27\n### Changed\n- Changed [EthTx](https://github.com/EthTx/ethtx) version - 0.1.5.\n- Changed app Config.\n- Removed EthtxConfig defaults.\n\n\n## 0.1.1 - 2021-07-26\n### Fixed\n- Fixed header on mobile devices.\n\n### Changed\n- Changed Development.MD note.\n\n### Added\n- Added configuration: AWS, Pipfile, pre-commit.\n\n## 0.1.0 - 2021-07-23\n### Added\n- First version EthTx CE.\n"
  },
  {
    "path": "DEVELOPMENT.md",
    "content": "# Local Development\n\nThis repository contains 2 basic applications: `frontend` & `api`. It is easy to manage, and you can easily add new\nlocal application(s).\n\n## Basic structure\n\nApplication is based on [blueprints](https://flask.palletsprojects.com/en/2.0.x/blueprints/).\n\nNew extension requires:\n\n- new Python Package in ![ethtx_ce](ethtx_ce/app) subdirectory.\n- `create_app` function (created in new package in `init` file) which returns `Flask` object by\n  calling ![app factory](ethtx_ce/app/factory.py) file.\n- calling a function above in a `wsgi.py` file with assigned url prefix.\n\nThese simple steps allow you to add new extension and integrate with entire application.\n"
  },
  {
    "path": "Dockerfile",
    "content": "FROM python:3.9\n\nWORKDIR /app/\n\n# Upgrade pip, install pipenv\nRUN pip install --upgrade pip && pip install pipenv\n\n# Copy Pipfile* in case it doesn't exist in the repo\nCOPY Pipfile* /app/\n\nCOPY ./ethtx_ce/entrypoint.sh /entrypoint.sh\nRUN chmod +x /entrypoint.sh\n\nCOPY ./ethtx_ce/start.sh  /start.sh\nRUN chmod +x /start.sh\n\nCOPY ./ethtx_ce/start-reload.sh /start-reload.sh\nRUN chmod +x /start-reload.sh\n\nCOPY ./ethtx_ce/gunicorn_conf.py /gunicorn_conf.py\n\nCOPY Makefile /Makefile\n\nRUN bash -c \"pipenv install --dev --deploy\"\n\nARG GIT_URL\nENV GIT_URL=$GIT_URL\n\nARG GIT_SHA\nENV GIT_SHA=$GIT_SHA\n\nARG CI=1\n\nCOPY ./ethtx_ce /app\nENV PYTHONPATH=/app\n\nEXPOSE 5000\n\nENTRYPOINT [\"/entrypoint.sh\"]\n"
  },
  {
    "path": "LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n"
  },
  {
    "path": "Makefile",
    "content": "help:\n\t@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = \":.*?## \"}; {printf \"\\033[36m%-30s\\033[0m %s\\n\", $$1, $$2}'\n\nbuild-image:  ## Build all docker images\n\tdocker build -t ethtx_ce .\n\nget-git-version: ## Get git version\n\t./scripts/git_version_for_docker.sh\n\nrun-database:  ## Run only a local database required for local development\n\tdocker-compose up -d mongo mongo-express\n\nrun-local:\n\tPYTHONPATH=./ethtx_ce FLASK_APP=ethtx_ce/app/wsgi.py FLASK_DEBUG=1 pipenv run flask run --host=0.0.0.0 --port 5555\n\nrun-prod:\n\tfuser -k 5000/tcp || true\n\tPYTHONPATH=./ethtx_ce pipenv run gunicorn --workers 4 --max-requests 4000 --timeout 600 --bind :5000 app.wsgi:app\n\nrun-docker:\n\tfuser -k 5000/tcp || true\n\tdocker-compose up -d\n\nrun-test-docker:\n\tdocker run -it ethtx_ce pipenv run python -m pytest .\n\ntest:\n\tPYTHONPATH=./ethtx_ce pipenv run python -m pytest ethtx_ce/tests/\n\ntest-all:\n\tPYTHONPATH=./ethtx_ce pipenv run python -m pytest .\n\nsetup:\n\tpipenv install --dev\n\tpipenv run pre-commit install\n"
  },
  {
    "path": "NOTICE",
    "content": "Copyright 2021 DAI FOUNDATION (the original version https://github.com/daifoundation/ethtx_ce)\nCopyright 2021-2022 Token Flow Insights SA (modifications to the original software as recorded\nin the changelog https://github.com/EthTx/ethtx/blob/master/CHANGELOG.md)\n\nThe product contains trademarks and other branding elements of Token Flow Insights SA which are\nnot licensed under the Apache 2.0 license. When using or reproducing the code, please remove\nthe trademark and/or other branding elements.\n"
  },
  {
    "path": "Pipfile",
    "content": "[[source]]\nurl = \"https://pypi.org/simple\"\nverify_ssl = true\nname = \"pypi\"\n\n[packages]\nethtx = \"==0.3.22\"\npython-dotenv = \"*\"\nflask = \">=2.0.2\"\nwerkzeug = \">=2.0.2\"\ngunicorn = {version = \">=20.1.0\"}\nflask-httpauth = \">=4.5.0\"\ngitpython = \">=3.1.24\"\njsonpickle = \">=3.0.0\"\nsimplejson = \"*\"\npydantic = \"<2.0.0\"\n\n[dev-packages]\nblack = \"*\"\npytest = \">=6.2.5\"\npytest-cov =\">=3.0.0\"\npytest-mock =\">=3.6.1\"\npre-commit = \"*\"\n\n[requires]\npython_version = \"3.9\"\n"
  },
  {
    "path": "README.md",
    "content": "<h1 align='center' style=\"border-bottom: none\">\n  EthTx Community Edition\n</h1>\n<br/>\n<p align=\"center\">\n    <em>Community version of EthTx transaction decoder</em>\n<br>\n    <em><a href=\"https://ethtx.info\">https://ethtx.info</a></em>\n</p>\n<p align=\"center\">\n<a target=\"_blank\">\n    <img src=\"https://img.shields.io/badge/Made%20with-Python-1f425f.svg\" alt=\"Python\">\n</a>\n<a target=\"_blank\">\n    <img src=\"https://img.shields.io/badge/code%20style-black-000000.svg\" alt=\"Black\">\n</a>\n<a target=\"_blank\">\n    <img src=\"https://badgen.net/badge/Open%20Source%20%3F/Yes%21/blue?icon=github\" alt=\"OpenSource\">\n</a>\n<a target=\"_blank\">\n    <img src=\"https://img.shields.io/badge/License-Apache%202.0-blue.svg\" alt=\"Apache\">\n</a>\n</p>\n\n---\n\n# Description\n\nThis project represents usage of [EthTx](https://github.com/ethtx/ethtx) decoding library in form of a website. If you\nare looking for implementation of the said decoding functionalities, please refer\nto [EthTx](https://github.com/ethtx/ethtx) repository.\n\n# Local environment\n\nHere is a list of steps to recreate local environment on <b>Ubuntu</b> distribution.\n\n1. Install needed packages using `apt`:\n\n    ```shell\n    apt install docker-compose python3-pip python3-dev pipenv make\n    apt install libxml2-dev libxslt1-dev gcc\n    ```\n2. Run:\n\n    ```shell\n    pipenv install\n    ```\n\n3. Copy `.env_sample` to `.env` and fill required field according to description\n\n    ```\n      # Proper nodes are required to run ethtx, provide connection strings for chains which will be used.\n      MAINNET_NODE_URL=https://geth-erigon-node:8545\n      # KOVAN_NODE_URL=\n      # RINKEBY_NODE_URL=\n\n      # EthTx supports multiple nodes, if one is unavailable, it will use others. You only need to specify them with a comma\n      # Example: MAINNET_NODE_URL=https://geth-erigon-node:8545,https://geth1-erigon-node:8545\n\n\n      # Etherscan API is used to get contract source code, required for decoding process\n      # You can get free key here https://etherscan.io/apis\n      ETHERSCAN_KEY=\n\n      # Optional. Those represent data required for connecting to mongoDB. It's used for caching semantics\n      # used in decoding process. But, it's not neccessary for running, If you don't want to use permanent\n      # db or setup mongo, leave those values, mongomock package is used to simulate in-memory mongo.\n      MONGO_CONNECTION_STRING=mongomock://localhost/ethtx\n\n      # Optional. Credentials for accessing semantics editor page, available under '/semantics/<str:address>'\n      ETHTX_ADMIN_USERNAME=admin\n      ETHTX_ADMIN_PASSWORD=admin\n\n      # Optional. Api key used for exposing\n      API_KEY=\n\n      # Optional. Valid values are ['production', 'staging', 'development']. Those mainly\n      # dictate what options are used for flask debugging and logging\n      ENV=development\n    ```\n\n4. Run\n    ```shell\n    PYTHONPATH=./ethtx_ce FLASK_APP=ethtx_ce/app/wsgi.py pipenv run flask run --host=0.0.0.0 --port 5000\n    ```\n   or\n    ```shell\n    make run-local\n    ```\n   This will setup new server on host 0.0.0.0 port 5000.\n5. Now `ethtx_ce` should be accessible through link [http://localhost:5000](http://localhost:5000)\n\nUse can also provided `docker-compose` for running this locally:\n\n```shell\ndocker-compose up\n```\n\nNote, this also need proper `.env` file to function properly.\n\n# .env file\n\nFor proper functioning, `.env` file is required containing all database and 3rd party providers configuration.\n`.env_sample` file is provided in repository with example values.\n\nParameters `[CHAIN_ID]_NODE_URL` should hold valid urls to ethereum nodes; Parameter `ETHERSCAN_KEY` should be equal to\nEtherscan API key assigned to user.\n\n# API\n\nThe EthTx APIs are provided as a community service and without warranty, so please use what you need and no more. We\nsupport `GET` requests.\n\n* **Decode transaction**\n\n  Returns decoded EthTx transaction, based on `chain_id` and transaction hash `tx_hash`\n\n    * **URL**\n      ```shell\n      /api/transactions/CHAIN_ID/TX_HASH\n      ```\n    * **Method**\n      `GET`\n    * **Authorization**\n        * Required:\n          header: `x-api-key=[string]` **OR** query parameter: `api_key=[string]`\n    * **URL Params**\n        * Required: `chain_id=[string]`,`tx_hash=[string]`\n    * **Example**\n      ```shell\n      curl --location --request GET 'http://0.0.0.0:5000/api/transactions/dsad/asd' \\\n      --header 'x-api-key: 05a2212d-9985-48d2-b54f-0fbc5ba28766'\n      ```\n\n\n* **Get Raw Semantic**\n\n  Returns raw semantic based on `chain_id` and sender/receiver `address`\n\n    * **URL**\n      ```shell\n      /api/semantics/CHAIN_ID/ADDRESS\n      ```\n    * **Method**\n      `GET`\n    * **Authorization**\n        * Required:\n          header: `x-api-key=[string]` **OR** query parameter: `api_key=[string]`\n    * **URL Params**\n        * Required:`chain_id=[string]`,`address=[string]`\n    * **Example**\n      ```shell\n      curl --location --request GET 'http://0.0.0.0:5000/api/semantics/dsad/asd' \\\n      --header 'x-api-key: 05a2212d-9985-48d2-b54f-0fbc5ba28766'\n      ```\n\n* **Info**\n\n  Returns information about the `EthTx`\n\n    * **URL**\n      ```shell\n      /api/info\n      ```\n    * **Method**\n      `GET`\n    * **Authorization**\n        * Required:\n          header: `x-api-key=[string]` **OR** query parameter: `api_key=[string]`\n    * **URL Params**\n        * None\n    * **Example**\n      ```shell\n      curl --location --request GET 'http://0.0.0.0:5000/api/info' \\\n      --header 'x-api-key: 05a2212d-9985-48d2-b54f-0fbc5ba28766'\n      ```"
  },
  {
    "path": "docker-compose.override.yml",
    "content": "version: \"3.6\"\nservices:\n  ethtx_ce:\n    ports:\n    - \"5000:5000\"\n    build:\n      context: .\n      dockerfile: Dockerfile\n\n    command: /start.sh\n\n  mongo:\n    ports:\n      - \"27017:27017\"\n  mongo-express:\n    depends_on:\n      - mongo\n    ports:\n      - \"8081:8081\""
  },
  {
    "path": "docker-compose.yaml",
    "content": "version: \"3.6\"\n\nservices:\n  ethtx_ce:\n    image: 'ethtx_ce:${TAG-latest}'\n    env_file:\n      - .env\n    depends_on:\n      - mongo\n    build:\n      context: .\n      dockerfile: Dockerfile\n\n  mongo:\n    image: mongo\n    environment:\n      - MONGO_INITDB_DATABASE=${MONGODB_DB}\n\n  mongo-express:\n    image: mongo-express\n    environment:\n      - ME_CONFIG_MONGODB_SERVER=mongo\n      - ME_CONFIG_MONGODB_PORT=27017\n      - ME_CONFIG_MONGODB_ENABLE_ADMIN=false\n      - ME_CONFIG_MONGODB_AUTH_DATABASE=${MONGODB_DB}\n      - ME_CONFIG_BASICAUTH_USERNAME=${MONGOEXPRESS_LOGIN}\n      - ME_CONFIG_BASICAUTH_PASSWORD=${MONGOEXPRESS_PASSWORD}\n    depends_on:\n      - mongo\n"
  },
  {
    "path": "ethtx_ce/.flake8",
    "content": "[flake8]\nmax-line-length = 130\nexclude = .git,__pycache__,__init__.py,.mypy_cache,.pytest_cache\nextend-ignore =\n    # See https://github.com/PyCQA/pycodestyle/issues/373\n    E203, F401, F403, F405\n"
  },
  {
    "path": "ethtx_ce/.gitignore",
    "content": "__pycache__\napp.egg-info\n"
  },
  {
    "path": "ethtx_ce/app/__init__.py",
    "content": "# Copyright 2021 DAI FOUNDATION (the original version https://github.com/daifoundation/ethtx_ce)\n# Copyright 2021-2022 Token Flow Insights SA (modifications to the original software as recorded\n# in the changelog https://github.com/EthTx/ethtx/blob/master/CHANGELOG.md)\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at: http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed\n# on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and limitations under the License.\n#\n# The product contains trademarks and other branding elements of Token Flow Insights SA which are\n# not licensed under the Apache 2.0 license. When using or reproducing the code, please remove\n# the trademark and/or other branding elements.\n"
  },
  {
    "path": "ethtx_ce/app/api/__init__.py",
    "content": "# Copyright 2021 DAI FOUNDATION (the original version https://github.com/daifoundation/ethtx_ce)\n# Copyright 2021-2022 Token Flow Insights SA (modifications to the original software as recorded\n# in the changelog https://github.com/EthTx/ethtx/blob/master/CHANGELOG.md)\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at: http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed\n# on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and limitations under the License.\n#\n# The product contains trademarks and other branding elements of Token Flow Insights SA which are\n# not licensed under the Apache 2.0 license. When using or reproducing the code, please remove\n# the trademark and/or other branding elements.\n\nfrom functools import wraps\nfrom typing import Dict, Type, Callable, Union, Optional\n\nfrom ethtx import EthTx\nfrom flask import Blueprint\nfrom flask import Flask\n\nfrom .. import factory\nfrom .decorators import auth_required\nfrom ..helpers import read_ethtx_versions\n\n\ndef create_app(\n    engine: EthTx, settings_override: Optional[Union[Dict, Type]] = None\n) -> Flask:\n    \"\"\"Returns API application instance.\"\"\"\n\n    app = factory.create_app(__name__, __path__, settings_override)\n    app.name = \"ethtx_ce/api\"\n\n    app.ethtx = engine  # init ethtx engine\n    read_ethtx_versions(app)\n\n    return app\n\n\ndef api_route(bp: Blueprint, *args, **kwargs):\n    kwargs.setdefault(\"strict_slashes\", False)\n\n    def decorator(f: Callable):\n        @bp.route(*args, **kwargs)\n        @auth_required\n        @wraps(f)\n        def wrapper(*args, **kwargs):\n            sc = 200\n            rv = f(*args, **kwargs)\n            if isinstance(rv, tuple):\n                sc = rv[1]\n                rv = rv[0]\n            return rv, sc\n\n        f.__name__ = str(id(f)) + f.__name__\n        return f\n\n    return decorator\n\n\n# avoid circular\nfrom .endpoints import *\nfrom .exceptions import exceptions_bp\n"
  },
  {
    "path": "ethtx_ce/app/api/decorators.py",
    "content": "# Copyright 2021 DAI FOUNDATION (the original version https://github.com/daifoundation/ethtx_ce)\n# Copyright 2021-2022 Token Flow Insights SA (modifications to the original software as recorded\n# in the changelog https://github.com/EthTx/ethtx/blob/master/CHANGELOG.md)\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at: http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed\n# on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and limitations under the License.\n#\n# The product contains trademarks and other branding elements of Token Flow Insights SA which are\n# not licensed under the Apache 2.0 license. When using or reproducing the code, please remove\n# the trademark and/or other branding elements.\n\nimport logging\nfrom functools import wraps\nfrom typing import Callable, Optional\n\nimport jsonpickle\nfrom flask import request, current_app, jsonify\n\nfrom ..exceptions import (\n    AuthorizationError,\n    PayloadTooLarge,\n    UnexpectedError,\n    InternalError,\n)\nfrom .utils import enable_direct, delete_bstrings\n\nlog = logging.getLogger(__name__)\n\njsonpickle.set_decoder_options('simplejson', use_decimal=True)\n\n\ndef auth_required(func: Callable):\n    \"\"\"api key  verification.\"\"\"\n\n    @wraps(func)\n    def check_auth(**kwargs):\n        api_key = request.headers.get(\"x-api-key\") or request.args.get(\"api_key\")\n        if api_key != current_app.config.get(\"API_KEY\"):\n            raise AuthorizationError(api_key)\n\n        return func(**kwargs)\n\n    return check_auth\n\n\ndef response(status: Optional[int] = 200):\n    \"\"\"\n    Return response with:\n    :param status: response status code, default: `200`\n    \"\"\"\n\n    def _response(f: Callable):\n        @wraps(f)\n        def wrapped(*args, **kwargs):\n            func = f(*args, **kwargs)\n\n            try:\n                data = jsonify(\n                    delete_bstrings(\n                        jsonpickle.decode(\n                            jsonpickle.encode(func, make_refs=False, unpicklable=False, use_decimal=True)\n                        )\n                    )\n                )\n            except TypeError as e:\n                log.critical(\"Response cannot be serialized. %s\", e)\n                raise InternalError()\n            except Exception as e:\n                log.exception(e)\n                raise UnexpectedError()\n\n            return data, status\n\n        return wrapped\n\n    return _response\n\n\n@enable_direct\ndef limit_content_length(max_length: Optional[int] = None):\n    \"\"\"\n    Limit content length. If not given:\n    The priority has app MAX_CONTENT_LENGTH value.\n    \"\"\"\n\n    def decorator(f):\n        @wraps(f)\n        def wrapper(*args, **kwargs):\n            cl = request.content_length\n            app_max_length = current_app.config.get(\"MAX_CONTENT_LENGTH\")\n            max_content_length = max_length if max_length else app_max_length\n\n            if cl is not None and cl > max_content_length:\n                raise PayloadTooLarge(\n                    content_length=cl, max_content_length=max_content_length\n                )\n            return f(*args, **kwargs)\n\n        return wrapper\n\n    return decorator\n"
  },
  {
    "path": "ethtx_ce/app/api/endpoints/__init__.py",
    "content": "# Copyright 2021 DAI FOUNDATION (the original version https://github.com/daifoundation/ethtx_ce)\n# Copyright 2021-2022 Token Flow Insights SA (modifications to the original software as recorded\n# in the changelog https://github.com/EthTx/ethtx/blob/master/CHANGELOG.md)\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at: http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed\n# on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and limitations under the License.\n#\n# The product contains trademarks and other branding elements of Token Flow Insights SA which are\n# not licensed under the Apache 2.0 license. When using or reproducing the code, please remove\n# the trademark and/or other branding elements.\n\nfrom .info import info_bp\nfrom .semantics import semantics_bp\nfrom .transactions import transactions_bp\n"
  },
  {
    "path": "ethtx_ce/app/api/endpoints/info.py",
    "content": "# Copyright 2021 DAI FOUNDATION (the original version https://github.com/daifoundation/ethtx_ce)\n# Copyright 2021-2022 Token Flow Insights SA (modifications to the original software as recorded\n# in the changelog https://github.com/EthTx/ethtx/blob/master/CHANGELOG.md)\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at: http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed\n# on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and limitations under the License.\n#\n# The product contains trademarks and other branding elements of Token Flow Insights SA which are\n# not licensed under the Apache 2.0 license. When using or reproducing the code, please remove\n# the trademark and/or other branding elements.\n\nfrom flask import Blueprint, current_app\n\nfrom .. import api_route\nfrom ..decorators import response\nfrom ...helpers import get_latest_ethtx_version\n\ninfo_bp = Blueprint(\"api_info\", __name__)\n\n\n@api_route(info_bp, \"/info\")\n@response(200)\ndef read_info():\n    \"\"\"Get info.\"\"\"\n    ethtx_version = current_app.config[\"ethtx_version\"]\n    latest_ethtx_version = get_latest_ethtx_version()\n\n    ethtx_ce_version = current_app.config[\"ethtx_ce_version\"]\n\n    return {\n        \"ethtx\": {\n            \"version\": ethtx_version,\n            \"is_latest\": ethtx_version == latest_ethtx_version,\n        },\n        \"ethtx_ce\": {\n            \"version\": ethtx_ce_version,\n        },\n    }\n"
  },
  {
    "path": "ethtx_ce/app/api/endpoints/semantics.py",
    "content": "# Copyright 2021 DAI FOUNDATION (the original version https://github.com/daifoundation/ethtx_ce)\n# Copyright 2021-2022 Token Flow Insights SA (modifications to the original software as recorded\n# in the changelog https://github.com/EthTx/ethtx/blob/master/CHANGELOG.md)\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at: http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed\n# on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and limitations under the License.\n#\n# The product contains trademarks and other branding elements of Token Flow Insights SA which are\n# not licensed under the Apache 2.0 license. When using or reproducing the code, please remove\n# the trademark and/or other branding elements.\n\nfrom typing import Optional\n\nfrom flask import Blueprint, current_app\n\nfrom .. import api_route\nfrom ..decorators import response\n\nsemantics_bp = Blueprint(\"api_semantics\", __name__)\n\n\n@api_route(semantics_bp, \"/semantics/<string:address>\")\n@api_route(semantics_bp, \"/semantics/<string:chain_id>/<string:address>\")\n@response(200)\ndef read_raw_semantic(address: str, chain_id: Optional[str] = None):\n    \"\"\"Get raw semantic.\"\"\"\n    raw_semantics = current_app.ethtx.semantics.get_semantics(\n        chain_id=chain_id, address=address\n    )\n    return raw_semantics.dict()\n"
  },
  {
    "path": "ethtx_ce/app/api/endpoints/transactions.py",
    "content": "# Copyright 2021 DAI FOUNDATION (the original version https://github.com/daifoundation/ethtx_ce)\n# Copyright 2021-2022 Token Flow Insights SA (modifications to the original software as recorded\n# in the changelog https://github.com/EthTx/ethtx/blob/master/CHANGELOG.md)\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at: http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed\n# on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and limitations under the License.\n#\n# The product contains trademarks and other branding elements of Token Flow Insights SA which are\n# not licensed under the Apache 2.0 license. When using or reproducing the code, please remove\n# the trademark and/or other branding elements.\n\nimport logging\nfrom typing import Optional\n\nfrom flask import Blueprint, current_app\n\nfrom .. import api_route\nfrom ..decorators import response\n\nlog = logging.getLogger(__name__)\ntransactions_bp = Blueprint(\"api_transactions\", __name__)\n\n\n@api_route(transactions_bp, \"/transactions/<string:tx_hash>\")\n@api_route(transactions_bp, \"/transactions/<string:chain_id>/<string:tx_hash>\")\n@response(200)\ndef read_decoded_transaction(tx_hash: str, chain_id: Optional[str] = None):\n    \"\"\"Decode transaction.\"\"\"\n    tx_hash = tx_hash if tx_hash.startswith(\"0x\") else \"0x\" + tx_hash\n\n    chain_id = chain_id or current_app.ethtx.default_chain\n    decoded_transaction = current_app.ethtx.decoders.decode_transaction(\n        chain_id=chain_id, tx_hash=tx_hash\n    )\n    decoded_transaction.metadata.timestamp = (\n        decoded_transaction.metadata.timestamp.strftime(\"%Y-%m-%d %H:%M:%S\")\n    )\n    return decoded_transaction.dict()\n"
  },
  {
    "path": "ethtx_ce/app/api/exceptions.py",
    "content": "# Copyright 2021 DAI FOUNDATION (the original version https://github.com/daifoundation/ethtx_ce)\n# Copyright 2021-2022 Token Flow Insights SA (modifications to the original software as recorded\n# in the changelog https://github.com/EthTx/ethtx/blob/master/CHANGELOG.md)\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at: http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed\n# on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and limitations under the License.\n#\n# The product contains trademarks and other branding elements of Token Flow Insights SA which are\n# not licensed under the Apache 2.0 license. When using or reproducing the code, please remove\n# the trademark and/or other branding elements.\n\nimport datetime\nimport logging\nfrom dataclasses import dataclass\nfrom http.client import responses\nfrom typing import Tuple, TypeVar\n\nfrom ethtx import exceptions as ethtx_exceptions\nfrom flask import Blueprint, request\nfrom web3.exceptions import TransactionNotFound\nfrom werkzeug.exceptions import HTTPException\n\nfrom .utils import as_dict\nfrom ..exceptions import *\n\nlog = logging.getLogger(__name__)\n\nexceptions_bp = Blueprint(\"exceptions\", __name__)\n\n\n@as_dict\n@dataclass\nclass BaseRequestException:\n    \"\"\"Base Request Exception\"\"\"\n\n    status: int\n    error: str\n    path: str\n    message: str = \"\"\n    timestamp: datetime.datetime.utcnow = None\n\n    def __post_init__(self):\n        \"\"\"Post init values.\"\"\"\n        self.error = str(self.error)\n        self.message = responses[self.status]\n        self.timestamp = datetime.datetime.utcnow()\n\n\nBaseErrorType = TypeVar(\"BaseErrorType\", bound=Tuple[BaseRequestException, int])\n\n\n@exceptions_bp.app_errorhandler(HTTPException)\ndef handle_all_http_exceptions(error: HTTPException) -> BaseErrorType:\n    \"\"\"All HTTP Exceptions handler.\"\"\"\n    return BaseRequestException(error.code, error.description, request.path), error.code\n\n\n@exceptions_bp.app_errorhandler(ethtx_exceptions.NodeConnectionException)\ndef node_connection_error(error) -> BaseErrorType:\n    \"\"\"EthTx - Node connection error.\"\"\"\n    return BaseRequestException(500, error, request.path), 500\n\n\n@exceptions_bp.app_errorhandler(ethtx_exceptions.ProcessingException)\ndef processing_error(error) -> BaseErrorType:\n    \"\"\"EthTx - Processing error.\"\"\"\n    return BaseRequestException(500, error, request.path), 500\n\n\n@exceptions_bp.app_errorhandler(ethtx_exceptions.InvalidTransactionHash)\ndef invalid_transaction_hash(error) -> BaseErrorType:\n    \"\"\"EthTx - Invalid transaction hash.\"\"\"\n    return BaseRequestException(400, error, request.path), 400\n\n\n@exceptions_bp.app_errorhandler(TransactionNotFound)\ndef transaction_not_found(error) -> BaseErrorType:\n    \"\"\"Could not find transaction.\"\"\"\n    return BaseRequestException(404, error, request.path), 404\n\n\n@exceptions_bp.app_errorhandler(AuthorizationError)\ndef authorization_error(error) -> BaseErrorType:\n    \"\"\"Unauthorized request.\"\"\"\n    return BaseRequestException(401, error, request.path), 401\n\n\n@exceptions_bp.app_errorhandler(MalformedRequest)\ndef malformed_request(error) -> BaseErrorType:\n    \"\"\"Wrong request.\"\"\"\n    return BaseRequestException(400, error, request.path), 400\n\n\n@exceptions_bp.app_errorhandler(PayloadTooLarge)\ndef payload_too_large(error) -> BaseErrorType:\n    \"\"\"Payload is too large.\"\"\"\n    return BaseRequestException(413, error, request.path), 413\n\n\n@exceptions_bp.app_errorhandler(ResourceLockedError)\ndef resource_locked_error(error) -> BaseErrorType:\n    \"\"\"Resource is locked.\"\"\"\n    return BaseRequestException(423, error, request.path), 423\n\n\n@exceptions_bp.app_errorhandler(Exception)\ndef unexpected_error(error) -> BaseErrorType:\n    \"\"\"Unexpected error.\"\"\"\n    log.exception(str(error))\n\n    return BaseRequestException(500, str(UnexpectedError()), request.path), 500\n"
  },
  {
    "path": "ethtx_ce/app/api/utils.py",
    "content": "# Copyright 2021 DAI FOUNDATION (the original version https://github.com/daifoundation/ethtx_ce)\n# Copyright 2021-2022 Token Flow Insights SA (modifications to the original software as recorded\n# in the changelog https://github.com/EthTx/ethtx/blob/master/CHANGELOG.md)\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at: http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed\n# on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and limitations under the License.\n#\n# The product contains trademarks and other branding elements of Token Flow Insights SA which are\n# not licensed under the Apache 2.0 license. When using or reproducing the code, please remove\n# the trademark and/or other branding elements.\n\nfrom dataclasses import asdict\nfrom functools import wraps\nfrom typing import Dict\nfrom decimal import Decimal\n\n\ndef enable_direct(decorator):\n    \"\"\"Decorator direct helper.\"\"\"\n\n    @wraps(decorator)\n    def wrapper(*args, **kwargs):\n        f = args[0]\n        if callable(f):\n            return decorator()(f)  # pass the function to be decorated\n        else:\n            return decorator(*args, **kwargs)  # pass the specified params\n\n    return wrapper\n\n\ndef as_dict(cls):\n    \"\"\"Return object as dict.\"\"\"\n\n    def wrapper(*args, **kwargs) -> Dict:\n        instance = cls(*args, **kwargs)\n        return asdict(instance)\n\n    return wrapper\n\n\ndef delete_bstrings(obj):\n    primitive = (str, bool, float, type(None))\n\n    if isinstance(obj, primitive):\n        return obj\n    elif isinstance(obj, int):\n        return str(obj)\n    elif isinstance(obj, Decimal):\n        if obj == obj.to_integral_value():\n            return str(obj)\n        else:\n            obj\n    elif type(obj) == bytes:\n        return obj.decode()\n    elif type(obj) == list:\n        for index, value in enumerate(obj):\n            obj[index] = delete_bstrings(value)\n    elif type(obj) == dict:\n        for index, value in obj.items():\n            obj[index] = delete_bstrings(value)\n    else:\n        raise Exception(\"Unknown type:\" + str(type(obj)))\n\n    return obj\n"
  },
  {
    "path": "ethtx_ce/app/config.py",
    "content": "# Copyright 2021 DAI FOUNDATION (the original version https://github.com/daifoundation/ethtx_ce)\n# Copyright 2021-2022 Token Flow Insights SA (modifications to the original software as recorded\n# in the changelog https://github.com/EthTx/ethtx/blob/master/CHANGELOG.md)\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at: http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed\n# on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and limitations under the License.\n#\n# The product contains trademarks and other branding elements of Token Flow Insights SA which are\n# not licensed under the Apache 2.0 license. When using or reproducing the code, please remove\n# the trademark and/or other branding elements.\n\nimport os\n\nfrom dotenv import load_dotenv, find_dotenv\n\nBASE_DIR = os.path.dirname(os.path.abspath(__file__))\nload_dotenv(find_dotenv(filename=\"../../.env\"))\n\n\nclass Config:\n    \"\"\"Base Config.\"\"\"\n\n    LOGGING_CONFIG = os.environ.get(\n        \"LOGGING_CONFIG\", os.path.join(BASE_DIR, \"../log_cfg.json\")\n    )\n    LOGGING_LOG_PATH = os.environ.get(\n        \"LOGGING_CONFIG\", os.path.join(BASE_DIR, \"../../tmp\")\n    )\n\n    API_KEY = os.getenv(\"API_KEY\", \"\")\n    MAX_CONTENT_LENGTH = 10 * 1024 * 1024\n\n    ETHTX_ADMIN_USERNAME = os.getenv(\"ETHTX_ADMIN_USERNAME\")\n    ETHTX_ADMIN_PASSWORD = os.getenv(\"ETHTX_ADMIN_PASSWORD\")\n\n\nclass ProductionConfig(Config):\n    \"\"\"Production Config.\"\"\"\n\n    ENV = \"production\"\n    FLASK_DEBUG = False\n    TESTING = False\n    PROPAGATE_EXCEPTIONS = True\n\n\nclass StagingConfig(Config):\n    \"\"\"Staging Config.\"\"\"\n\n    ENV = \"staging\"\n    FLASK_DEBUG = True\n    TESTING = False\n    PROPAGATE_EXCEPTIONS = True\n\n\nclass DevelopmentConfig(Config):\n    \"\"\"Development Config.\"\"\"\n\n    ENV = \"development\"\n    FLASK_DEBUG = True\n    TESTING = True\n    PROPAGATE_EXCEPTIONS = True\n"
  },
  {
    "path": "ethtx_ce/app/exceptions.py",
    "content": "# Copyright 2021 DAI FOUNDATION (the original version https://github.com/daifoundation/ethtx_ce)\n# Copyright 2021-2022 Token Flow Insights SA (modifications to the original software as recorded\n# in the changelog https://github.com/EthTx/ethtx/blob/master/CHANGELOG.md)\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at: http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed\n# on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and limitations under the License.\n#\n# The product contains trademarks and other branding elements of Token Flow Insights SA which are\n# not licensed under the Apache 2.0 license. When using or reproducing the code, please remove\n# the trademark and/or other branding elements.\n\nfrom typing import Optional, Union\n\n__all__ = [\n    \"AuthorizationError\",\n    \"MalformedRequest\",\n    \"PayloadTooLarge\",\n    \"MethodNotAllowed\",\n    \"ResourceLockedError\",\n    \"InternalError\",\n    \"UnexpectedError\",\n    \"FactoryAppException\",\n    \"EmptyResponseError\",\n]\n\n\nclass FactoryAppException(Exception):\n    \"\"\"Basic Factory App exception.\"\"\"\n\n\nclass UnexpectedError(Exception):\n    \"\"\"Internal Server Error.\"\"\"\n\n    def __init__(self):\n        super().__init__(\"Unexpected Error\")\n\n\nclass RequestError(Exception):\n    \"\"\"Request Error - basic class.\"\"\"\n\n\nclass AuthorizationError(RequestError):\n    \"\"\"Unauthorized requests.\"\"\"\n\n    def __init__(self, msg: Optional[str] = None):\n        super().__init__(\n            f\"The provided api key is invalid : {msg}.\"\n            if msg\n            else \"Api key is missing.\"\n        )\n\n\nclass MalformedRequest(RequestError):\n    \"\"\"Malformed Request Error.\"\"\"\n\n    def __init__(self, msg):\n        super().__init__(msg)\n\n\nclass PayloadTooLarge(RequestError):\n    \"\"\"Payload too large Error.\"\"\"\n\n    def __init__(self, content_length: Union[float, int], max_content_length: int):\n        super().__init__(\n            f\"The request is larger than the server is willing or able to process.\"\n            f\" Request length: {content_length}, but allowed is: {max_content_length}.\"\n        )\n\n\nclass MethodNotAllowed(RequestError):\n    \"\"\"Method not allowed.\"\"\"\n\n    def __init__(self, method: str):\n        super().__init__(f\"Method: {method} not allowed.\")\n\n\nclass ResourceLockedError(RequestError):\n    \"\"\"Resource is locked.\"\"\"\n\n    def __init__(self):\n        super().__init__(\"The resource that is being accessed is locked.\")\n\n\nclass EmptyResponseError(RequestError):\n    \"\"\"Response is empty.\"\"\"\n\n    def __init__(self, msg: str):\n        super().__init__(msg)\n\n\nclass InternalError(RequestError):\n    \"\"\"Validation Error\"\"\"\n\n    def __init__(self):\n        super().__init__(\n            \"The request was well-formed but server could not properly decode transaction.\"\n        )\n"
  },
  {
    "path": "ethtx_ce/app/factory.py",
    "content": "# Copyright 2021 DAI FOUNDATION (the original version https://github.com/daifoundation/ethtx_ce)\n# Copyright 2021-2022 Token Flow Insights SA (modifications to the original software as recorded\n# in the changelog https://github.com/EthTx/ethtx/blob/master/CHANGELOG.md)\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at: http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed\n# on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and limitations under the License.\n#\n# The product contains trademarks and other branding elements of Token Flow Insights SA which are\n# not licensed under the Apache 2.0 license. When using or reproducing the code, please remove\n# the trademark and/or other branding elements.\n\nimport os\nfrom typing import Optional, Dict\n\nfrom flask import Flask\n\nfrom .config import Config\nfrom .helpers import class_import, register_blueprints\nfrom .logger import setup_logging\n\nenv = os.getenv(\"ENV\", \"development\").capitalize()\nconfig_class = f\"app.config.{env}Config\"\nconfig: Config = class_import(config_class)\n\n\ndef create_app(\n    package_name: str,\n    package_path: str,\n    settings_override: Optional[Dict] = None,\n    **app_kwargs,\n) -> Flask:\n    \"\"\"\n    Returns a :class:`Flask` application instance\n    :param package_name: application package name\n    :param package_path: application package path\n    :param settings_override: a dictionary of settings to override\n    :param app_kwargs: additional app kwargs\n    \"\"\"\n    app = Flask(__name__, instance_relative_config=True, **app_kwargs)\n\n    app.config.from_object(config)\n    setup_logging(app=app)\n    app.config.from_object(settings_override)\n\n    register_blueprints(app, package_name, package_path)\n\n    return app\n"
  },
  {
    "path": "ethtx_ce/app/frontend/__init__.py",
    "content": "# Copyright 2021 DAI FOUNDATION (the original version https://github.com/daifoundation/ethtx_ce)\n# Copyright 2021-2022 Token Flow Insights SA (modifications to the original software as recorded\n# in the changelog https://github.com/EthTx/ethtx/blob/master/CHANGELOG.md)\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at: http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed\n# on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and limitations under the License.\n#\n# The product contains trademarks and other branding elements of Token Flow Insights SA which are\n# not licensed under the Apache 2.0 license. When using or reproducing the code, please remove\n# the trademark and/or other branding elements.\n\nfrom functools import wraps\nfrom typing import Callable, Dict, Optional, Union, Type\n\nfrom ethtx import EthTx\nfrom flask import Blueprint, Flask\n\n\nfrom .. import factory\nfrom ..helpers import read_ethtx_versions\n\n\ndef create_app(\n    engine: EthTx, settings_override: Optional[Union[Dict, Type]] = None\n) -> Flask:\n    \"\"\"Returns Frontend app instance.\"\"\"\n    app = factory.create_app(\n        __name__,\n        __path__,\n        settings_override,\n        template_folder=\"frontend/templates\",\n        static_folder=\"frontend/static\",\n    )\n    app.name = \"ethtx_ce/frontend\"\n\n    app.jinja_env.trim_blocks = True\n    app.jinja_env.lstrip_blocks = True\n\n    app.ethtx = engine  # init ethtx engine\n    read_ethtx_versions(app)\n\n    return app\n\n\ndef frontend_route(bp: Blueprint, *args, **kwargs):\n    \"\"\"Route in blueprint context.\"\"\"\n\n    def decorator(f: Callable):\n        @bp.route(*args, **kwargs)\n        @wraps(f)\n        def wrapper(*args, **kwargs):\n            return f(*args, **kwargs)\n\n        f.__name__ = str(id(f)) + f.__name__\n        return f\n\n    return decorator\n"
  },
  {
    "path": "ethtx_ce/app/frontend/deps.py",
    "content": "# Copyright 2021 DAI FOUNDATION (the original version https://github.com/daifoundation/ethtx_ce)\n# Copyright 2021-2022 Token Flow Insights SA (modifications to the original software as recorded\n# in the changelog https://github.com/EthTx/ethtx/blob/master/CHANGELOG.md)\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at: http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed\n# on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and limitations under the License.\n#\n# The product contains trademarks and other branding elements of Token Flow Insights SA which are\n# not licensed under the Apache 2.0 license. When using or reproducing the code, please remove\n# the trademark and/or other branding elements.\n\nimport json\nimport logging\nimport re\nimport time\nfrom secrets import compare_digest\nfrom typing import Optional\n\nimport requests\nfrom flask import request\nfrom flask_httpauth import HTTPBasicAuth\n\nfrom ..config import Config\n\nlog = logging.getLogger(__name__)\n\nauth = HTTPBasicAuth()\n\neth_price: Optional[float] = None\neth_price_update: Optional[float] = None\n\n\n@auth.verify_password\ndef verify_password(username: str, password: str) -> bool:\n    \"\"\"Verify user, return bool.\"\"\"\n    return username == Config.ETHTX_ADMIN_USERNAME and compare_digest(\n        password, Config.ETHTX_ADMIN_PASSWORD\n    )\n\n\ndef get_eth_price() -> Optional[float]:\n    \"\"\"\n    Get current ETH price from coinbase.com\n    Cache price for 60 seconds.\n    \"\"\"\n    global eth_price, eth_price_update\n\n    current_time = time.time()\n    if (\n        eth_price is None\n        or eth_price_update is None\n        or (current_time - eth_price_update) > 60\n    ):\n        response = requests.get(\n            \"https://api.coinbase.com/v2/prices/ETH-USD/buy\", timeout=2\n        )\n        if response.status_code == 200:\n            eth_price = float(json.loads(response.content)[\"data\"][\"amount\"])\n            eth_price_update = time.time()\n\n    return eth_price\n\n\ndef extract_tx_hash_from_req() -> str:\n    \"\"\"Extract tx hash from request url.\"\"\"\n    hash_match = re.findall(r\"(0x)?([A-Fa-f0-9]{64})\", request.url)\n\n    return (\n        f\"{hash_match[0][0]}{hash_match[0][1]}\"\n        if hash_match and len(hash_match[0]) == 2\n        else \"\"\n    )\n"
  },
  {
    "path": "ethtx_ce/app/frontend/exceptions.py",
    "content": "# Copyright 2021 DAI FOUNDATION (the original version https://github.com/daifoundation/ethtx_ce)\n# Copyright 2021-2022 Token Flow Insights SA (modifications to the original software as recorded\n# in the changelog https://github.com/EthTx/ethtx/blob/master/CHANGELOG.md)\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at: http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed\n# on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and limitations under the License.\n#\n# The product contains trademarks and other branding elements of Token Flow Insights SA which are\n# not licensed under the Apache 2.0 license. When using or reproducing the code, please remove\n# the trademark and/or other branding elements.\n\nimport logging\nfrom functools import wraps\nfrom typing import Callable, Optional\n\nfrom ethtx import exceptions as ethtx_exceptions\nfrom flask import Blueprint, render_template\nfrom web3.exceptions import TransactionNotFound\nfrom werkzeug.exceptions import HTTPException\n\nfrom .deps import extract_tx_hash_from_req\nfrom ..exceptions import *\n\nlog = logging.getLogger(__name__)\n\nexceptions_bp = Blueprint(\"exceptions\", __name__)\n\n\ndef render_error_page(status: Optional[int] = 500):\n    \"\"\"Render error page.\"\"\"\n\n    def _render_error_page(f: Callable):\n        @wraps(f)\n        def wrapper(*args, **kwargs):\n            error = f(*args, **kwargs)\n            status_code = status\n            if isinstance(error, HTTPException):\n                error, status_code = error.description, error.code\n            return (\n                render_template(\n                    \"exception.html\",\n                    status_code=status_code,\n                    error=error,\n                    tx_hash=extract_tx_hash_from_req(),\n                ),\n                status_code,\n            )\n\n        return wrapper\n\n    return _render_error_page\n\n\n@exceptions_bp.app_errorhandler(HTTPException)\n@render_error_page()\ndef handle_all_http_exceptions(error: HTTPException) -> HTTPException:\n    \"\"\"All HTTP Exceptions handler.\"\"\"\n    return error\n\n\n@exceptions_bp.app_errorhandler(ethtx_exceptions.NodeConnectionException)\n@render_error_page(500)\ndef node_connection_error(error) -> str:\n    \"\"\"EthTx - Node connection error.\"\"\"\n    return error\n\n\n@exceptions_bp.app_errorhandler(ethtx_exceptions.ProcessingException)\n@render_error_page(500)\ndef processing_error(error) -> str:\n    \"\"\"EthTx - Processing error.\"\"\"\n    return error\n\n\n@exceptions_bp.app_errorhandler(ethtx_exceptions.InvalidTransactionHash)\n@render_error_page(400)\ndef invalid_transaction_hash(error) -> str:\n    \"\"\"EthTx - Invalid transaction hash.\"\"\"\n    return error\n\n\n@exceptions_bp.app_errorhandler(TransactionNotFound)\n@render_error_page(404)\ndef transaction_not_found(error) -> str:\n    \"\"\"Could not find transaction.\"\"\"\n    return error\n\n\n@exceptions_bp.app_errorhandler(AuthorizationError)\n@render_error_page(401)\ndef authorization_error(error) -> str:\n    \"\"\"Unauthorized request.\"\"\"\n    return error\n\n\n@exceptions_bp.app_errorhandler(MalformedRequest)\n@render_error_page(400)\ndef malformed_request(error) -> str:\n    \"\"\"Wrong request.\"\"\"\n    return error\n\n\n@exceptions_bp.app_errorhandler(PayloadTooLarge)\n@render_error_page(413)\ndef payload_too_large(error) -> str:\n    \"\"\"Payload is too large.\"\"\"\n    return error\n\n\n@exceptions_bp.app_errorhandler(ResourceLockedError)\n@render_error_page(423)\ndef resource_locked_error(error) -> str:\n    \"\"\"Resource is locked.\"\"\"\n    return error\n\n\n@exceptions_bp.app_errorhandler(EmptyResponseError)\n@render_error_page(404)\ndef empty_response(error) -> str:\n    \"\"\"Response is empty.\"\"\"\n    return error\n\n\n@exceptions_bp.app_errorhandler(Exception)\n@render_error_page(500)\ndef unexpected_error(error) -> str:\n    \"\"\"Unexpected error.\"\"\"\n    log.exception(str(error))\n\n    return str(UnexpectedError())\n"
  },
  {
    "path": "ethtx_ce/app/frontend/semantics.py",
    "content": "# Copyright 2021 DAI FOUNDATION (the original version https://github.com/daifoundation/ethtx_ce)\n# Copyright 2021-2022 Token Flow Insights SA (modifications to the original software as recorded\n# in the changelog https://github.com/EthTx/ethtx/blob/master/CHANGELOG.md)\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at: http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed\n# on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and limitations under the License.\n#\n# The product contains trademarks and other branding elements of Token Flow Insights SA which are\n# not licensed under the Apache 2.0 license. When using or reproducing the code, please remove\n# the trademark and/or other branding elements.\n\nfrom __future__ import annotations\n\nimport json\nimport logging\nfrom typing import Optional, List, Dict\n\nfrom ethtx import EthTx\nfrom ethtx.models.semantics_model import (\n    AddressSemantics,\n    ContractSemantics,\n    ERC20Semantics,\n)\nfrom ethtx.models.semantics_model import (\n    EventSemantics,\n    FunctionSemantics,\n    TransformationSemantics,\n    ParameterSemantics,\n)\nfrom flask import Blueprint, render_template, current_app, request, jsonify\nfrom web3 import Web3\n\nfrom . import frontend_route\nfrom .deps import auth\nfrom ..exceptions import EmptyResponseError\n\nbp = Blueprint(\"semantics\", __name__)\n\nlog = logging.getLogger(__name__)\n\n\n@frontend_route(bp, \"/semantics/<string:address>/\")\n@frontend_route(bp, \"/semantics/<string:chain_id>/<string:address>/\")\n@auth.login_required\ndef semantics(address: str, chain_id: Optional[str] = None) -> show_semantics_page:\n    raw_semantics = current_app.ethtx.semantics.get_semantics(\n        chain_id=chain_id or current_app.ethtx._default_chain, address=address\n    )\n\n    return show_semantics_page(raw_semantics)\n\n\n@frontend_route(bp, \"/reload\", methods=[\"POST\"])\n@auth.login_required\ndef reload_semantics():\n    \"\"\"Reload raw semantic.\"\"\"\n    data = json.loads(request.data)\n\n    ethtx: EthTx = current_app.ethtx\n    ethtx.semantics.database._addresses.delete_one({\"address\": data[\"address\"]})\n    ethtx.semantics.get_semantics.cache_clear()\n    ethtx.semantics.get_semantics(\n        data[\"chain_id\"] if data.get(\"chain_id\") else current_app.ethtx._default_chain,\n        data[\"address\"],\n    )\n\n    return \"ok\"\n\n\n@frontend_route(bp, \"/save\", methods=[\"POST\"])\n@auth.login_required\ndef semantics_save():\n    data = json.loads(request.data)\n    return _semantics_save(data)\n\n\n@frontend_route(bp, \"/poke\", methods=[\"POST\"])\n@auth.login_required\ndef poke_abi():\n    data = json.loads(request.data)\n    return _poke_abi(data)\n\n\ndef show_semantics_page(data: AddressSemantics) -> render_template:\n    if data:\n\n        data_dict = data.dict()\n\n        address = data.address\n        chain_id = data.chain_id\n        name = data.name or address\n\n        if data.is_contract:\n            events = data_dict[\"contract\"][\"events\"] or {}\n            functions = data_dict[\"contract\"][\"functions\"] or {}\n            transformations = data_dict[\"contract\"][\"transformations\"] or {}\n            code_hash = data.contract.code_hash\n            contract_name = data.contract.name\n        else:\n            events = {}\n            functions = {}\n            transformations = {}\n            code_hash = \"EOA\"\n            contract_name = address\n\n        standard = data.standard\n        # ToDo: make it more universal\n\n        if standard == \"ERC20\":\n            standard_info = data_dict[\"erc20\"] or {}\n        elif standard == \"ERC721\":\n            standard_info = {}\n        else:\n            standard_info = {}\n\n        metadata = dict(\n            label=name,\n            chain=chain_id,\n            contract=dict(\n                name=contract_name,\n                code_hash=code_hash,\n                standard=dict(name=standard, data=standard_info),\n            ),\n        )\n\n        return (\n            render_template(\n                \"semantics.html\",\n                address=address,\n                events=events,\n                functions=functions,\n                transformations=transformations,\n                metadata=metadata,\n            ),\n            200,\n        )\n\n    raise EmptyResponseError(\n        \"The semantics are empty. It probably means that the given address \"\n        \"has not been decoded before or address is incorrect.\"\n    )\n\n\ndef _parameters_semantics(parameters: List[Dict]) -> List[ParameterSemantics]:\n    parameters_semantics_list = []\n    if parameters:\n        for parameter in parameters:\n            parameters_semantics_list.append(\n                ParameterSemantics(\n                    parameter_name=parameter.get(\"parameter_name\"),\n                    parameter_type=parameter.get(\"parameter_type\"),\n                    components=parameter.get(\"components\", []),\n                    indexed=parameter.get(\"indexed\", False),\n                    dynamic=parameter.get(\"dynamic\", False),\n                )\n            )\n\n    return parameters_semantics_list\n\n\ndef _semantics_save(data):\n    try:\n        address = data.get(\"address\")\n        metadata = data.get(\"metadata\")\n        events = data.get(\"events\")\n        functions = data.get(\"functions\")\n        transformations = data.get(\"transformations\")\n        standard_name = None\n        erc20_semantics = None\n\n        if metadata.get(\"contract\"):\n\n            events_semantics = dict()\n            functions_semantics = dict()\n            transformations_semantics = dict()\n\n            for event in events.values():\n                events_semantics[event.get(\"signature\")] = EventSemantics(\n                    signature=event.get(\"signature\"),\n                    anonymous=event.get(\"anonymous\"),\n                    name=event.get(\"name\"),\n                    parameters=_parameters_semantics(event.get(\"parameters\")),\n                )\n\n            for function in functions.values():\n                functions_semantics[function.get(\"signature\")] = FunctionSemantics(\n                    signature=function.get(\"signature\"),\n                    name=function.get(\"name\"),\n                    inputs=_parameters_semantics(function.get(\"inputs\")),\n                    outputs=_parameters_semantics(function.get(\"outputs\")),\n                )\n\n            for signature, transformation in transformations.items():\n                transformations_semantics[signature] = dict()\n                for parameter_name, parameter_transformation in transformation:\n                    transformations_semantics[signature][\n                        parameter_name\n                    ] = TransformationSemantics(\n                        transformed_name=parameter_transformation.get(\n                            \"transformed_name\"\n                        ),\n                        transformed_type=parameter_transformation.get(\n                            \"transformed_type\"\n                        ),\n                        transformation=parameter_transformation.get(\"transformation\"),\n                    )\n\n            standard_name = metadata[\"contract\"][\"standard\"][\"name\"]\n            if standard_name == \"ERC20\":\n                erc20_data = metadata[\"contract\"][\"standard\"].get(\"data\")\n                if erc20_data:\n                    erc20_semantics = ERC20Semantics(\n                        name=erc20_data.get(\"name\"),\n                        symbol=erc20_data.get(\"symbol\"),\n                        decimals=erc20_data.get(\"decimals\"),\n                    )\n\n            contract_semantics = ContractSemantics(\n                code_hash=metadata[\"contract\"].get(\"code_hash\"),\n                name=metadata[\"contract\"].get(\"name\"),\n                events=events_semantics,\n                functions=functions_semantics,\n                transformations=transformations_semantics,\n            )\n\n        else:\n            contract_semantics = None\n\n        address_semantics = AddressSemantics(\n            chain_id=metadata.get(\"chain\"),\n            address=address,\n            name=metadata.get(\"label\"),\n            is_contract=contract_semantics is not None,\n            contract=contract_semantics,\n            standard=standard_name,\n            erc20=erc20_semantics,\n        )\n\n        current_app.ethtx.semantics.update_semantics(semantics=address_semantics)\n        current_app.ethtx.semantics.get_semantics.cache_clear()\n        current_app.ethtx.semantics.get_event_abi.cache_clear()\n        current_app.ethtx.semantics.get_anonymous_event_abi.cache_clear()\n        current_app.ethtx.semantics.get_transformations.cache_clear()\n        current_app.ethtx.semantics.get_function_abi.cache_clear()\n        current_app.ethtx.semantics.get_constructor_abi.cache_clear()\n        current_app.ethtx.semantics.check_is_contract.cache_clear()\n        current_app.ethtx.semantics.get_standard.cache_clear()\n\n        result = \"ok\"\n\n    except Exception as e:\n        logging.exception(\"Semantics save error: %s\" % e)\n        result = \"error\"\n\n    return jsonify(result=result)\n\n\ndef _poke_abi(data):\n    # helper function decoding contract ABI\n    def _parse_abi(json_abi):\n\n        # helper function to recursively parse parameters\n        def _parse_parameters(parameters):\n\n            comp_canonical = \"(\"\n            comp_inputs = list()\n\n            for i, parameter in enumerate(parameters):\n                argument = dict(\n                    parameter_name=parameter[\"name\"], parameter_type=parameter[\"type\"]\n                )\n\n                if parameter[\"type\"][:5] == \"tuple\":\n                    sub_canonical, sub_components = _parse_parameters(\n                        parameter[\"components\"]\n                    )\n                    comp_canonical += sub_canonical + parameter[\"type\"][5:]\n                    argument[\"components\"] = sub_components\n                else:\n                    comp_canonical += parameter[\"type\"]\n                    sub_components = []\n\n                if i < len(parameters) - 1:\n                    comp_canonical += \",\"\n\n                if (\n                    parameter[\"type\"] in (\"string\", \"bytes\")\n                    or parameter[\"type\"][-2:] == \"[]\"\n                ):\n                    argument[\"dynamic\"] = True\n                elif parameter[\"type\"] == \"tuple\":\n                    argument[\"dynamic\"] = any(c[\"dynamic\"] for c in sub_components)\n                else:\n                    argument[\"dynamic\"] = False\n\n                if \"indexed\" in parameter:\n                    argument[\"indexed\"] = parameter[\"indexed\"]\n\n                comp_inputs.append(argument)\n\n            comp_canonical += \")\"\n\n            return comp_canonical, comp_inputs\n\n        functions = dict()\n        events = dict()\n\n        for item in json_abi:\n            if \"type\" in item:\n\n                # parse contract functions\n                if item[\"type\"] == \"constructor\":\n                    _, inputs = _parse_parameters(item[\"inputs\"])\n                    functions[\"constructor\"] = dict(\n                        signature=\"constructor\",\n                        name=\"constructor\",\n                        inputs=inputs,\n                        outputs=[],\n                    )\n                elif item[\"type\"] == \"fallback\":\n                    functions[\"fallback\"] = {}\n\n                elif item[\"type\"] == \"function\":\n                    canonical, inputs = _parse_parameters(item[\"inputs\"])\n                    canonical = item[\"name\"] + canonical\n                    function_hash = Web3.sha3(text=canonical).hex()\n                    signature = function_hash[0:10]\n\n                    _, outputs = _parse_parameters(item[\"outputs\"])\n\n                    functions[signature] = dict(\n                        signature=signature,\n                        name=item[\"name\"],\n                        inputs=inputs,\n                        outputs=outputs,\n                    )\n\n                # parse contract events\n                elif item[\"type\"] == \"event\":\n                    canonical, parameters = _parse_parameters(item[\"inputs\"])\n                    canonical = item[\"name\"] + canonical\n                    event_hash = Web3.sha3(text=canonical).hex()\n                    signature = event_hash\n\n                    events[signature] = dict(\n                        signature=signature,\n                        name=item[\"name\"],\n                        anonymous=item[\"anonymous\"],\n                        parameters=parameters,\n                    )\n\n        return functions, events\n\n    try:\n\n        address = data[\"address\"]\n        chash = data[\"chash\"]\n        network = data[\"network\"]\n        name = data[\"name\"]\n        standard = json.loads(data[\"standard\"])\n        abi = json.loads(data[\"abi\"])\n\n        if abi and abi != []:\n\n            is_contract = True\n            functions, events = _parse_abi(abi)\n\n            events_semantics = dict()\n            for event in events.values():\n                events_semantics[event.get(\"signature\")] = EventSemantics(\n                    signature=event.get(\"signature\"),\n                    anonymous=event.get(\"anonymous\"),\n                    name=event.get(\"name\"),\n                    parameters=_parameters_semantics(event.get(\"parameters\")),\n                )\n\n            functions_semantics = dict()\n            for function in functions.values():\n                functions_semantics[function.get(\"signature\")] = FunctionSemantics(\n                    signature=function.get(\"signature\"),\n                    name=function.get(\"name\"),\n                    inputs=_parameters_semantics(function.get(\"inputs\")),\n                    outputs=_parameters_semantics(function.get(\"outputs\")),\n                )\n\n            contract_semantics = ContractSemantics(\n                code_hash=chash,\n                name=name,\n                events=events_semantics,\n                functions=functions_semantics,\n                transformations=dict(),\n            )\n\n        else:\n\n            is_contract = False\n            contract_semantics = ContractSemantics(\n                code_hash=\"0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470\",\n                name=\"EOA\",\n                events=dict(),\n                functions=dict(),\n                transformations=dict(),\n            )\n\n        address_semantics = AddressSemantics(\n            chain_id=network,\n            address=address,\n            name=name,\n            is_contract=is_contract,\n            contract=contract_semantics,\n            standard=standard.get(\"name\"),\n            erc20=ERC20Semantics(\n                name=standard[\"data\"].get(\"name\"),\n                symbol=standard[\"data\"].get(\"symbol\"),\n                decimals=standard[\"data\"].get(\"decimals\"),\n            )\n            if standard.get(\"name\") == \"ERC20\"\n            else None,\n        )\n\n        current_app.ethtx.semantics.update_semantics(semantics=address_semantics)\n        current_app.ethtx.semantics.get_semantics.cache_clear()\n        current_app.ethtx.semantics.get_event_abi.cache_clear()\n        current_app.ethtx.semantics.get_anonymous_event_abi.cache_clear()\n        current_app.ethtx.semantics.get_transformations.cache_clear()\n        current_app.ethtx.semantics.get_function_abi.cache_clear()\n        current_app.ethtx.semantics.get_constructor_abi.cache_clear()\n        current_app.ethtx.semantics.check_is_contract.cache_clear()\n        current_app.ethtx.semantics.get_standard.cache_clear()\n\n        logging.info(f\"ABI for {address} decoded.\")\n\n        result = \"ok\"\n\n    except Exception as e:\n        logging.exception(\"ABI retrieval error: %s\" % e)\n        result = \"error\"\n\n    return jsonify(result=result)\n"
  },
  {
    "path": "ethtx_ce/app/frontend/static/ethtx.new.css",
    "content": "/*Copyright 2021 DAI FOUNDATION (the original version https://github.com/daifoundation/ethtx_ce)*/\n/*Copyright 2021-2022 Token Flow Insights SA (modifications to the original software as recorded*/\n/*in the changelog https://github.com/EthTx/ethtx/blob/master/CHANGELOG.md)*/\n\n/*Licensed under the Apache License, Version 2.0 (the \"License\");*/\n/*you may not use this file except in compliance with the License.*/\n/*You may obtain a copy of the License at: http://www.apache.org/licenses/LICENSE-2.0*/\n\n/*Unless required by applicable law or agreed to in writing, software distributed under the License is distributed*/\n/*on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.*/\n/*See the License for the specific language governing permissions and limitations under the License.*/\n\n/*The product contains trademarks and other branding elements of Token Flow Insights SA which are*/\n/*not licensed under the Apache 2.0 license. When using or reproducing the code, please remove*/\n/*the trademark and/or other branding elements.*/\n\n\nbody {\n    font-family: monospace;\n    margin: 5px;\n    background-color: whitesmoke;\n    width: 100%;\n}\n\n* {\n    margin: 0;\n    padding: 0;\n}\n\nh3 {\n    margin: 11px;\n}\n\np {\n    padding-bottom: 50px;\n}\n\n.my_p {\n    margin: 2px 2px 2px 11px;\n    border: 1px #ccc solid;\n    border-radius: 5px;\n    padding: 2px 5px;\n    white-space: nowrap;\n    display: inline-block;\n}\n\na:link,\na:visited {\n    color: black;\n    text-decoration: none;\n}\n\n.delegate a:link,\n.delegate a:visited {\n    color: darkorange;\n    text-decoration: none;\n}\n\n.table {\n    margin-left: 11px;\n    border: solid 1px #bbbbbb;\n    border-collapse: collapse;\n    border-spacing: 0;\n}\n\n.table thead th {\n    background-color: #ddd;\n    border: solid 1px #bbbbbb;\n    color: #444444;\n    padding: 3px 10px 3px 10px;\n    text-align: left;\n    text-shadow: 1px 1px 1px #fff;\n}\n\n.table tbody td {\n    border: solid 1px #bbbbbb;\n    color: #333;\n    padding: 3px 10px 3px 10px;\n    text-shadow: 1px 1px 1px #fff;\n    white-space: nowrap;\n}\n\n.content {\n    width: 500px;\n    margin: auto;\n}\n\n.transaction-info {\n    margin: 5px 0 4px 13px;\n}\n\n.transaction-hash {\n    color: darkgreen;\n}\n\n.events tbody td {\n    border: 1px #ccc solid;\n    padding: 5px 10px 2px 10px;\n}\n\n.calls table {\n    border: transparent;\n}\n\n.calls tbody td {\n    border: transparent;\n    padding: 5px 10px 2px 10px;\n}\n\n.l2txs table {\n    border: transparent;\n}\n\n.l2txs tbody td {\n    border: transparent;\n    padding: 5px 10px 2px 10px;\n}\n\n#tree .ui-fancytree ul {\n    padding-left: 20px;\n}\n\n#tree .ui-fancytree li {\n    list-style-type: none;\n    margin: 5px;\n    position: relative;\n}\n\n#tree .ui-fancytree li::before {\n    content: \"\";\n    position: absolute;\n    top: -7px;\n    left: 2px;\n    border-left: 1px solid #ccc;\n    border-bottom: 1px solid #ccc;\n    border-radius: 0 0 0 0;\n    width: 20px;\n    height: 15px;\n}\n\n#tree .ui-fancytree li::after {\n    position: absolute;\n    content: \"\";\n    top: 8px;\n    left: 2px;\n    border-left: 1px solid #ccc;\n    border-top: 1px solid #ccc;\n    border-radius: 0 0 0 0;\n    width: 20px;\n    height: 100%;\n}\n\n#tree .ui-fancytree li:last-child::after {\n    display: none;\n}\n\n#tree .ui-fancytree li:last-child:before {\n    border-radius: 0 0 0 5px;\n}\n\n#tree ul.ui-fancytree > li:first-child::before {\n    display: none;\n}\n\n#tree ul.ui-fancytree > li:first-child::after {\n    border-radius: 5px 0 0 0;\n}\n\n#tree .ui-fancytree li p {\n    border: 1px #ccc solid;\n    border-radius: 5px;\n    padding: 2px 5px;\n    white-space: nowrap;\n    display: inline-block;\n}\n\n#tree .ui-fancytree li p:hover,\n#tree .ui-fancytree li p:hover + ul li p,\n#tree .ui-fancytree li p:focus,\n#tree .ui-fancytree li p:focus + ul li p {\n    background: #eee;\n    color: #000;\n    border: 1px solid #aaa;\n}\n\n#tree .ui-fancytree li p:hover + ul li::after,\n#tree .ui-fancytree li p:focus + ul li::after,\n#tree .ui-fancytree li p:hover + ul li::before,\n#tree .ui-fancytree li p:focus + ul li::before,\n#tree .ui-fancytree li p:hover + ul::before,\n#tree .ui-fancytree li p:focus + ul::before,\n#tree .ui-fancytree li p:hover + ul ul::before,\n#tree .ui-fancytree li p:focus + ul ul::before {\n    border-color: #999;\n}\n\n#tree .ui-fancytree .fancytree-icon {\n    display: none;\n}\n\n#tree .fancytree-plain.fancytree-container.fancytree-treefocus span.fancytree-focused span.fancytree-title {\n    border-color: transparent;\n}\n\n#tree .fancytree-plain span.fancytree-node span.fancytree-title {\n    background-color: transparent;\n    border-color: transparent;\n}\n\n#tree ul.fancytree-container {\n    background-color: transparent;\n    border: none;\n    outline: none;\n}\n\n#tree .fancytree-expander {\n    position: relative;\n    z-index: 2;\n    top: -2px;\n    left: -5px;\n}\n\n#tree * {\n    font-family: monospace;\n}\n\n#tx_tree .ui-fancytree ul {\n    padding-left: 20px;\n}\n\n#tx_tree .ui-fancytree li {\n    list-style-type: none;\n    margin: 5px;\n    position: relative;\n}\n\n#tx_tree .ui-fancytree li::before {\n    content: \"\";\n    position: absolute;\n    top: -7px;\n    left: 2px;\n    border-left: 1px solid #ccc;\n    border-bottom: 1px solid #ccc;\n    border-radius: 0 0 0 0;\n    width: 20px;\n    height: 15px;\n}\n\n#tx_tree .ui-fancytree li::after {\n    position: absolute;\n    content: \"\";\n    top: 8px;\n    left: 2px;\n    border-left: 1px solid #ccc;\n    border-top: 1px solid #ccc;\n    border-radius: 0 0 0 0;\n    width: 20px;\n    height: 100%;\n}\n\n#tx_tree .ui-fancytree li:last-child::after {\n    display: none;\n}\n\n#tx_tree .ui-fancytree li:last-child:before {\n    border-radius: 0 0 0 5px;\n}\n\n#tx_tree ul.ui-fancytree > li:first-child::before {\n    display: none;\n}\n\n#tx_tree ul.ui-fancytree > li:first-child::after {\n    border-radius: 5px 0 0 0;\n}\n\n#tx_tree .ui-fancytree li p {\n    border: 1px #ccc solid;\n    border-radius: 5px;\n    padding: 2px 5px;\n    white-space: nowrap;\n    display: inline-block;\n}\n\n#tx_tree .ui-fancytree li p:hover,\n#tx_tree .ui-fancytree li p:hover + ul li p,\n#tx_tree .ui-fancytree li p:focus,\n#tx_tree .ui-fancytree li p:focus + ul li p {\n    background: #eee;\n    color: #000;\n    border: 1px solid #aaa;\n}\n\n#tx_tree .ui-fancytree li p:hover + ul li::after,\n#tx_tree .ui-fancytree li p:focus + ul li::after,\n#tx_tree .ui-fancytree li p:hover + ul li::before,\n#tx_tree .ui-fancytree li p:focus + ul li::before,\n#tx_tree .ui-fancytree li p:hover + ul::before,\n#tx_tree .ui-fancytree li p:focus + ul::before,\n#tx_tree .ui-fancytree li p:hover + ul ul::before,\n#tx_tree .ui-fancytree li p:focus + ul ul::before {\n    border-color: #999;\n}\n\n#tx_tree .ui-fancytree .fancytree-icon {\n    display: none;\n}\n\n#tx_tree\n.fancytree-plain.fancytree-container.fancytree-treefocus\nspan.fancytree-focused\nspan.fancytree-title {\n    border-color: transparent;\n}\n\n#tx_tree .fancytree-plain span.fancytree-node span.fancytree-title {\n    background-color: transparent;\n    border-color: transparent;\n}\n\n#tx_tree ul.fancytree-container {\n    background-color: transparent;\n    border: none;\n    outline: none;\n}\n\n#tx_tree .fancytree-expander {\n    position: relative;\n    z-index: 2;\n    top: -2px;\n    left: -5px;\n}\n\n#tx_tree * {\n    font-family: monospace;\n}\n\n.back-button-container {\n    display: flex;\n    align-items: center;\n    font-size: 16px;\n}\n\n.back-button-container img {\n    margin-left: 20px;\n    margin-right: 20px;\n}\n\n.with-back-button {\n    display: flex;\n    align-items: center;\n    margin: 10px 11px 10px;\n}\n\n.with-back-button h3 {\n    padding-bottom: 0;\n    margin: 0;\n}\n\n.container-top {\n    display: inline-block;\n}\n\n.container-info-logo {\n    display: flex;\n    justify-content: space-between;\n}\n\n@media screen and (max-width: 1281px) {\n    .hosted-by {\n        order: 1;\n        margin: 0 0 0 13px;\n    }\n\n    .container-info-logo {\n        flex-flow: column;\n    }\n\n    .transaction-info {\n        order: 2;\n    }\n\n}\n\n@media screen and (max-width: 1280px) {\n    .with-back-button {\n        flex-direction: column;\n        align-items: flex-start;\n    }\n\n    .back-button-container {\n        order: -1;\n        margin-bottom: 20px;\n        width: 100%;\n        justify-content: space-between;\n    }\n}\n"
  },
  {
    "path": "ethtx_ce/app/frontend/static.py",
    "content": "# Copyright 2021 DAI FOUNDATION (the original version https://github.com/daifoundation/ethtx_ce)\n# Copyright 2021-2022 Token Flow Insights SA (modifications to the original software as recorded\n# in the changelog https://github.com/EthTx/ethtx/blob/master/CHANGELOG.md)\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at: http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed\n# on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and limitations under the License.\n#\n# The product contains trademarks and other branding elements of Token Flow Insights SA which are\n# not licensed under the Apache 2.0 license. When using or reproducing the code, please remove\n# the trademark and/or other branding elements.\n\nfrom flask import render_template, Blueprint, current_app\n\nfrom . import frontend_route\n\nbp = Blueprint(\"static\", __name__)\n\n\n@frontend_route(bp, \"/\")\ndef search_page() -> render_template:\n    \"\"\"Render search page - index.\"\"\"\n    return (\n        render_template(\n            \"index.html\",\n            chains=current_app.ethtx.providers.web3provider.nodes.keys()\n            if current_app.ethtx\n            else [],\n            ethtx_version=current_app.config[\"ethtx_version\"],\n            ethtx_ce_version=current_app.config[\"ethtx_ce_version\"],\n        ),\n        200,\n    )\n"
  },
  {
    "path": "ethtx_ce/app/frontend/templates/exception.html",
    "content": "<!--\n# Copyright 2021 DAI FOUNDATION (the original version https://github.com/daifoundation/ethtx_ce)\n# Copyright 2021-2022 Token Flow Insights SA (modifications to the original software as recorded\n# in the changelog https://github.com/EthTx/ethtx/blob/master/CHANGELOG.md)\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at: http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed\n# on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and limitations under the License.\n#\n# The product contains trademarks and other branding elements of Token Flow Insights SA which are\n# not licensed under the Apache 2.0 license. When using or reproducing the code, please remove\n# the trademark and/or other branding elements.\n  -->\n\n<html lang=\"en\">\n<head>\n    {% include './partials/headtags.html' %}\n    <title>Ethtx error</title>\n\n    <style>\n        html {\n            height: 100%;\n        }\n\n        body {\n            text-align: center;\n            display: flex;\n            flex-direction: column;\n            justify-content: center;\n            min-height: 100vh;\n        }\n\n        h1 {\n            padding-right: 1rem;\n            margin-right: 1rem;\n            border-right: 1px solid var(--border);\n            font-size: 3rem;\n            font-weight: 500;\n            line-height: 1.2;\n            margin-top: 0;\n        }\n\n        h2 {\n            font-weight: 400;\n            font-size: 1rem;\n            margin-top: 0;\n        }\n\n        h3 {\n            font-weight: 300;\n            font-size: 0.75rem;\n            margin-top: 0;\n        }\n\n        .container {\n            margin: 0 auto;\n            position: relative;\n            box-sizing: border-box;\n        }\n\n        .container-sub {\n            max-width: 600px;\n            word-wrap: break-word;\n            display: flex;\n            align-items: center;\n            justify-content: center;\n        }\n\n        .container-one-line {\n            word-wrap: anywhere;\n            text-align: left;\n        }\n\n        .pulsate {\n            -webkit-animation: pulsate 3s ease-out;\n            -webkit-animation-iteration-count: infinite;\n            opacity: 0.5;\n        }\n\n        @-webkit-keyframes pulsate {\n            0% {\n                opacity: 0.5;\n            }\n            50% {\n                opacity: 1.0;\n            }\n            100% {\n                opacity: 0.5;\n            }\n        }\n\n        .container-hash {\n            text-align: center;\n            display: inline-block;\n        }\n\n        .tx_hash {\n            color: #c00;\n            word-wrap: anywhere;\n            filter: drop-shadow(0px 0.2px 0.2px #c00);\n            -webkit-filter: drop-shadow(0px 0.2px 0.2px #c00);\n        }\n\n    </style>\n</head>\n<body>\n<div class=\"container\">\n    <div class=\"container-sub\">\n        <h1 class=\"pulsate\">{{ status_code }}</h1>\n        <div class=\"container-one-line\">\n            <h2> {{ error }}</h2>\n        </div>\n    </div>\n    {% if tx_hash %}\n        <div class=\"container-hash\">\n            <h3>Tx hash: <span class=\"tx_hash\">{{ tx_hash }}</span></h3>\n        </div>\n    {%- endif -%}\n</div>\n\n</body>\n</html>"
  },
  {
    "path": "ethtx_ce/app/frontend/templates/index.html",
    "content": "<!--\n# Copyright 2021 DAI FOUNDATION (the original version https://github.com/daifoundation/ethtx_ce)\n# Copyright 2021-2022 Token Flow Insights SA (modifications to the original software as recorded\n# in the changelog https://github.com/EthTx/ethtx/blob/master/CHANGELOG.md)\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at: http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed\n# on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and limitations under the License.\n#\n# The product contains trademarks and other branding elements of Token Flow Insights SA which are\n# not licensed under the Apache 2.0 license. When using or reproducing the code, please remove\n# the trademark and/or other branding elements.\n  -->\n\n<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n    {% include './partials/headtags.html' %}\n\n    <title>EthDecoder</title>\n    <style>\n        html {\n            height: 100vh;\n        }\n\n        body {\n            font-family: Hind, sans-serif;\n            font-size: 16px;\n            background-color: var(--background);\n            margin: 0;\n            padding: 0;\n            min-height: 100vh;\n            display: flex;\n            flex-direction: column;\n        }\n\n        form {\n            display: grid;\n            grid-gap: 25px;\n            margin-bottom: 16px;\n        }\n\n        label {\n            font-size: 22px;\n            font-weight: 500;\n            display: block;\n            margin-bottom: 8px;\n        }\n\n        input[type=submit] {\n            background-color: var(--background-alt);\n            color: #fff;\n            border-radius: 3px;\n            font-size: 18px;\n            padding: 8px 64px;\n            cursor: pointer;\n            border: none;\n            line-height: 1.65;\n            margin: 0;\n            word-wrap: break-word;\n        }\n\n        .container {\n            max-width: 840px;\n            margin: 0 auto;\n            display: flex;\n            flex-direction: column;\n            flex-grow: 1;\n        }\n\n        .main-title {\n            font-size: 2.375rem;\n            margin-top: 30px;\n            margin-bottom: 30px;\n            font-weight: 700;\n            line-height: 1;\n        }\n\n        .panel-container h2 {\n            font-size: 22px;\n            font-weight: 500;\n            margin: 0;\n            color: var(--text);\n        }\n\n        .input {\n            font-size: 18px;\n            width: 100%;\n            padding: 13px 16px;\n            box-sizing: border-box;\n            border-radius: 6px;\n            border: 1px solid var(--border);\n            background-color: transparent;\n        }\n\n        .select {\n            cursor: pointer;\n            -moz-appearance: none;\n            -webkit-appearance: none;\n            appearance: none;\n            background: url(/static/images/chevron_down.png) no-repeat right 13px center;\n        }\n\n        .tx-submit {\n            margin-top: .625rem;\n        }\n\n        .tx-submit input {\n            width: 14.375rem;\n        }\n\n        @media screen and (max-width: 568px) {\n            .container-title {\n                flex-direction: column;\n                align-items: flex-start !important;\n            }\n\n        }\n\n        .error-msg {\n            color: #c00;\n            font-size: 12px;\n            margin-top: 5px;\n            position: absolute;\n        }\n\n        .container-title {\n            display: flex;\n            align-items: center;\n            justify-content: space-between;\n            margin-bottom: 1rem;\n        }\n    </style>\n</head>\n\n<body>\n<div class=\"container\">\n    <div class=\"container-title\">\n        <span class=\"main-title\">EthTx Transaction Decoder</span>\n    </div>\n    <form action=\"/\" method=\"GET\" onsubmit=\"return onSubmit(this)\">\n        <div>\n            <label for=\"net\">Network</label>\n            <select class=\"input select\" id=\"net\">\n                {% for chain in chains %}\n                    {% if chain == 'mainnet' %}\n                    <option value=\"mainnet\">ETH mainnet</option>\n                    {% else %}\n                    <option value=\"{{ chain }}\">{{ chain|title + ' testnet' }}</option>\n                    {% endif %}\n                {% endfor %}\n            </select>\n        </div>\n        <div>\n            <label for=\"tx\">Tx Hash</label>\n            <input class=\"input\" size=\"66\" type=\"text\" id=\"tx\"\n                   oninput=\"handleValueChange()\"\n                   onchange=\"handleValueChange()\">\n            <div class=\"error-msg\" id=\"error_hash\"></div>\n        </div>\n        <div class=\"tx-submit\">\n            <input id=\"tx_submit\" type=\"submit\" value=\"Decode now\">\n        </div>\n    </form>\n</div>\n\n<script type=\"text/javascript\">\n    function onSubmit(e) {\n        const tx = document.getElementById(`tx`).value;\n        if (!tx || checkTxHash(tx) === false) {\n            printError(\"error_hash\", \"Please, enter a valid transaction hash.\")\n            return false;\n        } else {\n            window.onbeforeunload = function () {\n                document.getElementById(\"tx_submit\").value = \"Decoding...\";\n            };\n            e.action = '/' + e.net.options[e.net.selectedIndex].value + '/' + e.tx.value;\n        }\n    }\n\n    function handleValueChange() {\n        hideError(\"error_hash\")\n    }\n\n    function checkTxHash(tx) {\n        const pattern = new RegExp(/^(0x)?([A-Fa-f0-9]{64})$/)\n        return pattern.test(tx);\n    }\n\n    function printError(elemId, hintMsg) {\n        document.getElementById(elemId).innerHTML = hintMsg;\n    }\n\n    function hideError(elemId) {\n        document.getElementById(elemId).innerHTML = \"\";\n    }\n</script>\n</body>\n</html>"
  },
  {
    "path": "ethtx_ce/app/frontend/templates/partials/headtags.html",
    "content": "<!--\n# Copyright 2021 DAI FOUNDATION (the original version https://github.com/daifoundation/ethtx_ce)\n# Copyright 2021-2022 Token Flow Insights SA (modifications to the original software as recorded\n# in the changelog https://github.com/EthTx/ethtx/blob/master/CHANGELOG.md)\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at: http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed\n# on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and limitations under the License.\n#\n# The product contains trademarks and other branding elements of Token Flow Insights SA which are\n# not licensed under the Apache 2.0 license. When using or reproducing the code, please remove\n# the trademark and/or other branding elements.\n  -->\n\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\">\n<link href=\"https://fonts.googleapis.com/css2?family=Hind:wght@400;500;700&amp;display=swap\" rel=\"preload stylesheet\"\n      as=\"style\" crossorigin=\"anonymous\">\n<link rel=\"preload stylesheet\" type=\"text/css\" as=\"style\" crossorigin=\"anonymous\"\n      href=\"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css\">\n<link rel=\"shortcut icon\" type=\"image/x-icon\" href=\"{{ url_for('static', filename='favicon.ico') }}\">\n<style>\n    :root {\n        --text: #000;\n        --text-alt: #717171;\n        --background: #FFF;\n        --background-alt: #717171;\n        --background-muted: #EFEFEF;\n        --border: #C4C4C4;\n        --link: #337AB8;\n    }\n</style>"
  },
  {
    "path": "ethtx_ce/app/frontend/templates/semantics.html",
    "content": "<!--\n# Copyright 2021 DAI FOUNDATION (the original version https://github.com/daifoundation/ethtx_ce)\n# Copyright 2021-2022 Token Flow Insights SA (modifications to the original software as recorded\n# in the changelog https://github.com/EthTx/ethtx/blob/master/CHANGELOG.md)\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at: http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed\n# on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and limitations under the License.\n#\n# The product contains trademarks and other branding elements of Token Flow Insights SA which are\n# not licensed under the Apache 2.0 license. When using or reproducing the code, please remove\n# the trademark and/or other branding elements.\n  -->\n\n<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n    {% include './partials/headtags.html' %}\n    <title>Semantics editor</title>\n    <link href=\"/static/ethtx.new.css\" rel=\"stylesheet\">\n    <link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/jsoneditor/6.3.0/jsoneditor.min.css\"\n          integrity=\"sha512-d1YLCsPQJUoFkEXaRSGrD2uoWGXbcK9EFDWILONSkZbuIUCeH0xahQV8W6PZ4bA9UTZ9hJLQ/5Zuu2K35NBBMQ==\"\n          crossorigin=\"anonymous\" referrerpolicy=\"no-referrer\"/>\n    <link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.css\"\n          integrity=\"sha512-urpIFwfLI9ZDL81s6eJjgBF7LpG+ROXjp1oNwTj4gSlCw00KiV1rWBrfszV3uf5r+v621fsAwqvy1wRJeeWT/A==\"\n          crossorigin=\"anonymous\" referrerpolicy=\"no-referrer\"/>\n    <script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js\"\n            integrity=\"sha512-894YE6QWD5I59HgZOGReFYm4dnWc1Qt5NtvYSaNcOP+u1T9qYdvdihz0PPSiiqn/+/3e7Jo4EaG7TubfWGUrMQ==\"\n            crossorigin=\"anonymous\" referrerpolicy=\"no-referrer\"></script>\n    <script src=\"https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.js\"\n            integrity=\"sha512-JGKswjfABjJjtSUnz+y8XUBjBlwM1UHNlm2ZJN7A2a9HUYT3Mskq+SacsI35k4lok+/zetSxhZjKS3r3tfAnQg==\"\n            crossorigin=\"anonymous\" referrerpolicy=\"no-referrer\"></script>\n    <script src=\"https://cdnjs.cloudflare.com/ajax/libs/jsoneditor/6.3.0/jsoneditor.min.js\"\n            integrity=\"sha512-ASZnbuJkW4TR1WAXYIHls9Tavd/aZlxfNIvbO1PUIClxx/dAZYZqzuD/QvN99lREXK65AYXsJWwegJaxFVvc5g==\"\n            crossorigin=\"anonymous\" referrerpolicy=\"no-referrer\"></script>\n    <style>\n        button {\n            background-color: #AAA;\n            color: #fff;\n            border-radius: 5px;\n            font-size: 16px;\n            margin-top: 10px;\n            margin-left: 5px;\n            padding: 5px 10px 5px 10px;\n        }\n\n\n        label,\n        input,\n        select {\n            display: block;\n        }\n\n        input.text {\n            margin-bottom: 12px;\n            width: 790px;\n            padding: .4em;\n        }\n\n        select {\n            margin-bottom: 12px;\n            width: 805px;\n            padding: .4em;\n        }\n\n        textarea {\n            width: 805px;\n            height: 360px;\n        }\n\n        fieldset {\n            padding: 0;\n            border: 0;\n            margin-top: 25px;\n        }\n\n    </style>\n\n    <script>\n        $(function () {\n            var\n                dialog, form,\n                address = $(\"#address\"),\n                network = $(\"#network\"),\n                name = $(\"#name\"),\n                abi = $(\"#abi\"),\n                chash = '{{ metadata.contract.code_hash }}',\n                standard = '{{ metadata.contract.standard | tojson }}';\n\n            function checkLength(o, min, max) {\n                return !(o.val().length > max || o.val().length < min);\n            }\n\n            function isJsonString(str) {\n                try {\n                    var json = JSON.parse(str);\n                    return (typeof json === 'object');\n                } catch (e) {\n                    return false;\n                }\n            }\n\n            function addABI() {\n                var valid = true;\n\n                valid = valid && checkLength(address, 42, 42);\n                valid = valid && checkLength(name, 2, 80);\n                valid = valid && abi.val().length > 0 && isJsonString(abi.val());\n\n                if (valid) {\n                    const poke = {\n                        address: address.val(),\n                        network: network.val(),\n                        name: name.val(),\n                        abi: abi.val(),\n                        chash: chash,\n                        standard: standard\n                    };\n                    $.ajax({\n                        url: \"/poke\",\n                        dataType: 'json',\n                        contentType: \"application/json; charset=utf-8\",\n                        data: JSON.stringify(poke),\n                        type: 'POST',\n                        success: function (response) {\n                            if (response[\"result\"] === 'ok') {\n                                alert(\"SUCCESS! ABI processed...\");\n                            } else {\n                                alert(\"ERROR! ABI processing error\");\n                            }\n                            location.reload();\n                        },\n                        error: function (error) {\n                            alert(\"ERROR! Processing error\");\n                            location.reload();\n                        }\n                    });\n                    dialog.dialog(\"close\");\n                } else {\n                    alert(\"Data is not correct!\")\n                }\n                return valid;\n            }\n\n            dialog = $(\"#dialog-form\").dialog({\n                autoOpen: false,\n                height: 720,\n                width: 850,\n                modal: true,\n                buttons: {\n                    \"Decode ABI\": addABI,\n                    Cancel: function () {\n                        dialog.dialog(\"close\");\n                    }\n                },\n                close: function () {\n                    form[0].reset();\n                }\n            });\n\n            form = dialog.find(\"form\").on(\"submit\", function (event) {\n                event.preventDefault();\n                addABI();\n            });\n\n            $(\"#poke-abi\").button().on(\"click\", function () {\n                dialog.dialog(\"open\");\n                return false;\n            });\n\n        });\n    </script>\n</head>\n\n<body>\n\n<h3>Semantics for: {{ address }} / {{ metadata.chain }}</h3>\n<div id=\"metadataJSONeditor\" style=\"float: left; width: 55%; height: 320px; margin: 5px;\"></div>\n<div id=\"transformationsJSONeditor\" style=\"float: left; width: 43%; height: 320px; margin: 5px;\"></div>\n<div id=\"eventsJSONeditor\" style=\"float: left; width: 55%; height: 320px; margin: 5px;\"></div>\n<div id=\"functionsJSONeditor\" style=\"float: left; width: 43%; height: 320px; margin: 5px;\"></div>\n\n<form>\n    <button id=\"save-semantics\">Save semantics</button>\n    <button id=\"poke-abi\">Poke ABI</button>\n    <button id=\"reload-semantics\" style=\"background-color: #c0392b; color: #fff;\">Reload semantics</button>\n</form>\n\n<div id=\"dialog-form\" title=\"Poke ABI\">\n    <form>\n        <fieldset>\n            <label for=\"address\">Contract address</label>\n            <input type=\"text\" name=\"address\" id=\"address\" class=\"text ui-widget-content ui-corner-all\"\n                   value=\"{{ address }}\">\n            <label for=\"network\">Network</label>\n            <select name=\"network\" id=\"network\">\n                <option {% if metadata.chain == 'mainnet' %} selected {% endif %} value=\"mainnet\">ETH mainnet</option>\n                <option {% if metadata.chain == 'goerli' %} selected {% endif %} value=\"goerli\">Goerli testnet</option>\n                <option {% if metadata.chain == 'rinkeby' %} selected {% endif %} value=\"rinkeby\">Rinkeby testnet\n                </option>\n            </select>\n            <label for=\"name\">Contract name</label>\n            <input type=\"text\" name=\"name\" id=\"name\" class=\"text ui-widget-content ui-corner-all\">\n            <label for=\"abi\">Contract ABI</label>\n            <textarea name=\"abi\" id=\"abi\" class=\"textarea ui-widget-content ui-corner-all\"></textarea>\n            <input type=\"submit\" tabindex=\"-1\" style=\"position:absolute; top:-1000px\">\n        </fieldset>\n    </form>\n</div>\n\n<script>\n    var metadataContainer = document.getElementById(\"metadataJSONeditor\");\n    var transformationsContainer = document.getElementById(\"transformationsJSONeditor\");\n    var eventsContainer = document.getElementById(\"eventsJSONeditor\");\n    var functionsContainer = document.getElementById(\"functionsJSONeditor\");\n\n    var options = {\n        modes: ['tree', 'code'],\n        enableSort: false,\n        enableTransform: false,\n        search: true,\n        indentation: 3,\n        statusBar: false\n    };\n\n    const metadata_editor = new JSONEditor(metadataContainer, {...options, name: 'Metadata'}, {{ metadata | tojson }});\n    const transformations_editor = new JSONEditor(transformationsContainer, {\n        ...options,\n        name: 'Transformations'\n    }, {{ transformations | tojson }});\n    const events_editor = new JSONEditor(eventsContainer, {...options, name: 'Events'}, {{ events | tojson }});\n    const functions_editor = new JSONEditor(functionsContainer, {\n        ...options,\n        name: 'Functions'\n    }, {{ functions | tojson }})\n\n    $(\"#save-semantics\").button().on(\"click\", function () {\n        saveSemantics();\n        {{ name }};\n        return false;\n    });\n\n    $(\"#reload-semantics\").button().on(\"click\", function () {\n        reloadSemantics();\n        return false;\n    });\n\n    // save semantics\n    function saveSemantics() {\n        const semantics = {\n            address: '{{ address }}',\n            metadata: metadata_editor.get(),\n            transformations: transformations_editor.get(),\n            events: events_editor.get(),\n            functions: functions_editor.get()\n        };\n        $.ajax({\n            url: \"/save\",\n            dataType: 'json',\n            contentType: \"application/json; charset=utf-8\",\n            data: JSON.stringify(semantics),\n            type: 'POST',\n            success: function (response) {\n                console.log(response);\n                location.reload();\n            },\n            error: function (error) {\n                console.log(error);\n                location.reload();\n            }\n        })\n    }\n\n    //reload semantics\n    function reloadSemantics() {\n        const data = {\n            address: '{{ address }}',\n            chain: '{{ metadata.chain }}'\n        };\n\n        $.ajax({\n            url: \"/reload\",\n            dataType: 'json',\n            contentType: \"application/json; charset=utf-8\",\n            data: JSON.stringify(data),\n            type: 'POST',\n            success: function (response) {\n                console.log(response);\n                location.reload();\n            },\n            error: function (error) {\n                console.log(error);\n                location.reload();\n            }\n        })\n    }\n</script>\n\n</body>\n\n</html>\n"
  },
  {
    "path": "ethtx_ce/app/frontend/templates/transaction.html",
    "content": "<!--\n# Copyright 2021 DAI FOUNDATION (the original version https://github.com/daifoundation/ethtx_ce)\n# Copyright 2021-2022 Token Flow Insights SA (modifications to the original software as recorded\n# in the changelog https://github.com/EthTx/ethtx/blob/master/CHANGELOG.md)\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at: http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed\n# on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and limitations under the License.\n#\n# The product contains trademarks and other branding elements of Token Flow Insights SA which are\n# not licensed under the Apache 2.0 license. When using or reproducing the code, please remove\n# the trademark and/or other branding elements.\n  -->\n\n{% macro address_link(address, label, badge=\"\") %}\n    {%- if address and address != '0x0000000000000000000000000000000000000000' -%}\n        <a href=\"https://{%- if transaction.chain_id != 'mainnet' -%}{{ transaction.chain_id }}.{% endif %}etherscan.io/address/{{ address }}\" target=\"_blank\">\n           {%- if badge -%}\n              <span class=\"badge badge-info\">[{{ badge }}] </span>\n           {%- endif -%}\n              {{- label -}}\n        </a>\n    {%- else -%}\n        {{- label -}}\n    {%- endif -%}\n{% endmacro %}\n\n{% macro nft_link(address, label) %}\n    {%- if address and address != '0x0000000000000000000000000000000000000000' -%}\n        <a href=\"https://{%- if transaction.chain_id != 'mainnet' -%}{{ transaction.chain_id }}.{% endif %}etherscan.io/token/{{ address }}\" target=\"_blank\">\n            {{- label -}}\n        </a>\n    {%- else -%}\n        {{- label -}}\n    {%- endif -%}\n{% endmacro %}\n\n{% macro print_event_arguments(arguments) %}\n    {% for argument in arguments %}\n        {% if argument.type != \"ignore\" %}\n            {% if loop.index > 1 %}, {% endif %}\n            {%- if argument.name == \"[no ABI]\" -%}\n                <span class=\"badge badge-danger\">no_ABI</span>\n            {%- else -%}\n                {%- if argument.name -%}\n                    <span style='color: darkred'>{{ argument.name }}=</span>\n                {%- endif -%}\n                {%- if argument.type == \"tuple\" -%}\n                    ({{- print_event_arguments(argument.value) -}})\n                {%- elif argument.type == \"tuple[]\" -%}\n                    [\n                    {%- for sub_arg in argument.value -%}\n                        {{- print_event_arguments(sub_arg) -}}\n                    {%- endfor -%}\n                    ]\n                {%- elif argument.type == \"address\" -%}\n                    {{- address_link(argument.value.address, argument.value.name, argument.value.badge) -}}\n                {%- elif argument.type == \"nft\" -%}\n                    {{- nft_link(argument.value.address, argument.value.name) -}}\n                {%- elif argument.type == \"call\" -%}\n                    {{- address_link(argument.value.address, argument.value.name, argument.value.badge) -}}.\n                    {{- argument.value.function_name -}}({{ print_event_arguments(argument.value.arguments) }})\n                {%- else -%}\n                    {{- argument.value -}}\n                {%- endif -%}\n            {% endif %}\n        {% endif %}\n    {% endfor %}\n{% endmacro %}\n\n{% macro print_call_arguments(arguments) %}\n    {% if arguments is not none %}\n        {%- for argument in arguments -%}\n            {% if argument.type != \"ignore\" %}\n                {%- if loop.index > 1 -%}, {% endif %}\n                {%- if argument.name == \"[no ABI]\" -%}\n                    <span class=\"badge badge-danger\">no_ABI</span>\n                {%- else -%}\n                    {%- if argument.name %}<span style='color: darkred'>{{- argument.name -}}=</span>{%- endif -%}\n                    {%- if argument.type == \"tuple\" -%}\n                        ({{- print_call_arguments(argument.value) -}})\n                    {%- elif argument.type == \"tuple[]\" -%}\n                        [\n                        {%- for sub_arg in argument.value -%}\n                            {%- if loop.index > 1 -%}, {% endif %}\n                            ({{- print_call_arguments(sub_arg) -}})\n                        {%- endfor -%}\n                        ]\n                    {%- elif argument.type == \"address\" -%}\n                        {{- address_link(argument.value.address, argument.value.name, argument.value.badge) -}}\n                    {%- elif argument.type == \"nft\" -%}\n                        {{- nft_link(argument.value.address, argument.value.name) -}}\n                    {% elif argument.type == \"call\" %}\n                        {{- address_link(argument.value.address, argument.value.name, argument.value.badge) -}}.\n                        <span style=\"color: darkgreen\">{{- argument.value.function_name -}}</span>(\n                        {{- print_event_arguments(argument.value.arguments) -}})\n                    {%- else -%}\n                        {{- argument.value -}}\n                    {%- endif -%}\n                {%- endif -%}\n            {%- endif -%}\n        {%- endfor -%}\n    {% endif %}\n{% endmacro %}\n\n{%- macro print_call_line(call) -%}\n    <li id=\"{{ call.id }}\"\n        class=\"indent-{{ call.indent }} {% if call.indent < 6 %}expanded{% endif %}\">\n        <p>\n            <span style=\"color: slategray\">[{{- call.gas_used if call.gas_used != None else \"N/A\" -}}]: </span>\n            {% if call.error %}\n                <span style='color: red'>({{ call.error }})</span>\n            {% endif %}\n            {% if call.call_type == \"delegatecall\" %}\n                <span style='color: darkorange'>(delegate)</span>\n            {% endif %}\n            {% if call.value and call.call_type != \"selfdestruct\" %}\n                <span style='color: blue'>ETH {{ call.value -}}</span>\n            {% endif %}\n            {%- if call.call_type == \"selfdestruct\" -%}\n                {{- address_link(call.from_address.address, call.from_address.name, call.from_address.badge) -}}\n                <span style='color: darkgreen'>.{{ call.call_type }}({% if call.value > 0 %}\n                    <span style='color: blue'>ETH {{ call.value }}</span> =>\n                    {{ address_link(call.to_address.address, call.to_address.name, call.to_address.badge) -}}{% endif %}\n                    )</span>\n            {%- elif call.call_type == \"create\" -%}\n                {{- address_link(call.to_address.address, call.to_address.name, call.to_address.badge) -}}.<span\n                    style='color: darkgreen'>New()</span>\n            {%- else -%}\n\n                {%- if call.call_type == \"delegatecall\" -%}\n                    {{- address_link(call.from_address.address, call.from_address.name, call.from_address.badge) -}}\n                {%- else -%}\n                    {{- address_link(call.to_address.address, call.to_address.name, call.to_address.badge) -}}\n                {%- endif -%}\n\n                {%- if call.call_type == \"delegatecall\" -%}\n                    [<span\n                        class='delegate'>{{- address_link(call.to_address.address, call.to_address.name, call.to_address.badge) -}}</span>\n                {%- endif -%}\n                {%- if call.function_guessed -%}\n                    <span style='color: dodgerblue'>.{{- call.function_name -}}</span>\n                {%- elif call.function_name != \"0x\" -%}\n                    <span style='color: darkgreen'>.{{- call.function_name -}}</span>\n                {%- else -%}\n                    <span style='color: darkgreen'>.fallback</span>\n                {%- endif -%}\n                {%- if call.call_type == \"delegatecall\" -%}\n                    ]\n                {%- endif -%}\n\n                <span>({{- print_call_arguments(call.arguments) -}}) => ({{- print_call_arguments(call.outputs) -}})</span>\n            {%- endif -%}\n        </p>\n        <ul>\n            {% for sub_call in call.subcalls %}\n                {{- print_call_line(sub_call) -}}\n            {% endfor %}\n        </ul>\n    </li>\n{%- endmacro -%}\n\n<!doctype html>\n<html lang=\"en\">\n\n<head>\n    {% include './partials/headtags.html' %}\n    <title>Ethtx.info Analysis {{ transaction.tx_hash }}</title>\n    <script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js\"\n            integrity=\"sha512-894YE6QWD5I59HgZOGReFYm4dnWc1Qt5NtvYSaNcOP+u1T9qYdvdihz0PPSiiqn/+/3e7Jo4EaG7TubfWGUrMQ==\"\n            crossorigin=\"anonymous\" referrerpolicy=\"no-referrer\"></script>\n    <link rel=\"stylesheet\"\n          href=\"https://cdnjs.cloudflare.com/ajax/libs/jquery.fancytree/2.38.2/skin-themeroller/ui.fancytree.min.css\"\n          integrity=\"sha512-LtulT9+xwtALkeFjtiojm4zOrWyDR+qivwmAKI8DSMdtJnJP/cXlV2TfwbiGe3m4nHoWy2Jbgg+I7BKHqqo2Jg==\"\n          crossorigin=\"anonymous\" referrerpolicy=\"no-referrer\"/>\n    <script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery.fancytree/2.38.2/jquery.fancytree-all-deps.min.js\"\n            integrity=\"sha512-8Fstaj+d8Fha0qzgW/bGQpyG4NcVSYcmflfYOzhV1z/4/SYwf96rqrANH+lUmO7ZSq9WgRDYgASFiiq20bgK7g==\"\n            crossorigin=\"anonymous\" referrerpolicy=\"no-referrer\"></script>\n    <link href=\"/static/ethtx.new.css\" rel=\"stylesheet\">\n</head>\n\n<body>\n\n<div>\n    <div class=\"container-top\">\n        <h3>\n            <a title=\"Home\" href=\"/\">\n                <i class=\"fas fa-home \"></i>\n            </a> Analysis for: <span class=\"transaction-hash\">\n            {{ transaction.tx_hash }} / {{ transaction.chain_id }}</span>\n        </h3>\n        <div class=\"container-info-logo\">\n            <div class=\"transaction-info\">\n                <div>\n                    Block number: <span style='color: darkred'>{{ transaction.block_number }}</span>\n                    {% if transaction.timestamp %}\n                        at <span style='color: darkred'>{{ transaction.timestamp }}</span> UTC\n                    {% endif %}\n                </div>\n                <div>\n                    Tx cost: <span\n                        style='color: darkred'>{{ (transaction.gas_used * transaction.gas_price / 10 ** 9) }}</span>\n                    ETH\n                    {% if transaction.chain_id == 'mainnet' and eth_price %}\n                        /\n                        <span style='color: darkred'>{{ \"{:,.2f}\".format(transaction.gas_used * transaction.gas_price * eth_price / 10 ** 9) }}</span>\n                        USD\n                    {% endif %}\n                </div>\n                <div>\n                    Gas used: <span style='color: darkred'>{{ \"{:,}\".format(transaction.gas_used) }}</span> / <span\n                        style='color: darkred'>{{ \"{:,.2f}\".format(transaction.gas_price ) }}</span> Gwei\n                </div>\n            </div>\n        </div>\n    </div>\n</div>\n\n{% if events %}\n    <div class=\"events\">\n        <h3>Emitted events:</h3>\n        <table class=\"table table-striped\">\n            {% for event in events %}\n                <tr>\n                    <td>\n                        <span style=\"color: slategray\">[{{- event.index -}}]</span>\n                        {{ address_link(event.contract.address, event.contract.name, event.contract.badge) }}\n                        {%- if event.event_guessed -%}\n                            <span style='color: dodgerblue'>.{{ event.event_name }}</span>\n                        {%- else -%}\n                            <span style='color: darkgreen'>.{{ event.event_name }}</span>\n                        {%- endif -%}\n                        ({{ print_event_arguments(event.parameters) }})\n                    </td>\n                </tr>\n            {% endfor %}\n        </table>\n    </div>\n{% endif %}\n\n{% if balances %}\n    <div class=\"account-balances\">\n        <h3>Account balances:</h3>\n        <table class=\"table table-sm\">\n            <thead>\n            <tr>\n                <th scope=\"col\">Address</th>\n                <th scope=\"col\">Token</th>\n                <th scope=\"col\">Balance</th>\n            </tr>\n            </thead>\n            <tbody>\n            {% for balance in balances %}\n                <tr>\n                    <td rowspan=\"{{- balance.tokens|length -}}\">\n                        {{- address_link(balance.holder.address, balance.holder.name, balance.holder.badge) -}}\n                    </td>\n                    {% for token in balance.tokens %}\n                        {% if loop.index > 1 %}\n                            <tr>{% endif %}\n                    {% if token.token_standard == 'ERC721' %}\n                        <td class=\"\">{{- nft_link(token.token_address, token.token_symbol) -}}</td>\n                    {% else %}\n                        <td class=\"\">{{- address_link(token.token_address, token.token_symbol) -}}</td>\n                    {% endif %}\n                    <td style=\"text-align: right\">\n                        {% if token.balance[0] == '-' %}\n                            <span style=\"color: darkred\">{{- token.balance -}}</span>\n                        {% else %}\n                            {{- token.balance -}}\n                        {% endif %}\n                    </td>\n                    {% if loop.index > 1 %}</tr>{% endif %}\n                    {% endfor %}\n            {% endfor %}\n            </tbody>\n        </table>\n    </div>\n{% endif %}\n\n{% if transfers %}\n    <div class=\"transfers\">\n        <h3>Token transfers: </h3>\n        <table class=\"table\">\n            <thead>\n            <tr>\n                <th scope=\"col\">Sender</th>\n                <th scope=\"col\">Token</th>\n                <th scope=\"col\">Amount</th>\n                <th scope=\"col\">Receiver</th>\n            </tr>\n            </thead>\n            <tbody>\n            {% for transfer in transfers %}\n                <tr>\n                    <td>{{- address_link(transfer.from_address.address, transfer.from_address.name, transfer.from_address.badge) -}}</td>\n                    {% if transfer.token_standard == 'ERC721' %}\n                        <td class=\"\">{{- nft_link(transfer.token_address, transfer.token_symbol) -}}</td>\n                    {% else %}\n                        <td>{{- address_link(transfer.token_address, transfer.token_symbol) -}}</td>\n                    {% endif %}\n                    <td style=\"text-align: right\">\n                        {{- \"{:,.4f}\".format(transfer.value) -}}\n                    </td>\n                    <td>{{- address_link(transfer.to_address.address, transfer.to_address.name, transfer.to_address.badge) -}}</td>\n                </tr>\n            {% endfor %}\n            </tbody>\n        </table>\n    </div>\n{% endif %}\n\n{% if call %}\n    <div class=\"calls\">\n        <h3>Execution trace:</h3>\n        <div id=\"tree\">\n            <ul class=\"tree\">\n                <li>\n                    <p>\n                        <span style=\"color: slategray\">[{{- transaction.gas_used -}}]: </span>\n                        {{- address_link(transaction.sender.address, transaction.sender.name, 'sender') -}}\n                    </p>\n                    <ul>\n                        {{- print_call_line(call) -}}\n                    </ul>\n                </li>\n            </ul>\n        </div>\n    </div>\n{% else %}\n    <h3>Trace decoding error...</h3>\n{% endif %}\n\n<script>\n    $(document).ready(() => {\n        $(\"#tree\").fancytree({\n            minExpandLevel: 2,\n            toggleEffect: false,\n        });\n        $(\"#tx_tree\").fancytree({\n            minExpandLevel: 2,\n            toggleEffect: false,\n        });\n    });\n</script>\n</body>\n</html>\n"
  },
  {
    "path": "ethtx_ce/app/frontend/transactions.py",
    "content": "# Copyright 2021 DAI FOUNDATION (the original version https://github.com/daifoundation/ethtx_ce)\n# Copyright 2021-2022 Token Flow Insights SA (modifications to the original software as recorded\n# in the changelog https://github.com/EthTx/ethtx/blob/master/CHANGELOG.md)\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at: http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed\n# on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and limitations under the License.\n#\n# The product contains trademarks and other branding elements of Token Flow Insights SA which are\n# not licensed under the Apache 2.0 license. When using or reproducing the code, please remove\n# the trademark and/or other branding elements.\n\nimport logging\nimport os\nfrom typing import Optional\n\nfrom ethtx.models.decoded_model import DecodedTransaction\nfrom flask import Blueprint, render_template, current_app, request\n\nfrom . import frontend_route, deps\n\nlog = logging.getLogger(__name__)\n\nbp = Blueprint(\"transactions\", __name__)\n\n\n@frontend_route(bp, \"/<string:tx_hash>/\")\n@frontend_route(bp, \"/<string:chain_id>/<string:tx_hash>/\")\ndef read_decoded_transaction(\n    tx_hash: str, chain_id: Optional[str] = None\n) -> \"show_transaction_page\":\n    tx_hash = tx_hash if tx_hash.startswith(\"0x\") else \"0x\" + tx_hash\n\n    refresh_semantics = \"refresh\" in request.args\n\n    if refresh_semantics:\n        refresh_secure_key = request.args[\"refresh\"]\n        if refresh_secure_key != os.environ[\"SEMANTIC_REFRESH_KEY\"]:\n            return \"Invalid semantics refresh key\"\n\n        log.info(f\"Decoding tx {tx_hash} with semantics refresh\")\n\n    chain_id = chain_id or current_app.ethtx.default_chain\n    decoded_transaction = current_app.ethtx.decoders.decode_transaction(\n        chain_id=chain_id, tx_hash=tx_hash, recreate_semantics=refresh_semantics\n    )\n    decoded_transaction.metadata.timestamp = (\n        decoded_transaction.metadata.timestamp.strftime(\"%Y-%m-%d %H:%M:%S\")\n    )\n\n    return show_transaction_page(decoded_transaction)\n\n\ndef show_transaction_page(data: DecodedTransaction) -> render_template:\n    \"\"\"Render transaction/exception page.\"\"\"\n    return (\n        render_template(\n            \"transaction.html\",\n            eth_price=deps.get_eth_price(),\n            transaction=data.metadata,\n            events=data.events,\n            call=data.calls,\n            transfers=data.transfers,\n            balances=data.balances,\n        ),\n        200,\n    )\n"
  },
  {
    "path": "ethtx_ce/app/helpers.py",
    "content": "# Copyright 2021 DAI FOUNDATION (the original version https://github.com/daifoundation/ethtx_ce)\n# Copyright 2021-2022 Token Flow Insights SA (modifications to the original software as recorded\n# in the changelog https://github.com/EthTx/ethtx/blob/master/CHANGELOG.md)\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at: http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed\n# on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and limitations under the License.\n#\n# The product contains trademarks and other branding elements of Token Flow Insights SA which are\n# not licensed under the Apache 2.0 license. When using or reproducing the code, please remove\n# the trademark and/or other branding elements.\n\nimport importlib\nimport logging\nimport os\nimport pkgutil\nfrom typing import Any, List, Tuple, Optional\n\nimport pkg_resources\nimport requests\nfrom flask import Blueprint, Flask\nfrom git import Repo\n\nlog = logging.getLogger(__name__)\n\n\ndef register_blueprints(\n    app: Flask, package_name: str, package_path: str\n) -> List[Blueprint]:\n    \"\"\"\n    Register all Blueprint instances on the specified Flask application found\n    in all modules for the specified package.\n    :param app: the Flask application\n    :param package_name: the package name\n    :param package_path: the package path\n    \"\"\"\n    rv = []\n\n    for _, name, _ in pkgutil.iter_modules(package_path):\n        m = importlib.import_module(\"%s.%s\" % (package_name, name))\n        for item in dir(m):\n            item = getattr(m, item)\n            if isinstance(item, Blueprint):\n                app.register_blueprint(item)\n            rv.append(item)\n\n    return rv\n\n\ndef class_import(name: str) -> Any:\n    \"\"\"Import class from string.\"\"\"\n    d = name.rfind(\".\")\n    classname = name[d + 1 : len(name)]\n    m = __import__(name[0:d], globals(), locals(), [classname])\n\n    return getattr(m, classname)\n\n\nclass Singleton(type):\n    _instances = {}\n\n    def __call__(cls, *args, **kwargs):\n        if cls not in cls._instances:\n            cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)\n        return cls._instances[cls]\n\n\ndef read_ethtx_versions(app: Flask) -> None:\n    \"\"\"Read ethtx and ethtx_ce versions.\"\"\"\n    ethtx_version = pkg_resources.get_distribution(\"ethtx\").version\n\n    try:\n        remote_url, sha = _get_version_from_git()\n    except Exception:\n        remote_url, sha = _get_version_from_docker()\n\n    ethtx_ce_version = f\"{_clean_up_git_link(remote_url)}/tree/{sha}\"\n\n    log.info(\n        \"%s: EthTx version: %s. EthTx CE version: %s\",\n        app.name,\n        ethtx_version,\n        ethtx_ce_version,\n    )\n\n    app.config[\"ethtx_version\"] = ethtx_version\n    app.config[\"ethtx_ce_version\"] = ethtx_ce_version\n\n\ndef get_latest_ethtx_version() -> str:\n    \"\"\"Get latest EthTx version.\"\"\"\n    package = \"EthTx\"\n    response = requests.get(f\"https://pypi.org/pypi/{package}/json\")\n\n    if response.status_code != 200:\n        log.warning(\"Failed to get latest EthTx version from PyPI\")\n        return \"\"\n\n    log.info(\"Latest EthTx version: %s\", response.json()[\"info\"][\"version\"])\n    return response.json()[\"info\"][\"version\"]\n\n\ndef _get_version_from_git() -> Tuple[str, str]:\n    \"\"\"Get EthTx CE version from .git\"\"\"\n    repo = Repo(__file__, search_parent_directories=True)\n\n    remote_url = repo.remote(\"origin\").url\n    sha = repo.head.commit.hexsha\n    short_sha = repo.git.rev_parse(sha, short=True)\n\n    return remote_url, short_sha\n\n\ndef _get_version_from_docker() -> Tuple[str, str]:\n    \"\"\"Get EthTx CE version from env.\"\"\"\n    return os.getenv(\"GIT_URL\", \"\"), os.getenv(\"GIT_SHA\", \"\")\n\n\ndef _clean_up_git_link(git_link: str) -> str:\n    \"\"\"Clean up git link, delete .git extension, make https url.\"\"\"\n    if \"@\" in git_link:\n        git_link.replace(\":\", \"/\").replace(\"git@\", \"https://\")\n\n    if git_link.endswith(\".git\"):\n        git_link = git_link[:-4]\n\n    return git_link\n"
  },
  {
    "path": "ethtx_ce/app/logger.py",
    "content": "# Copyright 2021 DAI FOUNDATION (the original version https://github.com/daifoundation/ethtx_ce)\n# Copyright 2021-2022 Token Flow Insights SA (modifications to the original software as recorded\n# in the changelog https://github.com/EthTx/ethtx/blob/master/CHANGELOG.md)\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at: http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed\n# on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and limitations under the License.\n#\n# The product contains trademarks and other branding elements of Token Flow Insights SA which are\n# not licensed under the Apache 2.0 license. When using or reproducing the code, please remove\n# the trademark and/or other branding elements.\n\nimport json\nimport logging\nimport logging.config\nimport os\n\nfrom flask import Flask\n\n\ndef setup_logging(app: Flask):\n    \"\"\"Setup logging\"\"\"\n    with open(app.config[\"LOGGING_CONFIG\"], \"r\") as f:\n        config = json.load(f)\n\n    config[\"root\"][\"level\"] = \"DEBUG\" if app.config[\"DEBUG\"] else \"INFO\"\n    filename = config[\"handlers\"][\"file_handler\"][\"filename\"]\n    if \"/\" not in filename:\n        log_file_path = os.path.join(app.config[\"LOGGING_LOG_PATH\"], filename)\n        os.makedirs(os.path.dirname(log_file_path), exist_ok=True)\n        config[\"handlers\"][\"file_handler\"][\"filename\"] = log_file_path\n\n    logging.config.dictConfig(config)\n\n    setup_external_logging()\n\n\ndef setup_external_logging() -> None:\n    \"\"\"Setup and override external libs loggers.\"\"\"\n    logging.getLogger(\"web3\").setLevel(logging.INFO)  # web3 logger\n"
  },
  {
    "path": "ethtx_ce/app/wsgi.py",
    "content": "# Copyright 2021 DAI FOUNDATION (the original version https://github.com/daifoundation/ethtx_ce)\n# Copyright 2021-2022 Token Flow Insights SA (modifications to the original software as recorded\n# in the changelog https://github.com/EthTx/ethtx/blob/master/CHANGELOG.md)\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at: http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed\n# on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and limitations under the License.\n#\n# The product contains trademarks and other branding elements of Token Flow Insights SA which are\n# not licensed under the Apache 2.0 license. When using or reproducing the code, please remove\n# the trademark and/or other branding elements.\n\nimport os\n\nfrom ethtx import EthTx, EthTxConfig\nfrom flask import Flask\nfrom werkzeug.middleware.dispatcher import DispatcherMiddleware\n\nfrom . import frontend, api\n\napp = Flask(__name__)\n\nethtx_config = EthTxConfig(\n    mongo_connection_string=os.getenv(\"MONGO_CONNECTION_STRING\"),\n    etherscan_api_key=os.getenv(\"ETHERSCAN_KEY\"),\n    web3nodes={\n        \"mainnet\": dict(hook=os.getenv(\"MAINNET_NODE_URL\", \"\"), poa=False),\n        \"goerli\": dict(hook=os.getenv(\"GOERLI_NODE_URL\", \"\"), poa=True),\n    },\n    default_chain=\"mainnet\",\n    etherscan_urls={\n        \"mainnet\": \"https://api.etherscan.io/api\",\n        \"goerli\": \"https://api-goerli.etherscan.io/api\",\n    },\n)\n\nethtx = EthTx.initialize(ethtx_config)\n\napp.wsgi_app = DispatcherMiddleware(\n    frontend.create_app(engine=ethtx, settings_override=EthTxConfig),\n    {\"/api\": api.create_app(engine=ethtx, settings_override=EthTxConfig)},\n)\n\n# ethtx_ce/ as Source Root\nif __name__ == \"__main__\":\n    app.run()\n"
  },
  {
    "path": "ethtx_ce/entrypoint.sh",
    "content": "#!/usr/bin/env sh\nset -e\n\necho \"ethtx_ce entrypoint.sh\"\n\nif [ -f /app/app/wsgi.py ]; then\n    DEFAULT_MODULE_NAME=app.wsgi\nelif [ -f /app/wsgi.py ]; then\n    DEFAULT_MODULE_NAME=wsgi\nfi\n\nMODULE_NAME=${MODULE_NAME:-$DEFAULT_MODULE_NAME}\nVARIABLE_NAME=${VARIABLE_NAME:-app}\n\nexport APP_MODULE=${APP_MODULE:-\"$MODULE_NAME:$VARIABLE_NAME\"}\n\nif [ -f /app/gunicorn_conf.py ]; then\n    DEFAULT_GUNICORN_CONF=/app/gunicorn_conf.py\nelif [ -f /app/app/gunicorn_conf.py ]; then\n    DEFAULT_GUNICORN_CONF=/app/app/gunicorn_conf.py\nelse\n    DEFAULT_GUNICORN_CONF=/gunicorn_conf.py\nfi\nexport GUNICORN_CONF=${GUNICORN_CONF:-$DEFAULT_GUNICORN_CONF}\n\nexec \"$@\""
  },
  {
    "path": "ethtx_ce/gunicorn_conf.py",
    "content": "import json\nimport multiprocessing\nimport os\n\nworkers_per_core_str = os.getenv(\"WORKERS_PER_CORE\", \"1\")\nmax_workers_str = os.getenv(\"MAX_WORKERS\")\nuse_max_workers = None\nif max_workers_str:\n    use_max_workers = int(max_workers_str)\nweb_concurrency_str = os.getenv(\"WEB_CONCURRENCY\", None)\n\nhost = os.getenv(\"HOST\", \"0.0.0.0\")\nport = os.getenv(\"PORT\", \"5000\")\nbind_env = os.getenv(\"BIND\", None)\nuse_loglevel = os.getenv(\"LOG_LEVEL\", \"info\")\nif bind_env:\n    use_bind = bind_env\nelse:\n    use_bind = f\"{host}:{port}\"\n\ncores = multiprocessing.cpu_count()\nworkers_per_core = float(workers_per_core_str)\ndefault_web_concurrency = workers_per_core * cores\nif web_concurrency_str:\n    web_concurrency = int(web_concurrency_str)\n    assert web_concurrency > 0\nelse:\n    web_concurrency = max(int(default_web_concurrency), 2)\n    if use_max_workers:\n        web_concurrency = min(web_concurrency, use_max_workers)\naccesslog_var = os.getenv(\"ACCESS_LOG\", \"-\")\nuse_accesslog = accesslog_var or None\nerrorlog_var = os.getenv(\"ERROR_LOG\", \"-\")\nuse_errorlog = errorlog_var or None\ngraceful_timeout_str = os.getenv(\"GRACEFUL_TIMEOUT\", \"600\")\ntimeout_str = os.getenv(\"TIMEOUT\", \"600\")\nkeepalive_str = os.getenv(\"KEEP_ALIVE\", \"5\")\n\n# Gunicorn config variables\nloglevel = use_loglevel\nworkers = web_concurrency\nbind = use_bind\nerrorlog = use_errorlog\nworker_tmp_dir = \"/dev/shm\"\naccesslog = use_accesslog\ngraceful_timeout = int(graceful_timeout_str)\ntimeout = int(timeout_str)\nkeepalive = int(keepalive_str)\n\n\n# For debugging and testing\nlog_data = {\n    \"loglevel\": loglevel,\n    \"workers\": workers,\n    \"bind\": bind,\n    \"graceful_timeout\": graceful_timeout,\n    \"timeout\": timeout,\n    \"keepalive\": keepalive,\n    \"errorlog\": errorlog,\n    \"accesslog\": accesslog,\n    # Additional, non-gunicorn variables\n    \"workers_per_core\": workers_per_core,\n    \"use_max_workers\": use_max_workers,\n    \"host\": host,\n    \"port\": port,\n}\nprint(json.dumps(log_data))\n"
  },
  {
    "path": "ethtx_ce/log_cfg.json",
    "content": "{\n  \"version\": 1,\n  \"disable_existing_loggers\": false,\n  \"formatters\": {\n    \"extra\": {\n      \"format\": \"%(asctime)s.%(msecs)03d %(levelname)-8s [%(filename)s:%(lineno)d - %(funcName)s %(process)d/%(processName)s]     %(message)s\",\n      \"datefmt\": \"%Y-%m-%d %H:%M:%S\"\n    }\n  },\n  \"handlers\": {\n    \"console\": {\n      \"class\": \"logging.StreamHandler\",\n      \"level\": \"DEBUG\",\n      \"formatter\": \"extra\",\n      \"stream\": \"ext://sys.stdout\"\n    },\n    \"file_handler\": {\n      \"class\": \"logging.handlers.RotatingFileHandler\",\n      \"level\": \"INFO\",\n      \"formatter\": \"extra\",\n      \"filename\": \"ethtx_ce.log\",\n      \"maxBytes\": 5242880,\n      \"backupCount\": 5,\n      \"encoding\": \"utf8\"\n    }\n  },\n  \"root\": {\n    \"level\": \"INFO\",\n    \"handlers\": [\n      \"file_handler\",\n      \"console\"\n    ]\n  }\n}"
  },
  {
    "path": "ethtx_ce/start-reload.sh",
    "content": "#! /usr/bin/env sh\nset -e\n\n# Start Gunicorn\necho \"Starting Gunicorn...\"\nexec pipenv run gunicorn \"--reload\" -c \"$GUNICORN_CONF\" \"$APP_MODULE\""
  },
  {
    "path": "ethtx_ce/start.sh",
    "content": "#! /usr/bin/env sh\nset -e\n\n# Start Gunicorn\necho \"Starting Gunicorn...\"\nexec pipenv run gunicorn -c \"$GUNICORN_CONF\" \"$APP_MODULE\""
  },
  {
    "path": "ethtx_ce/tests/flask_test.py",
    "content": "import pytest\n\nfrom app.frontend import create_app\n\n\nclass TestFlask:\n    @pytest.fixture\n    def client(self):\n        ethtx = None\n        app = create_app(ethtx)\n        with app.test_client() as client:\n            yield client\n\n    def test_landing_page(self, client):\n        resp = client.get(\"/\")\n        assert resp.status_code == 200\n        # it's a dump check, but it's something\n        assert len(resp.data) > 500\n\n    def test_semantics_is_secured_with_basic_auth(self, client):\n        tx_hash = \"0xf9baa1792d644bbda985324a0bfdc052a806ca1a4b6a3df3578c73025f7fe544\"\n        url = f\"/semantics/mainnet/{tx_hash}/\"\n        resp = client.get(url)\n        assert resp.status_code == 401\n"
  },
  {
    "path": "ethtx_ce/tests/mocks/__init__.py",
    "content": ""
  },
  {
    "path": "ethtx_ce/tests/mocks/mocks.py",
    "content": "from typing import Dict\n\nfrom ethtx.models.w3_model import W3Transaction, W3Block, W3Receipt, W3Log\nfrom ethtx.utils.attr_dict import AttrDict\nfrom hexbytes import HexBytes\n\n\nclass MockWeb3Provider:\n    blocks = {\n        1: {\n            \"difficulty\": 123,  # int\n            \"extraData\": \"test\",  # HexBytes\n            \"gasLimit\": 123,  # int\n            \"gasUsed\": 123,  # int\n            \"hash\": HexBytes(\n                b\"\\x88\\xe9mE7\\xbe\\xa4\\xd9\\xc0]\\x12T\\x99\\x07\\xb3%a\\xd3\\xbf1\\xf4Z\\xaesL\\xdc\\x11\\x9f\\x13@l\\xb6\"\n            ),  # str\n            \"logsBloom\": \"test\",  # HexBytes\n            \"miner\": \"test\",  # str\n            \"nonce\": \"test\",  # HexBytes\n            \"number\": 123,  # int\n            \"parentHash\": HexBytes(\n                b\"\\x88\\xe9mE7\\xbe\\xa4\\xd9\\xc0]\\x12T\\x99\\x07\\xb3%a\\xd3\\xbf1\\xf4Z\\xaesL\\xdc\\x11\\x9f\\x13@l\\xb6\"\n            ),  # str\n            \"receiptsRoot\": \"test\",  # HexBytes\n            \"sha3Uncles\": \"test\",  # HexBytes\n            \"size\": 123,  # int\n            \"stateRoot\": \"test\",  # HexBytes\n            \"timestamp\": 123,  # int,\n            \"totalDifficulty\": 123,  # int\n            \"transactions\": [],  # List\n            \"transactionsRoot\": \"test\",  # HexBytes\n            \"uncles\": [],  # List\n        }\n    }\n\n    txs = {\n        \"0xd7701a0fc05593aee3a16f20cab605db7183f752ae942cc75fd0975feaf1072e\": {\n            \"blockHash\": HexBytes(\n                b\"\\x88\\xe9mE7\\xbe\\xa4\\xd9\\xc0]\\x12T\\x99\\x07\\xb3%a\\xd3\\xbf1\\xf4Z\\xaesL\\xdc\\x11\\x9f\\x13@l\\xb6\"\n            ),  # str\n            \"blockNumber\": 1,  # int\n            \"from_address\": \"fromasd\",  # str\n            \"gas\": 420,  # int\n            \"gasPrice\": 1,  # int\n            \"hash\": \"co\",  # HexBytes,\n            \"input\": \"jeszcze jak\",  # str\n            \"nonce\": 123,  # int\n            \"r\": \"ds\",  # HexBytes\n            \"s\": \"sdf\",  # HexBytes\n            \"to\": \"sdf\",  # str\n            \"transactionIndex\": 1,  # int\n            \"v\": 1,  # int\n            \"value\": 1,  # int\n        }\n    }\n\n    def add_mocked_block_details(self, block_number, block_details: Dict):\n        self.blocks[block_number] = block_details\n\n    def get_transaction(self, tx_hash):\n        return W3Transaction(**self.txs[tx_hash])\n\n    def get_transaction_receipt(self, tx_hash):\n        log_values = AttrDict(\n            {\n                \"address\": \"test\",  # str\n                \"blockHash\": \"test\",  # HexBytes\n                \"blockNumber\": 123,  # int\n                \"data\": \"test\",  # str\n                \"logIndex\": 132,  # int\n                \"removed\": False,  # bool,\n                \"topics\": [HexBytes(\"d\")],  # List[HexBytes]\n                \"transactionHash\": \"test\",  # HexBytes\n                \"transactionIndex\": 123,  # int\n            }\n        )\n        values = {\n            \"blockHash\": \"test\",  # HexBytes\n            \"blockNumber\": 123,  # int\n            \"contractAddress\": 123,  # str\n            \"cumulativeGasUsed\": 132,  # int,\n            \"from_address\": \"from\",  # str\n            \"gasUsed\": 123,  # int\n            \"logs\": [log_values],  # List\n            \"logsBloom\": \"test\",  # HexBytes\n            \"root\": \"test\",  # str\n            \"status\": 123,  # int,\n            \"to_address\": \"test\",  # str\n            \"transactionHash\": \"test\",  # HexBytes\n            \"transactionIndex\": 123,  # int\n        }\n        return W3Receipt(**values)\n\n    def get_block(self, block_number: int) -> W3Block:\n        return W3Block(**self.blocks[block_number])\n\n\nclass Mocks:\n    @staticmethod\n    def get_mocked_w3_log():\n        log_data = {\n            \"address\": \"test\",\n            \"blockHash\": \"test_block_hash\",\n            \"blockNumber\": 123,\n            \"data\": \"data\",\n            \"logIndex\": 1,\n            \"removed\": False,\n            \"topics\": [1, 2, 3, 4],\n            \"transactionHash\": \"txHash\",\n            \"transactionIndex\": 1,\n        }\n\n        log = W3Log(**log_data)\n        return log\n\n\nclass TestMocks:\n    def test_can_create_w3_log(self):\n        w3log = Mocks.get_mocked_w3_log()\n        assert w3log is not None\n"
  },
  {
    "path": "scripts/git_version_for_docker.sh",
    "content": "#! /usr/bin/env bash\n\n# Execute in root project directory\n# Exit in case of error\nset -e\n\nremote_url=$(git config --get remote.origin.url)\nsha=$(git rev-parse HEAD)\n\n# set env variables\nexport GIT_URL=\"${remote_url}\"\nexport GIT_SHA=\"${sha}\"\n\n# url, sha\nurl_sha=\"${remote_url},${sha}\"\n\n# return\necho \"$url_sha\"\n"
  }
]