diff options
author | Hsuan Lee <boczeratul@gmail.com> | 2019-03-06 17:46:50 +0800 |
---|---|---|
committer | Hsuan Lee <boczeratul@gmail.com> | 2019-03-06 17:46:50 +0800 |
commit | 35703539d0f2b4ddb3b11d0de8c9634af59ab71f (patch) | |
tree | ae3731221dbbb3a6fa40060a8d916cfd3f738289 /python-packages/order_utils/src | |
parent | 92a1fde5b1ecd81b07cdb5bf0c9c1cd3544799db (diff) | |
download | dexon-0x-contracts-35703539d0f2b4ddb3b11d0de8c9634af59ab71f.tar.gz dexon-0x-contracts-35703539d0f2b4ddb3b11d0de8c9634af59ab71f.tar.zst dexon-0x-contracts-35703539d0f2b4ddb3b11d0de8c9634af59ab71f.zip |
Deploy @dexon-foundation/0x.jsstable
Diffstat (limited to 'python-packages/order_utils/src')
11 files changed, 0 insertions, 958 deletions
diff --git a/python-packages/order_utils/src/conf.py b/python-packages/order_utils/src/conf.py deleted file mode 100644 index d8f56b29e..000000000 --- a/python-packages/order_utils/src/conf.py +++ /dev/null @@ -1,55 +0,0 @@ -"""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-order-utils" -# pylint: disable=redefined-builtin -copyright = "2018, ZeroEx, Intl." -author = "F. Eugene Aumson" -version = pkg_resources.get_distribution("0x-order-utils").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", - "sphinx_autodoc_typehints", -] - -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 = "order_utilspydoc" - -# -- Extension configuration: - -# Example configuration for intersphinx: refer to the Python standard library. -intersphinx_mapping = {"https://docs.python.org/": None} diff --git a/python-packages/order_utils/src/doc_static/.gitkeep b/python-packages/order_utils/src/doc_static/.gitkeep deleted file mode 100644 index e69de29bb..000000000 --- a/python-packages/order_utils/src/doc_static/.gitkeep +++ /dev/null diff --git a/python-packages/order_utils/src/doc_templates/.gitkeep b/python-packages/order_utils/src/doc_templates/.gitkeep deleted file mode 100644 index e69de29bb..000000000 --- a/python-packages/order_utils/src/doc_templates/.gitkeep +++ /dev/null diff --git a/python-packages/order_utils/src/index.rst b/python-packages/order_utils/src/index.rst deleted file mode 100644 index ce618c6ff..000000000 --- a/python-packages/order_utils/src/index.rst +++ /dev/null @@ -1,36 +0,0 @@ -.. source for the sphinx-generated build/docs/web/index.html - -Python zero_ex.order_utils -========================== - -.. toctree:: - :maxdepth: 2 - :caption: Contents: - -.. automodule:: zero_ex.order_utils - :members: - -.. autoclass:: zero_ex.order_utils.Order - - See source for class properties. Sphinx is having problems generating docs for ``TypedDict`` declarations; pull requests welcome. - -zero_ex.order_utils.asset_data_utils ------------------------------------- - -.. automodule:: zero_ex.order_utils.asset_data_utils - :members: - -.. autoclass:: zero_ex.order_utils.asset_data_utils.ERC20AssetData - - See source for class properties. Sphinx is having problems generating docs for ``TypedDict`` declarations; pull requests welcome. - -.. autoclass:: zero_ex.order_utils.asset_data_utils.ERC721AssetData - - See source for class properties. Sphinx is having problems generating docs for ``TypedDict`` declarations; pull requests welcome. - -Indices and tables -================== - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` diff --git a/python-packages/order_utils/src/zero_ex/__init__.py b/python-packages/order_utils/src/zero_ex/__init__.py deleted file mode 100644 index e90d833db..000000000 --- a/python-packages/order_utils/src/zero_ex/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -"""0x Python API.""" -__import__("pkg_resources").declare_namespace(__name__) diff --git a/python-packages/order_utils/src/zero_ex/dev_utils/__init__.py b/python-packages/order_utils/src/zero_ex/dev_utils/__init__.py deleted file mode 100644 index b6a224d2c..000000000 --- a/python-packages/order_utils/src/zero_ex/dev_utils/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Dev utils to be shared across 0x projects and packages.""" diff --git a/python-packages/order_utils/src/zero_ex/dev_utils/abi_utils.py b/python-packages/order_utils/src/zero_ex/dev_utils/abi_utils.py deleted file mode 100644 index 3fec775b0..000000000 --- a/python-packages/order_utils/src/zero_ex/dev_utils/abi_utils.py +++ /dev/null @@ -1,101 +0,0 @@ -"""Ethereum ABI utilities. - -Builds on the eth-abi package, adding some convenience methods like those found -in npmjs.com/package/ethereumjs-abi. Ideally, all of this code should be -pushed upstream into eth-abi. -""" - -import re -from typing import Any, List - -from mypy_extensions import TypedDict - -from web3 import Web3 -from eth_abi import encode_abi - -from .type_assertions import assert_is_string, assert_is_list - - -class MethodSignature(TypedDict, total=False): - """Object interface to an ABI method signature.""" - - method: str - args: List[str] - - -def parse_signature(signature: str) -> MethodSignature: - """Parse a method signature into its constituent parts. - - >>> parse_signature("ERC20Token(address)") - {'method': 'ERC20Token', 'args': ['address']} - """ - assert_is_string(signature, "signature") - - matches = re.match(r"^(\w+)\((.+)\)$", signature) - if matches is None: - raise ValueError(f"Invalid method signature {signature}") - return {"method": matches[1], "args": matches[2].split(",")} - - -def elementary_name(name: str) -> str: - """Convert from short to canonical names; barely implemented. - - Modeled after ethereumjs-abi's ABI.elementaryName(), but only implemented - to support our particular use case and a few other simple ones. - - >>> elementary_name("address") - 'address' - >>> elementary_name("uint") - 'uint256' - """ - assert_is_string(name, "name") - - return { - "int": "int256", - "uint": "uint256", - "fixed": "fixed128x128", - "ufixed": "ufixed128x128", - }.get(name, name) - - -def event_id(name: str, types: List[str]) -> str: - """Return the Keccak-256 hash of the given method. - - >>> event_id("ERC20Token", ["address"]) - '0xf47261b06eedbfce68afd46d0f3c27c60b03faad319eaf33103611cf8f6456ad' - """ - assert_is_string(name, "name") - assert_is_list(types, "types") - - signature = f"{name}({','.join(list(map(elementary_name, types)))})" - return Web3.sha3(text=signature).hex() - - -def method_id(name: str, types: List[str]) -> str: - """Return the 4-byte method identifier. - - >>> method_id("ERC20Token", ["address"]) - '0xf47261b0' - """ - assert_is_string(name, "name") - assert_is_list(types, "types") - - return event_id(name, types)[0:10] - - -def simple_encode(method: str, *args: Any) -> bytes: - r"""Encode a method ABI. - - >>> simple_encode("ERC20Token(address)", "0x1dc4c1cefef38a777b15aa20260a54e584b16c48") - b'\xf4ra\xb0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1d\xc4\xc1\xce\xfe\xf3\x8aw{\x15\xaa &\nT\xe5\x84\xb1lH' - """ # noqa: E501 (line too long) - assert_is_string(method, "method") - - signature: MethodSignature = parse_signature(method) - - return bytes.fromhex( - ( - method_id(signature["method"], signature["args"]) - + encode_abi(signature["args"], args).hex() - )[2:] - ) diff --git a/python-packages/order_utils/src/zero_ex/dev_utils/type_assertions.py b/python-packages/order_utils/src/zero_ex/dev_utils/type_assertions.py deleted file mode 100644 index 4681315a8..000000000 --- a/python-packages/order_utils/src/zero_ex/dev_utils/type_assertions.py +++ /dev/null @@ -1,87 +0,0 @@ -"""Assertions for runtime type checking of function arguments.""" - -from typing import Any - -from eth_utils import is_address -from web3.providers.base import BaseProvider - - -def assert_is_string(value: Any, name: str) -> None: - """If :param value: isn't of type str, raise a TypeError. - - >>> try: assert_is_string(123, 'var') - ... except TypeError as type_error: print(str(type_error)) - ... - expected variable 'var', with value 123, to have type 'str', not 'int' - """ - if not isinstance(value, str): - raise TypeError( - f"expected variable '{name}', with value {str(value)}, to have" - + f" type 'str', not '{type(value).__name__}'" - ) - - -def assert_is_list(value: Any, name: str) -> None: - """If :param value: isn't of type list, raise a TypeError. - - >>> try: assert_is_list(123, 'var') - ... except TypeError as type_error: print(str(type_error)) - ... - expected variable 'var', with value 123, to have type 'list', not 'int' - """ - if not isinstance(value, list): - raise TypeError( - f"expected variable '{name}', with value {str(value)}, to have" - + f" type 'list', not '{type(value).__name__}'" - ) - - -def assert_is_int(value: Any, name: str) -> None: - """If :param value: isn't of type int, raise a TypeError. - - >>> try: assert_is_int('asdf', 'var') - ... except TypeError as type_error: print(str(type_error)) - ... - expected variable 'var', with value asdf, to have type 'int', not 'str' - """ - if not isinstance(value, int): - raise TypeError( - f"expected variable '{name}', with value {str(value)}, to have" - + f" type 'int', not '{type(value).__name__}'" - ) - - -def assert_is_hex_string(value: Any, name: str) -> None: - """Assert that :param value: is a string of hex chars. - - If :param value: isn't a str, raise a TypeError. If it is a string but - contains non-hex characters ("0x" prefix permitted), raise a ValueError. - """ - assert_is_string(value, name) - int(value, 16) # raises a ValueError if value isn't a base-16 str - - -def assert_is_address(value: Any, name: str) -> None: - """Assert that `value` is a valid Ethereum address. - - If `value` isn't a hex string, raise a TypeError. If it isn't a valid - Ethereum address, raise a ValueError. - """ - assert_is_hex_string(value, name) - if not is_address(value): - raise ValueError( - f"Expected variable '{name}' to be a valid Ethereum" - + " address, but it's not." - ) - - -def assert_is_provider(value: Any, name: str) -> None: - """Assert that `value` is a Web3 provider. - - If `value` isn't a Web3 provider, raise a TypeError. - """ - if not isinstance(value, BaseProvider): - raise TypeError( - f"Expected variable '{name}' to be an instance of a Web3 provider," - + " but it's not." - ) diff --git a/python-packages/order_utils/src/zero_ex/order_utils/__init__.py b/python-packages/order_utils/src/zero_ex/order_utils/__init__.py deleted file mode 100644 index 39d411507..000000000 --- a/python-packages/order_utils/src/zero_ex/order_utils/__init__.py +++ /dev/null @@ -1,535 +0,0 @@ -"""Order utilities for 0x applications. - -Some methods require the caller to pass in a `Web3.BaseProvider`:code: object. -For local testing one may construct such a provider pointing at an instance of -`ganache-cli <https://www.npmjs.com/package/ganache-cli>`_ which has the 0x -contracts deployed on it. For convenience, a docker container is provided for -just this purpose. To start it: -`docker run -d -p 8545:8545 0xorg/ganache-cli:2.2.2`:code:. -""" - -from copy import copy -from enum import auto, Enum -import json -from typing import cast, Dict, NamedTuple, Tuple -from pkg_resources import resource_string - -from mypy_extensions import TypedDict - -from eth_utils import keccak, remove_0x_prefix, to_bytes, to_checksum_address -from web3 import Web3 -import web3.exceptions -from web3.providers.base import BaseProvider -from web3.utils import datatypes - -from zero_ex.contract_addresses import NETWORK_TO_ADDRESSES, NetworkId -import zero_ex.contract_artifacts -from zero_ex.dev_utils.type_assertions import ( - assert_is_address, - assert_is_hex_string, - assert_is_provider, -) -from zero_ex.json_schemas import assert_valid - - -class _Constants: - """Static data used by order utilities.""" - - null_address = "0x0000000000000000000000000000000000000000" - - eip191_header = b"\x19\x01" - - eip712_domain_separator_schema_hash = keccak( - b"EIP712Domain(string name,string version,address verifyingContract)" - ) - - eip712_domain_struct_header = ( - eip712_domain_separator_schema_hash - + keccak(b"0x Protocol") - + keccak(b"2") - ) - - eip712_order_schema_hash = keccak( - b"Order(" - + b"address makerAddress," - + b"address takerAddress," - + b"address feeRecipientAddress," - + b"address senderAddress," - + b"uint256 makerAssetAmount," - + b"uint256 takerAssetAmount," - + b"uint256 makerFee," - + b"uint256 takerFee," - + b"uint256 expirationTimeSeconds," - + b"uint256 salt," - + b"bytes makerAssetData," - + b"bytes takerAssetData" - + b")" - ) - - class SignatureType(Enum): - """Enumeration of known signature types.""" - - ILLEGAL = 0 - INVALID = auto() - EIP712 = auto() - ETH_SIGN = auto() - WALLET = auto() - VALIDATOR = auto() - PRE_SIGNED = auto() - N_SIGNATURE_TYPES = auto() - - -class Order(TypedDict): # pylint: disable=too-many-instance-attributes - """A Web3-compatible representation of the Exchange.Order struct.""" - - makerAddress: str - """Address that created the order.""" - - takerAddress: str - """Address that is allowed to fill the order. - - If set to 0, any address is allowed to fill the order. - """ - - feeRecipientAddress: str - """Address that will recieve fees when order is filled.""" - - senderAddress: str - """Address that is allowed to call Exchange contract methods that affect - this order. If set to 0, any address is allowed to call these methods. - """ - - makerAssetAmount: int - """Amount of makerAsset being offered by maker. Must be greater than 0.""" - - takerAssetAmount: int - """Amount of takerAsset being bid on by maker. Must be greater than 0.""" - - makerFee: int - """Amount of ZRX paid to feeRecipient by maker when order is filled. If - set to 0, no transfer of ZRX from maker to feeRecipient will be attempted. - """ - - takerFee: int - """Amount of ZRX paid to feeRecipient by taker when order is filled. If - set to 0, no transfer of ZRX from taker to feeRecipient will be attempted. - """ - - expirationTimeSeconds: int - """Timestamp in seconds at which order expires.""" - - salt: int - """Arbitrary number to facilitate uniqueness of the order's hash.""" - - makerAssetData: bytes - """Encoded data that can be decoded by a specified proxy contract when - transferring makerAsset. The last byte references the id of this proxy. - """ - - takerAssetData: bytes - """Encoded data that can be decoded by a specified proxy contract when - transferring takerAsset. The last byte references the id of this proxy. - """ - - -def make_empty_order() -> Order: - """Construct an empty order. - - Initializes all strings to "0x0000000000000000000000000000000000000000", - all numbers to 0, and all bytes to nulls. - """ - return { - "makerAddress": _Constants.null_address, - "takerAddress": _Constants.null_address, - "senderAddress": _Constants.null_address, - "feeRecipientAddress": _Constants.null_address, - "makerAssetData": (b"\x00") * 20, - "takerAssetData": (b"\x00") * 20, - "salt": 0, - "makerFee": 0, - "takerFee": 0, - "makerAssetAmount": 0, - "takerAssetAmount": 0, - "expirationTimeSeconds": 0, - } - - -def order_to_jsdict( - order: Order, exchange_address="0x0000000000000000000000000000000000000000" -) -> dict: - """Convert a Web3-compatible order struct to a JSON-schema-compatible dict. - - More specifically, do explicit decoding for the `bytes`:code: fields. - - >>> import pprint - >>> pprint.pprint(order_to_jsdict( - ... { - ... 'makerAddress': "0x0000000000000000000000000000000000000000", - ... 'takerAddress': "0x0000000000000000000000000000000000000000", - ... 'feeRecipientAddress': - ... "0x0000000000000000000000000000000000000000", - ... 'senderAddress': "0x0000000000000000000000000000000000000000", - ... 'makerAssetAmount': 1, - ... 'takerAssetAmount': 1, - ... 'makerFee': 0, - ... 'takerFee': 0, - ... 'expirationTimeSeconds': 1, - ... 'salt': 1, - ... 'makerAssetData': (0).to_bytes(1, byteorder='big') * 20, - ... 'takerAssetData': (0).to_bytes(1, byteorder='big') * 20, - ... }, - ... )) - {'exchangeAddress': '0x0000000000000000000000000000000000000000', - 'expirationTimeSeconds': 1, - 'feeRecipientAddress': '0x0000000000000000000000000000000000000000', - 'makerAddress': '0x0000000000000000000000000000000000000000', - 'makerAssetAmount': 1, - 'makerAssetData': '0x0000000000000000000000000000000000000000', - 'makerFee': 0, - 'salt': 1, - 'senderAddress': '0x0000000000000000000000000000000000000000', - 'takerAddress': '0x0000000000000000000000000000000000000000', - 'takerAssetAmount': 1, - 'takerAssetData': '0x0000000000000000000000000000000000000000', - 'takerFee': 0} - """ - jsdict = cast(Dict, copy(order)) - - # encode bytes fields - jsdict["makerAssetData"] = "0x" + order["makerAssetData"].hex() - jsdict["takerAssetData"] = "0x" + order["takerAssetData"].hex() - - jsdict["exchangeAddress"] = exchange_address - - assert_valid(jsdict, "/orderSchema") - - return jsdict - - -def jsdict_order_to_struct(jsdict: dict) -> Order: - r"""Convert a JSON-schema-compatible dict order to a Web3-compatible struct. - - More specifically, do explicit encoding of the `bytes`:code: fields. - - >>> import pprint - >>> pprint.pprint(jsdict_order_to_struct( - ... { - ... 'makerAddress': "0x0000000000000000000000000000000000000000", - ... 'takerAddress': "0x0000000000000000000000000000000000000000", - ... 'feeRecipientAddress': "0x0000000000000000000000000000000000000000", - ... 'senderAddress': "0x0000000000000000000000000000000000000000", - ... 'makerAssetAmount': 1000000000000000000, - ... 'takerAssetAmount': 1000000000000000000, - ... 'makerFee': 0, - ... 'takerFee': 0, - ... 'expirationTimeSeconds': 12345, - ... 'salt': 12345, - ... 'makerAssetData': "0x0000000000000000000000000000000000000000", - ... 'takerAssetData': "0x0000000000000000000000000000000000000000", - ... 'exchangeAddress': "0x0000000000000000000000000000000000000000", - ... }, - ... )) - {'expirationTimeSeconds': 12345, - 'feeRecipientAddress': '0x0000000000000000000000000000000000000000', - 'makerAddress': '0x0000000000000000000000000000000000000000', - 'makerAssetAmount': 1000000000000000000, - 'makerAssetData': b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00', - 'makerFee': 0, - 'salt': 12345, - 'senderAddress': '0x0000000000000000000000000000000000000000', - 'takerAddress': '0x0000000000000000000000000000000000000000', - 'takerAssetAmount': 1000000000000000000, - 'takerAssetData': b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00', - 'takerFee': 0} - """ # noqa: E501 (line too long) - assert_valid(jsdict, "/orderSchema") - - order = cast(Order, copy(jsdict)) - - order["makerAssetData"] = bytes.fromhex( - remove_0x_prefix(jsdict["makerAssetData"]) - ) - order["takerAssetData"] = bytes.fromhex( - remove_0x_prefix(jsdict["takerAssetData"]) - ) - - del order["exchangeAddress"] # type: ignore - # silence mypy pending release of - # https://github.com/python/mypy/issues/3550 - - return order - - -def generate_order_hash_hex(order: Order, exchange_address: str) -> str: - """Calculate the hash of the given order as a hexadecimal string. - - :param order: The order to be hashed. Must conform to `the 0x order JSON schema <https://github.com/0xProject/0x-monorepo/blob/development/packages/json-schemas/schemas/order_schema.json>`_. - :param exchange_address: The address to which the 0x Exchange smart - contract has been deployed. - :returns: A string, of ASCII hex digits, representing the order hash. - - >>> generate_order_hash_hex( - ... { - ... 'makerAddress': "0x0000000000000000000000000000000000000000", - ... 'takerAddress': "0x0000000000000000000000000000000000000000", - ... 'feeRecipientAddress': "0x0000000000000000000000000000000000000000", - ... 'senderAddress': "0x0000000000000000000000000000000000000000", - ... 'makerAssetAmount': "1000000000000000000", - ... 'takerAssetAmount': "1000000000000000000", - ... 'makerFee': "0", - ... 'takerFee': "0", - ... 'expirationTimeSeconds': "12345", - ... 'salt': "12345", - ... 'makerAssetData': (0).to_bytes(1, byteorder='big') * 20, - ... 'takerAssetData': (0).to_bytes(1, byteorder='big') * 20, - ... }, - ... exchange_address="0x0000000000000000000000000000000000000000", - ... ) - '55eaa6ec02f3224d30873577e9ddd069a288c16d6fb407210eecbc501fa76692' - """ # noqa: E501 (line too long) - assert_is_address(exchange_address, "exchange_address") - assert_valid(order_to_jsdict(order, exchange_address), "/orderSchema") - - def pad_20_bytes_to_32(twenty_bytes: bytes): - return bytes(12) + twenty_bytes - - def int_to_32_big_endian_bytes(i: int): - return i.to_bytes(32, byteorder="big") - - eip712_domain_struct_hash = keccak( - _Constants.eip712_domain_struct_header - + pad_20_bytes_to_32(to_bytes(hexstr=exchange_address)) - ) - - eip712_order_struct_hash = keccak( - _Constants.eip712_order_schema_hash - + pad_20_bytes_to_32(to_bytes(hexstr=order["makerAddress"])) - + pad_20_bytes_to_32(to_bytes(hexstr=order["takerAddress"])) - + pad_20_bytes_to_32(to_bytes(hexstr=order["feeRecipientAddress"])) - + pad_20_bytes_to_32(to_bytes(hexstr=order["senderAddress"])) - + int_to_32_big_endian_bytes(int(order["makerAssetAmount"])) - + int_to_32_big_endian_bytes(int(order["takerAssetAmount"])) - + int_to_32_big_endian_bytes(int(order["makerFee"])) - + int_to_32_big_endian_bytes(int(order["takerFee"])) - + int_to_32_big_endian_bytes(int(order["expirationTimeSeconds"])) - + int_to_32_big_endian_bytes(int(order["salt"])) - + keccak(to_bytes(hexstr=order["makerAssetData"].hex())) - + keccak(to_bytes(hexstr=order["takerAssetData"].hex())) - ) - - return keccak( - _Constants.eip191_header - + eip712_domain_struct_hash - + eip712_order_struct_hash - ).hex() - - -class OrderInfo(NamedTuple): - """A Web3-compatible representation of the Exchange.OrderInfo struct.""" - - order_status: str - """A `str`:code: describing the order's validity and fillability.""" - - order_hash: bytes - """A `bytes`:code: object representing the EIP712 hash of the order.""" - - order_taker_asset_filled_amount: int - """An `int`:code: indicating the amount that has already been filled.""" - - -def is_valid_signature( - provider: BaseProvider, data: str, signature: str, signer_address: str -) -> Tuple[bool, str]: - """Check the validity of the supplied signature. - - Check if the supplied `signature`:code: corresponds to signing `data`:code: - with the private key corresponding to `signer_address`:code:. - - :param provider: A Web3 provider able to access the 0x Exchange contract. - :param data: The hex encoded data signed by the supplied signature. - :param signature: The hex encoded signature. - :param signer_address: The hex encoded address that signed the data to - produce the supplied signature. - :returns: Tuple consisting of a boolean and a string. Boolean is true if - valid, false otherwise. If false, the string describes the reason. - - >>> is_valid_signature( - ... Web3.HTTPProvider("http://127.0.0.1:8545"), - ... '0x6927e990021d23b1eb7b8789f6a6feaf98fe104bb0cf8259421b79f9a34222b0', - ... '0x1B61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc3340349190569279751135161d22529dc25add4f6069af05be04cacbda2ace225403', - ... '0x5409ed021d9299bf6814279a6a1411a7e866a631', - ... ) - (True, '') - """ # noqa: E501 (line too long) - assert_is_provider(provider, "provider") - assert_is_hex_string(data, "data") - assert_is_hex_string(signature, "signature") - assert_is_address(signer_address, "signer_address") - - web3_instance = Web3(provider) - # false positive from pylint: disable=no-member - contract_address = NETWORK_TO_ADDRESSES[ - NetworkId(int(web3_instance.net.version)) - ].exchange - # false positive from pylint: disable=no-member - contract: datatypes.Contract = web3_instance.eth.contract( - address=to_checksum_address(contract_address), - abi=zero_ex.contract_artifacts.abi_by_name("Exchange"), - ) - try: - return ( - contract.call().isValidSignature( - data, to_checksum_address(signer_address), signature - ), - "", - ) - except web3.exceptions.BadFunctionCallOutput as exception: - known_revert_reasons = [ - "LENGTH_GREATER_THAN_0_REQUIRED", - "SIGNATURE_ILLEGAL", - "SIGNATURE_UNSUPPORTED", - "LENGTH_0_REQUIRED", - "LENGTH_65_REQUIRED", - ] - for known_revert_reason in known_revert_reasons: - if known_revert_reason in str(exception): - return (False, known_revert_reason) - return (False, f"Unknown: {exception}") - - -class ECSignature(TypedDict): - """Object representation of an elliptic curve signature's parameters.""" - - v: int - r: str - s: str - - -def _parse_signature_hex_as_vrs(signature_hex: str) -> ECSignature: - """Parse signature hex as a concatentation of EC parameters ordered V, R, S. - - >>> _parse_signature_hex_as_vrs('0x1b117902c86dfb95fe0d1badd983ee166ad259b27acb220174cbb4460d872871137feabdfe76e05924b484789f79af4ee7fa29ec006cedce1bbf369320d034e10b03') - {'v': 27, 'r': '117902c86dfb95fe0d1badd983ee166ad259b27acb220174cbb4460d87287113', 's': '7feabdfe76e05924b484789f79af4ee7fa29ec006cedce1bbf369320d034e10b'} - """ # noqa: E501 (line too long) - signature: ECSignature = { - "v": int(signature_hex[2:4], 16), - "r": signature_hex[4:68], - "s": signature_hex[68:132], - } - if signature["v"] == 0 or signature["v"] == 1: - signature["v"] = signature["v"] + 27 - return signature - - -def _parse_signature_hex_as_rsv(signature_hex: str) -> ECSignature: - """Parse signature hex as a concatentation of EC parameters ordered R, S, V. - - >>> _parse_signature_hex_as_rsv('0x117902c86dfb95fe0d1badd983ee166ad259b27acb220174cbb4460d872871137feabdfe76e05924b484789f79af4ee7fa29ec006cedce1bbf369320d034e10b00') - {'r': '117902c86dfb95fe0d1badd983ee166ad259b27acb220174cbb4460d87287113', 's': '7feabdfe76e05924b484789f79af4ee7fa29ec006cedce1bbf369320d034e10b', 'v': 27} - """ # noqa: E501 (line too long) - signature: ECSignature = { - "r": signature_hex[2:66], - "s": signature_hex[66:130], - "v": int(signature_hex[130:132], 16), - } - if signature["v"] == 0 or signature["v"] == 1: - signature["v"] = signature["v"] + 27 - return signature - - -def _convert_ec_signature_to_vrs_hex(signature: ECSignature) -> str: - """Convert elliptic curve signature object to hex hash string. - - >>> _convert_ec_signature_to_vrs_hex( - ... { - ... 'r': '117902c86dfb95fe0d1badd983ee166ad259b27acb220174cbb4460d87287113', - ... 's': '7feabdfe76e05924b484789f79af4ee7fa29ec006cedce1bbf369320d034e10b', - ... 'v': 27 - ... } - ... ) - '0x1b117902c86dfb95fe0d1badd983ee166ad259b27acb220174cbb4460d872871137feabdfe76e05924b484789f79af4ee7fa29ec006cedce1bbf369320d034e10b' - """ # noqa: E501 (line too long) - return ( - "0x" - + signature["v"].to_bytes(1, byteorder="big").hex() - + signature["r"] - + signature["s"] - ) - - -def sign_hash( - provider: BaseProvider, signer_address: str, hash_hex: str -) -> str: - """Sign a message with the given hash, and return the signature. - - :param provider: A Web3 provider. - :param signer_address: The address of the signing account. - :param hash_hex: A hex string representing the hash, like that returned - from `generate_order_hash_hex()`:code:. - :returns: A string, of ASCII hex digits, representing the signature. - - >>> provider = Web3.HTTPProvider("http://127.0.0.1:8545") - >>> sign_hash( - ... provider, - ... Web3(provider).personal.listAccounts[0], - ... '0x34decbedc118904df65f379a175bb39ca18209d6ce41d5ed549d54e6e0a95004', - ... ) - '0x1b117902c86dfb95fe0d1badd983ee166ad259b27acb220174cbb4460d872871137feabdfe76e05924b484789f79af4ee7fa29ec006cedce1bbf369320d034e10b03' - """ # noqa: E501 (line too long) - assert_is_provider(provider, "provider") - assert_is_address(signer_address, "signer_address") - assert_is_hex_string(hash_hex, "hash_hex") - - web3_instance = Web3(provider) - # false positive from pylint: disable=no-member - signature = web3_instance.eth.sign( # type: ignore - signer_address, hexstr=hash_hex.replace("0x", "") - ).hex() - - valid_v_param_values = [27, 28] - - # HACK: There is no consensus on whether the signatureHex string should be - # formatted as v + r + s OR r + s + v, and different clients (even - # different versions of the same client) return the signature params in - # different orders. In order to support all client implementations, we - # parse the signature in both ways, and evaluate if either one is a valid - # signature. r + s + v is the most prevalent format from eth_sign, so we - # attempt this first. - - ec_signature = _parse_signature_hex_as_rsv(signature) - if ec_signature["v"] in valid_v_param_values: - signature_as_vrst_hex = ( - _convert_ec_signature_to_vrs_hex(ec_signature) - + _Constants.SignatureType.ETH_SIGN.value.to_bytes( - 1, byteorder="big" - ).hex() - ) - - (valid, _) = is_valid_signature( - provider, hash_hex, signature_as_vrst_hex, signer_address - ) - - if valid is True: - return signature_as_vrst_hex - - ec_signature = _parse_signature_hex_as_vrs(signature) - if ec_signature["v"] in valid_v_param_values: - signature_as_vrst_hex = ( - _convert_ec_signature_to_vrs_hex(ec_signature) - + _Constants.SignatureType.ETH_SIGN.value.to_bytes( - 1, byteorder="big" - ).hex() - ) - (valid, _) = is_valid_signature( - provider, hash_hex, signature_as_vrst_hex, signer_address - ) - - if valid is True: - return signature_as_vrst_hex - - raise RuntimeError( - "Signature returned from web3 provider is in an unknown format." - + " Attempted to parse as RSV and as VRS." - ) diff --git a/python-packages/order_utils/src/zero_ex/order_utils/asset_data_utils.py b/python-packages/order_utils/src/zero_ex/order_utils/asset_data_utils.py deleted file mode 100644 index f8e9bc42b..000000000 --- a/python-packages/order_utils/src/zero_ex/order_utils/asset_data_utils.py +++ /dev/null @@ -1,141 +0,0 @@ -"""Asset data encoding and decoding utilities.""" - -from mypy_extensions import TypedDict - -import eth_abi - -from zero_ex.dev_utils import abi_utils -from zero_ex.dev_utils.type_assertions import assert_is_string, assert_is_int - - -ERC20_ASSET_DATA_BYTE_LENGTH = 36 -ERC721_ASSET_DATA_MINIMUM_BYTE_LENGTH = 53 -SELECTOR_LENGTH = 10 - - -class ERC20AssetData(TypedDict): - """Object interface to ERC20 asset data.""" - - asset_proxy_id: str - """asset proxy id""" - - token_address: str - - -class ERC721AssetData(TypedDict): - """Object interface to ERC721 asset data.""" - - asset_proxy_id: str - token_address: str - token_id: int - - -def encode_erc20_asset_data(token_address: str) -> str: - """Encode an ERC20 token address into an asset data string. - - :param token_address: the ERC20 token's contract address. - :returns: hex encoded asset data string, usable in the makerAssetData or - takerAssetData fields in a 0x order. - - >>> encode_erc20_asset_data('0x1dc4c1cefef38a777b15aa20260a54e584b16c48') - '0xf47261b00000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c48' - """ - assert_is_string(token_address, "token_address") - - return ( - "0x" - + abi_utils.simple_encode("ERC20Token(address)", token_address).hex() - ) - - -def decode_erc20_asset_data(asset_data: str) -> ERC20AssetData: - """Decode an ERC20 asset data hex string. - - :param asset_data: String produced by prior call to encode_erc20_asset_data() - - >>> decode_erc20_asset_data("0xf47261b00000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c48") - {'asset_proxy_id': '0xf47261b0', 'token_address': '0x1dc4c1cefef38a777b15aa20260a54e584b16c48'} - """ # noqa: E501 (line too long) - assert_is_string(asset_data, "asset_data") - - if len(asset_data) < ERC20_ASSET_DATA_BYTE_LENGTH: - raise ValueError( - "Could not decode ERC20 Proxy Data. Expected length of encoded" - + f" data to be at least {str(ERC20_ASSET_DATA_BYTE_LENGTH)}." - + f" Got {str(len(asset_data))}." - ) - - asset_proxy_id: str = asset_data[0:SELECTOR_LENGTH] - if asset_proxy_id != abi_utils.method_id("ERC20Token", ["address"]): - raise ValueError( - "Could not decode ERC20 Proxy Data. Expected Asset Proxy Id to be" - + f" ERC20 ({abi_utils.method_id('ERC20Token', ['address'])})" - + f" but got {asset_proxy_id}." - ) - - # workaround for https://github.com/PyCQA/pylint/issues/1498 - # pylint: disable=unsubscriptable-object - token_address = eth_abi.decode_abi( - ["address"], bytes.fromhex(asset_data[SELECTOR_LENGTH:]) - )[0] - - return {"asset_proxy_id": asset_proxy_id, "token_address": token_address} - - -def encode_erc721_asset_data(token_address: str, token_id: int) -> str: - """Encode an ERC721 asset data hex string. - - :param token_address: the ERC721 token's contract address. - :param token_id: the identifier of the asset's instance of the token. - :returns: hex encoded asset data string, usable in the makerAssetData or - takerAssetData fields in a 0x order. - - >>> encode_erc721_asset_data('0x1dc4c1cefef38a777b15aa20260a54e584b16c48', 1) - '0x025717920000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c480000000000000000000000000000000000000000000000000000000000000001' - """ # noqa: E501 (line too long) - assert_is_string(token_address, "token_address") - assert_is_int(token_id, "token_id") - - return ( - "0x" - + abi_utils.simple_encode( - "ERC721Token(address,uint256)", token_address, token_id - ).hex() - ) - - -def decode_erc721_asset_data(asset_data: str) -> ERC721AssetData: - """Decode an ERC721 asset data hex string. - - >>> decode_erc721_asset_data('0x025717920000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c480000000000000000000000000000000000000000000000000000000000000001') - {'asset_proxy_id': '0x02571792', 'token_address': '0x1dc4c1cefef38a777b15aa20260a54e584b16c48', 'token_id': 1} - """ # noqa: E501 (line too long) - assert_is_string(asset_data, "asset_data") - - if len(asset_data) < ERC721_ASSET_DATA_MINIMUM_BYTE_LENGTH: - raise ValueError( - "Could not decode ERC721 Asset Data. Expected length of encoded" - + f"data to be at least {ERC721_ASSET_DATA_MINIMUM_BYTE_LENGTH}. " - + f"Got {len(asset_data)}." - ) - - asset_proxy_id: str = asset_data[0:SELECTOR_LENGTH] - if asset_proxy_id != abi_utils.method_id( - "ERC721Token", ["address", "uint256"] - ): - raise ValueError( - "Could not decode ERC721 Asset Data. Expected Asset Proxy Id to be" - + f" ERC721 (" - + f"{abi_utils.method_id('ERC721Token', ['address', 'uint256'])}" - + f"), but got {asset_proxy_id}" - ) - - (token_address, token_id) = eth_abi.decode_abi( - ["address", "uint256"], bytes.fromhex(asset_data[SELECTOR_LENGTH:]) - ) - - return { - "asset_proxy_id": asset_proxy_id, - "token_address": token_address, - "token_id": token_id, - } diff --git a/python-packages/order_utils/src/zero_ex/order_utils/py.typed b/python-packages/order_utils/src/zero_ex/order_utils/py.typed deleted file mode 100644 index e69de29bb..000000000 --- a/python-packages/order_utils/src/zero_ex/order_utils/py.typed +++ /dev/null |