feat(order_utils.py): ERC20 asset data encoding and decoding
In addition to the ERC20 codec, also: Stopped ignoring type errors on 3rd party imports, by including interface stubs for them; Removed the unimplemented signature-utils module, which was just a stand-in when the python project support was first put in place. https://github.com/0xProject/0x-monorepo/pull/1144
This commit is contained in:
parent
1ba207f1fe
commit
1f0c7f8fbe
@ -26,3 +26,4 @@ packages/subproviders/ @fabioberger @dekz
|
|||||||
packages/connect/ @fragosti
|
packages/connect/ @fragosti
|
||||||
packages/monorepo-scripts/ @fabioberger
|
packages/monorepo-scripts/ @fabioberger
|
||||||
packages/order-utils/ @fabioberger @LogvinovLeon
|
packages/order-utils/ @fabioberger @LogvinovLeon
|
||||||
|
python-packages/ @feuGeneA
|
31
packages/order-utils/test/asset_data_utils_test.ts
Normal file
31
packages/order-utils/test/asset_data_utils_test.ts
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import * as chai from 'chai';
|
||||||
|
|
||||||
|
import { ERC20AssetData } from '@0x/types';
|
||||||
|
|
||||||
|
import { assetDataUtils } from '../src/asset_data_utils';
|
||||||
|
|
||||||
|
import { chaiSetup } from './utils/chai_setup';
|
||||||
|
|
||||||
|
chaiSetup.configure();
|
||||||
|
const expect = chai.expect;
|
||||||
|
|
||||||
|
const KNOWN_ENCODINGS = [
|
||||||
|
{
|
||||||
|
address: '0x1dc4c1cefef38a777b15aa20260a54e584b16c48',
|
||||||
|
assetData: '0xf47261b00000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c48',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const ERC20_ASSET_PROXY_ID = '0xf47261b0';
|
||||||
|
|
||||||
|
describe('assetDataUtils', () => {
|
||||||
|
it('should encode', () => {
|
||||||
|
const assetData = assetDataUtils.encodeERC20AssetData(KNOWN_ENCODINGS[0].address);
|
||||||
|
expect(assetData).to.equal(KNOWN_ENCODINGS[0].assetData);
|
||||||
|
});
|
||||||
|
it('should decode', () => {
|
||||||
|
const assetData: ERC20AssetData = assetDataUtils.decodeERC20AssetData(KNOWN_ENCODINGS[0].assetData);
|
||||||
|
expect(assetData.tokenAddress).to.equal(KNOWN_ENCODINGS[0].address);
|
||||||
|
expect(assetData.assetProxyId).to.equal(ERC20_ASSET_PROXY_ID);
|
||||||
|
});
|
||||||
|
});
|
39
python-packages/order_utils/setup.py
Normal file → Executable file
39
python-packages/order_utils/setup.py
Normal file → Executable file
@ -1,13 +1,16 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
"""setuptools module for order_utils package."""
|
"""setuptools module for order_utils package."""
|
||||||
|
|
||||||
import subprocess # nosec
|
import subprocess # nosec
|
||||||
from shutil import rmtree
|
from shutil import rmtree
|
||||||
from os import path, remove, walk
|
from os import environ, path, remove, walk
|
||||||
|
from sys import argv
|
||||||
|
|
||||||
from distutils.command.clean import clean # type: ignore
|
from distutils.command.clean import clean
|
||||||
from setuptools import setup # type: ignore
|
import distutils.command.build_py
|
||||||
import setuptools.command.build_py # type: ignore
|
from setuptools import setup
|
||||||
from setuptools.command.test import test as TestCommand # type: ignore
|
from setuptools.command.test import test as TestCommand
|
||||||
|
|
||||||
|
|
||||||
class TestCommandExtension(TestCommand):
|
class TestCommandExtension(TestCommand):
|
||||||
@ -15,13 +18,13 @@ class TestCommandExtension(TestCommand):
|
|||||||
|
|
||||||
def run_tests(self):
|
def run_tests(self):
|
||||||
"""Invoke pytest."""
|
"""Invoke pytest."""
|
||||||
import pytest # type: ignore
|
import pytest
|
||||||
|
|
||||||
pytest.main()
|
pytest.main()
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=too-many-ancestors
|
# pylint: disable=too-many-ancestors
|
||||||
class LintCommand(setuptools.command.build_py.build_py):
|
class LintCommand(distutils.command.build_py.build_py):
|
||||||
"""Custom setuptools command class for running linters."""
|
"""Custom setuptools command class for running linters."""
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
@ -34,7 +37,7 @@ class LintCommand(setuptools.command.build_py.build_py):
|
|||||||
# docstring style checker:
|
# docstring style checker:
|
||||||
"pydocstyle src test setup.py".split(),
|
"pydocstyle src test setup.py".split(),
|
||||||
# static type checker:
|
# static type checker:
|
||||||
"mypy src setup.py".split(),
|
"mypy src test setup.py".split(),
|
||||||
# security issue checker:
|
# security issue checker:
|
||||||
"bandit -r src ./setup.py".split(),
|
"bandit -r src ./setup.py".split(),
|
||||||
# general linter:
|
# general linter:
|
||||||
@ -42,6 +45,21 @@ class LintCommand(setuptools.command.build_py.build_py):
|
|||||||
# pylint takes relatively long to run, so it runs last, to enable
|
# pylint takes relatively long to run, so it runs last, to enable
|
||||||
# fast failures.
|
# fast failures.
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# tell mypy where to find interface stubs for 3rd party libs
|
||||||
|
environ["MYPYPATH"] = path.join(
|
||||||
|
path.dirname(path.realpath(argv[0])), "stubs"
|
||||||
|
)
|
||||||
|
|
||||||
|
# HACK(gene): until eth_abi releases
|
||||||
|
# https://github.com/ethereum/eth-abi/pull/107 , we need to simply
|
||||||
|
# create an empty file `py.typed` in the eth_abi package directory.
|
||||||
|
import eth_abi
|
||||||
|
|
||||||
|
eth_abi_dir = path.dirname(path.realpath(eth_abi.__file__))
|
||||||
|
with open(path.join(eth_abi_dir, "py.typed"), "a"):
|
||||||
|
pass
|
||||||
|
|
||||||
for lint_command in lint_commands:
|
for lint_command in lint_commands:
|
||||||
print(
|
print(
|
||||||
"Running lint command `", " ".join(lint_command).strip(), "`"
|
"Running lint command `", " ".join(lint_command).strip(), "`"
|
||||||
@ -79,7 +97,7 @@ setup(
|
|||||||
"test": TestCommandExtension,
|
"test": TestCommandExtension,
|
||||||
},
|
},
|
||||||
include_package_data=True,
|
include_package_data=True,
|
||||||
install_requires=["web3"],
|
install_requires=["eth-abi", "web3"],
|
||||||
extras_require={
|
extras_require={
|
||||||
"dev": [
|
"dev": [
|
||||||
"bandit",
|
"bandit",
|
||||||
@ -87,6 +105,7 @@ setup(
|
|||||||
"coverage",
|
"coverage",
|
||||||
"coveralls",
|
"coveralls",
|
||||||
"mypy",
|
"mypy",
|
||||||
|
"mypy_extensions",
|
||||||
"pycodestyle",
|
"pycodestyle",
|
||||||
"pydocstyle",
|
"pydocstyle",
|
||||||
"pylint",
|
"pylint",
|
||||||
@ -118,7 +137,7 @@ setup(
|
|||||||
"Topic :: Software Development :: Libraries",
|
"Topic :: Software Development :: Libraries",
|
||||||
"Topic :: Utilities",
|
"Topic :: Utilities",
|
||||||
],
|
],
|
||||||
zip_safe=False,
|
zip_safe=False, # required per mypy
|
||||||
command_options={
|
command_options={
|
||||||
"build_sphinx": {
|
"build_sphinx": {
|
||||||
"source_dir": ("setup.py", "src"),
|
"source_dir": ("setup.py", "src"),
|
||||||
|
@ -2,6 +2,9 @@
|
|||||||
|
|
||||||
# Reference: http://www.sphinx-doc.org/en/master/config
|
# Reference: http://www.sphinx-doc.org/en/master/config
|
||||||
|
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=invalid-name
|
# pylint: disable=invalid-name
|
||||||
# because these variables are not named in upper case, as globals should be.
|
# because these variables are not named in upper case, as globals should be.
|
||||||
|
|
||||||
@ -29,7 +32,7 @@ master_doc = "index" # The master toctree document.
|
|||||||
|
|
||||||
language = None
|
language = None
|
||||||
|
|
||||||
exclude_patterns = [] # type: ignore
|
exclude_patterns: List[str] = []
|
||||||
|
|
||||||
# The name of the Pygments (syntax highlighting) style to use.
|
# The name of the Pygments (syntax highlighting) style to use.
|
||||||
pygments_style = None
|
pygments_style = None
|
||||||
|
@ -10,9 +10,12 @@ order_utils.py
|
|||||||
.. automodule:: zero_ex.order_utils
|
.. automodule:: zero_ex.order_utils
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
.. automodule:: zero_ex.order_utils.signature_utils
|
.. automodule:: zero_ex.order_utils.asset_data_utils
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: zero_ex.order_utils.asset_data_utils.ERC20AssetData
|
||||||
|
|
||||||
|
See source for properties. Sphinx does not easily generate class property docs; pull requests welcome.
|
||||||
|
|
||||||
Indices and tables
|
Indices and tables
|
||||||
==================
|
==================
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
"""Dev utils to be shared across 0x projects and packages."""
|
102
python-packages/order_utils/src/zero_ex/dev_utils/abi_utils.py
Normal file
102
python-packages/order_utils/src/zero_ex/dev_utils/abi_utils.py
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
"""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 eth_abi import encode_abi
|
||||||
|
from web3 import Web3
|
||||||
|
|
||||||
|
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:
|
||||||
|
# docstring considered all one line by pylint: disable=line-too-long
|
||||||
|
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:]
|
||||||
|
)
|
@ -0,0 +1,33 @@
|
|||||||
|
"""Assertions for runtime type checking of function arguments."""
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
|
||||||
|
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__}'"
|
||||||
|
)
|
@ -0,0 +1,72 @@
|
|||||||
|
"""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
|
||||||
|
|
||||||
|
|
||||||
|
ERC20_ASSET_DATA_BYTE_LENGTH = 36
|
||||||
|
SELECTOR_LENGTH = 10
|
||||||
|
|
||||||
|
|
||||||
|
class ERC20AssetData(TypedDict):
|
||||||
|
"""Object interface to ERC20 asset data."""
|
||||||
|
|
||||||
|
asset_proxy_id: str
|
||||||
|
token_address: str
|
||||||
|
|
||||||
|
|
||||||
|
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.
|
||||||
|
:rtype: 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:
|
||||||
|
# docstring considered all one line by pylint: disable=line-too-long
|
||||||
|
"""Decode an ERC20 assetData 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:10]
|
||||||
|
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}
|
@ -1,13 +0,0 @@
|
|||||||
"""Signature utilities."""
|
|
||||||
|
|
||||||
|
|
||||||
def ec_sign_order_hash():
|
|
||||||
"""Signs an orderHash.
|
|
||||||
|
|
||||||
Returns its elliptic curve signature and signature type. This method
|
|
||||||
currently supports TestRPC, Geth, and Parity above and below v1.6.6.
|
|
||||||
|
|
||||||
>>> ec_sign_order_hash()
|
|
||||||
'stub return value'
|
|
||||||
"""
|
|
||||||
return "stub return value"
|
|
@ -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: ...
|
||||||
|
...
|
1
python-packages/order_utils/stubs/pytest/raises.pyi
Normal file
1
python-packages/order_utils/stubs/pytest/raises.pyi
Normal file
@ -0,0 +1 @@
|
|||||||
|
def raises(exception: Exception) -> ExceptionInfo: ...
|
@ -0,0 +1,6 @@
|
|||||||
|
from distutils.dist import Distribution
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
def setup(**attrs: Any) -> Distribution: ...
|
||||||
|
|
||||||
|
class Command: ...
|
@ -0,0 +1,3 @@
|
|||||||
|
from setuptools import Command
|
||||||
|
|
||||||
|
class test(Command): ...
|
10
python-packages/order_utils/stubs/web3/__init__.pyi
Normal file
10
python-packages/order_utils/stubs/web3/__init__.pyi
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
from typing import Optional, Union
|
||||||
|
|
||||||
|
class Web3:
|
||||||
|
@staticmethod
|
||||||
|
def sha3(
|
||||||
|
primitive: Optional[Union[bytes, int, None]] = None,
|
||||||
|
text: Optional[str] = None,
|
||||||
|
hexstr: Optional[str] = None
|
||||||
|
) -> bytes: ...
|
||||||
|
...
|
53
python-packages/order_utils/test/test_abi_utils.py
Normal file
53
python-packages/order_utils/test/test_abi_utils.py
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
"""Tests of 0x.abi_utils."""
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from zero_ex.dev_utils.abi_utils import (
|
||||||
|
elementary_name,
|
||||||
|
event_id,
|
||||||
|
method_id,
|
||||||
|
parse_signature,
|
||||||
|
simple_encode,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_signature_type_error():
|
||||||
|
"""Test that passing in wrong types raises TypeError."""
|
||||||
|
with pytest.raises(TypeError):
|
||||||
|
parse_signature(123)
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_signature_bad_input():
|
||||||
|
"""Test that passing a non-signature string raises a ValueError."""
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
parse_signature("a string that's not even close to a signature")
|
||||||
|
|
||||||
|
|
||||||
|
def test_elementary_name_type_error():
|
||||||
|
"""Test that passing in wrong types raises TypeError."""
|
||||||
|
with pytest.raises(TypeError):
|
||||||
|
elementary_name(123)
|
||||||
|
|
||||||
|
|
||||||
|
def test_event_id_type_error():
|
||||||
|
"""Test that passing in wrong types raises TypeError."""
|
||||||
|
with pytest.raises(TypeError):
|
||||||
|
event_id(123, [])
|
||||||
|
|
||||||
|
with pytest.raises(TypeError):
|
||||||
|
event_id("valid string", 123)
|
||||||
|
|
||||||
|
|
||||||
|
def test_method_id_type_error():
|
||||||
|
"""Test that passing in wrong types raises TypeError."""
|
||||||
|
with pytest.raises(TypeError):
|
||||||
|
method_id(123, [])
|
||||||
|
|
||||||
|
with pytest.raises(TypeError):
|
||||||
|
method_id("ERC20Token", 123)
|
||||||
|
|
||||||
|
|
||||||
|
def test_simple_encode_type_error():
|
||||||
|
"""Test that passing in wrong types raises TypeError."""
|
||||||
|
with pytest.raises(TypeError):
|
||||||
|
simple_encode(123)
|
35
python-packages/order_utils/test/test_asset_data_utils.py
Normal file
35
python-packages/order_utils/test/test_asset_data_utils.py
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
"""Tests of 0x.order_utils.asset_data_utils."""
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from zero_ex.order_utils.asset_data_utils import (
|
||||||
|
encode_erc20_asset_data,
|
||||||
|
decode_erc20_asset_data,
|
||||||
|
ERC20_ASSET_DATA_BYTE_LENGTH,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_encode_erc20_asset_data_type_error():
|
||||||
|
"""Test that passing in a non-string raises a TypeError."""
|
||||||
|
with pytest.raises(TypeError):
|
||||||
|
encode_erc20_asset_data(123)
|
||||||
|
|
||||||
|
|
||||||
|
def test_decode_erc20_asset_data_type_error():
|
||||||
|
"""Test that passing in a non-string raises a TypeError."""
|
||||||
|
with pytest.raises(TypeError):
|
||||||
|
decode_erc20_asset_data(123)
|
||||||
|
|
||||||
|
|
||||||
|
def test_decode_erc20_asset_data_too_short():
|
||||||
|
"""Test that passing an insufficiently long string raises a ValueError."""
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
decode_erc20_asset_data(" " * (ERC20_ASSET_DATA_BYTE_LENGTH - 1))
|
||||||
|
|
||||||
|
|
||||||
|
def test_decode_erc20_asset_data_invalid_proxy_id():
|
||||||
|
"""Test that passing data with an invalid proxy ID raises a ValueError."""
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
decode_erc20_asset_data(
|
||||||
|
"0xffffffff" + (" " * ERC20_ASSET_DATA_BYTE_LENGTH)
|
||||||
|
)
|
@ -1,10 +1,24 @@
|
|||||||
"""Exercise doctests for order_utils module."""
|
"""Exercise doctests for order_utils module."""
|
||||||
|
|
||||||
from doctest import testmod
|
from doctest import testmod
|
||||||
from zero_ex.order_utils import signature_utils
|
|
||||||
|
from zero_ex.dev_utils import abi_utils, type_assertions
|
||||||
|
from zero_ex.order_utils import asset_data_utils
|
||||||
|
|
||||||
|
|
||||||
def test_doctest():
|
def test_doctest_asset_data_utils():
|
||||||
"""Invoke doctest on the module."""
|
"""Invoke doctest on the asset_data_utils module."""
|
||||||
(failure_count, _) = testmod(signature_utils)
|
(failure_count, _) = testmod(asset_data_utils)
|
||||||
|
assert failure_count == 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_doctest_abi_utils():
|
||||||
|
"""Invoke doctest on the abi_utils module."""
|
||||||
|
(failure_count, _) = testmod(abi_utils)
|
||||||
|
assert failure_count == 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_doctest_type_assertions():
|
||||||
|
"""Invoke doctest on the type_assertions module."""
|
||||||
|
(failure_count, _) = testmod(type_assertions)
|
||||||
assert failure_count == 0
|
assert failure_count == 0
|
||||||
|
@ -1,8 +0,0 @@
|
|||||||
"""Tests of 0x.order_utils.signature_utils.*."""
|
|
||||||
|
|
||||||
from zero_ex.order_utils.signature_utils import ec_sign_order_hash
|
|
||||||
|
|
||||||
|
|
||||||
def test_ec_sign_order_hash():
|
|
||||||
"""Test the signing of order hashes."""
|
|
||||||
assert ec_sign_order_hash() == "stub return value"
|
|
Loading…
x
Reference in New Issue
Block a user