diff options
author | F. Eugene Aumson <feuGeneA@users.noreply.github.com> | 2019-01-09 22:58:29 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-01-09 22:58:29 +0800 |
commit | aa5af04447dfae24731557c6beead55bd8ff99a9 (patch) | |
tree | 1ffcc631ab078c88f85e2ab2b708f5d91b731cea /python-packages/contract_addresses | |
parent | 5b7eff217e9c8d09d64ff8721d7a16e1df8a7c58 (diff) | |
download | dexon-sol-tools-aa5af04447dfae24731557c6beead55bd8ff99a9.tar.gz dexon-sol-tools-aa5af04447dfae24731557c6beead55bd8ff99a9.tar.zst dexon-sol-tools-aa5af04447dfae24731557c6beead55bd8ff99a9.zip |
Python contract demo, with lots of refactoring (#1485)
* Refine Order for Web3 compat. & add conversions
Changed some of the fields in the Order class so that it can be passed
to our contracts via Web3.
Added conversion utilities so that an Order can be easily converted to
and from a JSON-compatible dict (specifically by encoding/decoding the
`bytes` fields), to facilitate validation against the JSON schema.
Also modified JSON order schema to accept integers in addition to
stringified integers.
* Fixes for json_schemas
Has-types indicator file, py.typed, was not being included in package.
Schemas were not being properly gathered into package installation.
* Add test/demo of Exchange.getOrderInfo()
* web3 bug workaround
* Fix problem packaging contract artifacts
* Move contract addresses to their own package
* Move contract artifacts to their own package
* Add scripts to install, test & lint all components
* prettierignore files in local python dev env
* Correct missing coverage analysis for sra_client
* CI cache lint: don't save, re-use from test-python
* tag hacks as hacks
* correct merge mistake
* remove local strip_0x() in favor of eth_utils
* remove json schemas from old order_utils location
* correct merge mistake
* doctest json schemas via command-line, not code
Diffstat (limited to 'python-packages/contract_addresses')
16 files changed, 443 insertions, 0 deletions
diff --git a/python-packages/contract_addresses/.discharge.json b/python-packages/contract_addresses/.discharge.json new file mode 100644 index 000000000..d6c90a20f --- /dev/null +++ b/python-packages/contract_addresses/.discharge.json @@ -0,0 +1,13 @@ +{ + "domain": "0x-contract-addresses-py", + "build_command": "python setup.py build_sphinx", + "upload_directory": "build/docs/html", + "index_key": "index.html", + "error_key": "index.html", + "trailing_slashes": true, + "cache": 3600, + "aws_profile": "default", + "aws_region": "us-east-1", + "cdn": false, + "dns_configured": true +} diff --git a/python-packages/contract_addresses/README.md b/python-packages/contract_addresses/README.md new file mode 100644 index 000000000..e2714afdf --- /dev/null +++ b/python-packages/contract_addresses/README.md @@ -0,0 +1,45 @@ +## 0x-contract-addresses + +Addresses at which the 0x smart contracts have been deployed. + +Read the [documentation](http://0x-contract-addresses-py.s3-website-us-east-1.amazonaws.com/) + +## Installing + +```bash +pip install 0x-contract-addresses +``` + +## Contributing + +We welcome improvements and fixes from the wider community! To report bugs within this package, please create an issue in this repository. + +Please read our [contribution guidelines](../../CONTRIBUTING.md) before getting started. + +### Install Code and Dependencies + +Ensure that you have installed Python >=3.6 and Docker. Then: + +```bash +pip install -e .[dev] +``` + +### Test + +Tests depend on a running ganache instance with the 0x contracts deployed in it. For convenience, a docker container is provided that has ganache-cli and a snapshot containing the necessary contracts. A shortcut is provided to run that docker container: `./setup.py ganache`. With that running, the tests can be run with `./setup.py test`. + +### Clean + +`./setup.py clean --all` + +### Lint + +`./setup.py lint` + +### Build Documentation + +`./setup.py build_sphinx` + +### More + +See `./setup.py --help-commands` for more info. diff --git a/python-packages/contract_addresses/setup.py b/python-packages/contract_addresses/setup.py new file mode 100755 index 000000000..9ddafbeea --- /dev/null +++ b/python-packages/contract_addresses/setup.py @@ -0,0 +1,181 @@ +#!/usr/bin/env python + +"""setuptools module for contract_addresses package.""" + +import subprocess # nosec +from shutil import rmtree +from os import environ, path +from sys import argv + +from distutils.command.clean import clean +import distutils.command.build_py +from setuptools import find_packages, setup + + +class LintCommand(distutils.command.build_py.build_py): + """Custom setuptools command class for running linters.""" + + description = "Run linters" + + def run(self): + """Run linter shell commands.""" + lint_commands = [ + # formatter: + "black --line-length 79 --check --diff src setup.py".split(), + # style guide checker (formerly pep8): + "pycodestyle --show-source --show-pep8 src setup.py".split(), + # docstring style checker: + "pydocstyle src setup.py".split(), + # static type checker: + "mypy src setup.py".split(), + # security issue checker: + "bandit -r src ./setup.py".split(), + # run doctests: + "pytest --doctest-modules".split(), + # general linter: + "pylint src setup.py".split(), + # pylint takes relatively long to run, so it runs last, to enable + # fast failures. + ] + + # tell mypy where to find interface stubs for 3rd party libs + environ["MYPYPATH"] = path.join( + path.dirname(path.realpath(argv[0])), "stubs" + ) + + for lint_command in lint_commands: + print( + "Running lint command `", " ".join(lint_command).strip(), "`" + ) + subprocess.check_call(lint_command) # nosec + + +class CleanCommandExtension(clean): + """Custom command to do custom cleanup.""" + + def run(self): + """Run the regular clean, followed by our custom commands.""" + super().run() + rmtree("dist", ignore_errors=True) + rmtree(".mypy_cache", ignore_errors=True) + rmtree(".tox", ignore_errors=True) + rmtree(".pytest_cache", ignore_errors=True) + rmtree("src/0x_contract_addresses.egg-info", ignore_errors=True) + + +class TestPublishCommand(distutils.command.build_py.build_py): + """Custom command to publish to test.pypi.org.""" + + description = ( + "Publish dist/* to test.pypi.org. Run sdist & bdist_wheel first." + ) + + def run(self): + """Run twine to upload to test.pypi.org.""" + subprocess.check_call( # nosec + ( + "twine upload --repository-url https://test.pypi.org/legacy/" + + " --verbose dist/*" + ).split() + ) + + +class PublishCommand(distutils.command.build_py.build_py): + """Custom command to publish to pypi.org.""" + + description = "Publish dist/* to pypi.org. Run sdist & bdist_wheel first." + + def run(self): + """Run twine to upload to pypi.org.""" + subprocess.check_call("twine upload dist/*".split()) # nosec + + +class PublishDocsCommand(distutils.command.build_py.build_py): + """Custom command to publish docs to S3.""" + + description = ( + "Publish docs to " + + "http://0x-contract-addresses-py.s3-website-us-east-1.amazonaws.com/" + ) + + def run(self): + """Run npm package `discharge` to build & upload docs.""" + subprocess.check_call("discharge deploy".split()) # nosec + + +with open("README.md", "r") as file_handle: + README_MD = file_handle.read() + + +setup( + name="0x-contract-addresses", + version="2.0.0", + description="Addresses at which the 0x smart contracts have been deployed", + long_description=README_MD, + long_description_content_type="text/markdown", + url=( + "https://github.com/0xproject/0x-monorepo/tree/development" + + "/python-packages/contract_addresses" + ), + author="F. Eugene Aumson", + author_email="feuGeneA@users.noreply.github.com", + cmdclass={ + "clean": CleanCommandExtension, + "lint": LintCommand, + "test_publish": TestPublishCommand, + "publish": PublishCommand, + "publish_docs": PublishDocsCommand, + }, + install_requires=["mypy_extensions"], + extras_require={ + "dev": [ + "bandit", + "black", + "coverage", + "coveralls", + "mypy", + "mypy_extensions", + "pycodestyle", + "pydocstyle", + "pylint", + "pytest", + "sphinx", + "tox", + "twine", + ] + }, + python_requires=">=3.6, <4", + package_data={"zero_ex.contract_addresses": ["py.typed"]}, + package_dir={"": "src"}, + license="Apache 2.0", + keywords=( + "ethereum cryptocurrency 0x decentralized blockchain dex exchange" + ), + namespace_packages=["zero_ex"], + packages=find_packages("src"), + classifiers=[ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "Intended Audience :: Financial and Insurance Industry", + "License :: OSI Approved :: Apache Software License", + "Natural Language :: English", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", + "Topic :: Internet :: WWW/HTTP", + "Topic :: Office/Business :: Financial", + "Topic :: Other/Nonlisted Topic", + "Topic :: Security :: Cryptography", + "Topic :: Software Development :: Libraries", + "Topic :: Utilities", + ], + zip_safe=False, # required per mypy + command_options={ + "build_sphinx": { + "source_dir": ("setup.py", "src"), + "build_dir": ("setup.py", "build/docs"), + } + }, +) diff --git a/python-packages/contract_addresses/src/conf.py b/python-packages/contract_addresses/src/conf.py new file mode 100644 index 000000000..a0f372bc5 --- /dev/null +++ b/python-packages/contract_addresses/src/conf.py @@ -0,0 +1,54 @@ +"""Configuration file for the Sphinx documentation builder.""" + +# Reference: http://www.sphinx-doc.org/en/master/config + +from typing import List +import pkg_resources + + +# pylint: disable=invalid-name +# because these variables are not named in upper case, as globals should be. + +project = "0x-contract-addresses" +# pylint: disable=redefined-builtin +copyright = "2018, ZeroEx, Intl." +author = "F. Eugene Aumson" +version = pkg_resources.get_distribution("0x-contract-addresses").version +release = "" # The full version, including alpha/beta/rc tags + +extensions = [ + "sphinx.ext.autodoc", + "sphinx.ext.doctest", + "sphinx.ext.intersphinx", + "sphinx.ext.coverage", + "sphinx.ext.viewcode", +] + +templates_path = ["doc_templates"] + +source_suffix = ".rst" +# eg: source_suffix = [".rst", ".md"] + +master_doc = "index" # The master toctree document. + +language = None + +exclude_patterns: List[str] = [] + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = None + +html_theme = "alabaster" + +html_static_path = ["doc_static"] +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". + +# Output file base name for HTML help builder. +htmlhelp_basename = "contract_addressespydoc" + +# -- Extension configuration: + +# Example configuration for intersphinx: refer to the Python standard library. +intersphinx_mapping = {"https://docs.python.org/": None} diff --git a/python-packages/contract_addresses/src/doc_static/.gitkeep b/python-packages/contract_addresses/src/doc_static/.gitkeep new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python-packages/contract_addresses/src/doc_static/.gitkeep diff --git a/python-packages/contract_addresses/src/index.rst b/python-packages/contract_addresses/src/index.rst new file mode 100644 index 000000000..7ac329ce2 --- /dev/null +++ b/python-packages/contract_addresses/src/index.rst @@ -0,0 +1,25 @@ +.. source for the sphinx-generated build/docs/web/index.html + +Python zero_ex.contract_addresses +================================= + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + +.. autoclass:: zero_ex.contract_addresses.NetworkId + + See source for enum members. + +.. autoclass:: zero_ex.contract_addresses.ContractAddresses + :members: + +.. autodata:: zero_ex.contract_addresses.NETWORK_TO_ADDRESSES + :annotation: + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/python-packages/contract_addresses/src/zero_ex/__init__.py b/python-packages/contract_addresses/src/zero_ex/__init__.py new file mode 100644 index 000000000..e90d833db --- /dev/null +++ b/python-packages/contract_addresses/src/zero_ex/__init__.py @@ -0,0 +1,2 @@ +"""0x Python API.""" +__import__("pkg_resources").declare_namespace(__name__) diff --git a/python-packages/contract_addresses/src/zero_ex/contract_addresses/__init__.py b/python-packages/contract_addresses/src/zero_ex/contract_addresses/__init__.py new file mode 100644 index 000000000..a4bfc3f4c --- /dev/null +++ b/python-packages/contract_addresses/src/zero_ex/contract_addresses/__init__.py @@ -0,0 +1,93 @@ +"""Addresses at which the 0x smart contracts have been deployed.""" + +from enum import Enum +from typing import Dict, NamedTuple + + +class ContractAddresses(NamedTuple): # noqa + """An abstract record listing all the contracts that have addresses.""" + + erc20_proxy: str + erc721_proxy: str + zrx_token: str + ether_token: str + exchange: str + asset_proxy_owner: str + forwarder: str + order_validator: str + + +class NetworkId(Enum): + """Network names correlated to their network identification numbers. + + >>> NetworkId.MAINNET + <NetworkId.MAINNET: 1> + """ + + MAINNET = 1 + ROPSTEN = 3 + RINKEBY = 4 + KOVAN = 42 + GANACHE = 50 + + +NETWORK_TO_ADDRESSES: Dict[NetworkId, ContractAddresses] = { + NetworkId.MAINNET: ContractAddresses( + erc20_proxy="0x2240dab907db71e64d3e0dba4800c83b5c502d4e", + erc721_proxy="0x208e41fb445f1bb1b6780d58356e81405f3e6127", + zrx_token="0xe41d2489571d322189246dafa5ebde1f4699f498", + ether_token="0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", + exchange="0x4f833a24e1f95d70f028921e27040ca56e09ab0b", + asset_proxy_owner="0x17992e4ffb22730138e4b62aaa6367fa9d3699a6", + forwarder="0x5468a1dc173652ee28d249c271fa9933144746b1", + order_validator="0x9463e518dea6810309563c81d5266c1b1d149138", + ), + NetworkId.ROPSTEN: ContractAddresses( + erc20_proxy="0xb1408f4c245a23c31b98d2c626777d4c0d766caa", + erc721_proxy="0xe654aac058bfbf9f83fcaee7793311dd82f6ddb4", + zrx_token="0xff67881f8d12f372d91baae9752eb3631ff0ed00", + ether_token="0xc778417e063141139fce010982780140aa0cd5ab", + exchange="0x4530c0483a1633c7a1c97d2c53721caff2caaaaf", + asset_proxy_owner="0xf5fa5b5fed2727a0e44ac67f6772e97977aa358b", + forwarder="0x2240dab907db71e64d3e0dba4800c83b5c502d4e", + order_validator="0x90431a90516ab49af23a0530e04e8c7836e7122f", + ), + NetworkId.RINKEBY: ContractAddresses( + exchange="0xbce0b5f6eb618c565c3e5f5cd69652bbc279f44e", + erc20_proxy="0x2f5ae4f6106e89b4147651688a92256885c5f410", + erc721_proxy="0x7656d773e11ff7383a14dcf09a9c50990481cd10", + zrx_token="0x8080c7e4b81ecf23aa6f877cfbfd9b0c228c6ffa", + ether_token="0xc778417e063141139fce010982780140aa0cd5ab", + asset_proxy_owner="0xe1703da878afcebff5b7624a826902af475b9c03", + forwarder="0x2d40589abbdee84961f3a7656b9af7adb0ee5ab4", + order_validator="0x0c5173a51e26b29d6126c686756fb9fbef71f762", + ), + NetworkId.KOVAN: ContractAddresses( + erc20_proxy="0xf1ec01d6236d3cd881a0bf0130ea25fe4234003e", + erc721_proxy="0x2a9127c745688a165106c11cd4d647d2220af821", + zrx_token="0x2002d3812f58e35f0ea1ffbf80a75a38c32175fa", + ether_token="0xd0a1e359811322d97991e03f863a0c30c2cf029c", + exchange="0x35dd2932454449b14cee11a94d3674a936d5d7b2", + asset_proxy_owner="0x2c824d2882baa668e0d5202b1e7f2922278703f8", + forwarder="0x17992e4ffb22730138e4b62aaa6367fa9d3699a6", + order_validator="0xb389da3d204b412df2f75c6afb3d0a7ce0bc283d", + ), + NetworkId.GANACHE: ContractAddresses( + exchange="0x48bacb9266a570d521063ef5dd96e61686dbe788", + erc20_proxy="0x1dc4c1cefef38a777b15aa20260a54e584b16c48", + erc721_proxy="0x1d7022f5b17d2f8b695918fb48fa1089c9f85401", + zrx_token="0x871dd7c2b4b25e1aa18728e9d5f2af4c4e431f5c", + ether_token="0x0b1ba0af832d7c05fd64161e0db78e85978e8082", + asset_proxy_owner="0x34d402f14d58e001d8efbe6585051bf9706aa064", + forwarder="0xb69e673309512a9d726f87304c6984054f87a93b", + order_validator="0xe86bb98fcf9bff3512c74589b78fb168200cc546", + ), +} +"""A mapping from instances of NetworkId to instances of ContractAddresses. + +Addresses under NetworkId.Ganache are from our Ganache snapshot generated from +migrations. + +>>> NETWORK_TO_ADDRESSES[NetworkId.MAINNET].exchange +0x4f833a24e1f95d70f028921e27040ca56e09ab0b +""" diff --git a/python-packages/contract_addresses/src/zero_ex/contract_addresses/py.typed b/python-packages/contract_addresses/src/zero_ex/contract_addresses/py.typed new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python-packages/contract_addresses/src/zero_ex/contract_addresses/py.typed diff --git a/python-packages/contract_addresses/stubs/distutils/__init__.pyi b/python-packages/contract_addresses/stubs/distutils/__init__.pyi new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python-packages/contract_addresses/stubs/distutils/__init__.pyi diff --git a/python-packages/contract_addresses/stubs/distutils/command/__init__.pyi b/python-packages/contract_addresses/stubs/distutils/command/__init__.pyi new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python-packages/contract_addresses/stubs/distutils/command/__init__.pyi diff --git a/python-packages/contract_addresses/stubs/distutils/command/clean.pyi b/python-packages/contract_addresses/stubs/distutils/command/clean.pyi new file mode 100644 index 000000000..46a42ddb1 --- /dev/null +++ b/python-packages/contract_addresses/stubs/distutils/command/clean.pyi @@ -0,0 +1,7 @@ +from distutils.core import Command + +class clean(Command): + def initialize_options(self: clean) -> None: ... + def finalize_options(self: clean) -> None: ... + def run(self: clean) -> None: ... + ... diff --git a/python-packages/contract_addresses/stubs/setuptools/__init__.pyi b/python-packages/contract_addresses/stubs/setuptools/__init__.pyi new file mode 100644 index 000000000..8ea8d32b7 --- /dev/null +++ b/python-packages/contract_addresses/stubs/setuptools/__init__.pyi @@ -0,0 +1,8 @@ +from distutils.dist import Distribution +from typing import Any, List + +def setup(**attrs: Any) -> Distribution: ... + +class Command: ... + +def find_packages(where: str) -> List[str]: ... diff --git a/python-packages/contract_addresses/stubs/setuptools/command/__init__.pyi b/python-packages/contract_addresses/stubs/setuptools/command/__init__.pyi new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python-packages/contract_addresses/stubs/setuptools/command/__init__.pyi diff --git a/python-packages/contract_addresses/stubs/setuptools/command/test.pyi b/python-packages/contract_addresses/stubs/setuptools/command/test.pyi new file mode 100644 index 000000000..c5ec770ad --- /dev/null +++ b/python-packages/contract_addresses/stubs/setuptools/command/test.pyi @@ -0,0 +1,3 @@ +from setuptools import Command + +class test(Command): ... diff --git a/python-packages/contract_addresses/tox.ini b/python-packages/contract_addresses/tox.ini new file mode 100644 index 000000000..aa1037ed7 --- /dev/null +++ b/python-packages/contract_addresses/tox.ini @@ -0,0 +1,12 @@ +# tox (https://tox.readthedocs.io/) is a tool for running tests +# in multiple virtualenvs. This configuration file will run the +# test suite on all supported python versions. To use it, "pip install tox" +# and then run "tox" from this directory. + +[tox] +envlist = py37 + +[testenv] +commands = + pip install -e .[dev] + python setup.py lint |