diff options
author | Brandon Millman <brandon.millman@gmail.com> | 2018-10-29 09:36:13 +0800 |
---|---|---|
committer | Brandon Millman <brandon.millman@gmail.com> | 2018-10-29 10:07:53 +0800 |
commit | 8d1689073b702d973075d30b2bb36369487fad1c (patch) | |
tree | cb0380d4782b0715f0d24e7a9c5f88b49a60fa50 /python-packages | |
parent | 4f5ab1a72d33dc6a7516d7b1d51f1aa15752a6b8 (diff) | |
parent | ae6202ed3d777605a3fd02cd29141a3ba40f4b34 (diff) | |
download | dexon-sol-tools-8d1689073b702d973075d30b2bb36369487fad1c.tar dexon-sol-tools-8d1689073b702d973075d30b2bb36369487fad1c.tar.gz dexon-sol-tools-8d1689073b702d973075d30b2bb36369487fad1c.tar.bz2 dexon-sol-tools-8d1689073b702d973075d30b2bb36369487fad1c.tar.lz dexon-sol-tools-8d1689073b702d973075d30b2bb36369487fad1c.tar.xz dexon-sol-tools-8d1689073b702d973075d30b2bb36369487fad1c.tar.zst dexon-sol-tools-8d1689073b702d973075d30b2bb36369487fad1c.zip |
Merge branch 'development' into feature/instant/fixed-orders-in-render-method
* development:
fix(instant): refactor some props to use isDisabled instead of disabled
linting imports
feat(instant): Disable input when processing
Add back debounce
Make doesBuyQuoteMatchState in reducer less strict
fix(instant): prevent outdated quote requests from overriding the correct quote
selected asset buy order state button -> selected asset buy order state buttons
buy order state button -> buy order state buttons
feat(order_utils.py): ERC721 asset data codec (#1186)
Add note about tslint false positive
tsx -> ts
Get BuyOrderState one big connected component, and let user view failure
Show View Transaction button on failure, and allow setting of width for Try Again button and View Txn button
Added string to constants
chore: Update contract-wrappers CHANGELOG.json
fix(contract-wrappers): Fix tslint errors that were lingering due to misconfiguration
chore: Add --format stylish to tslint
Diffstat (limited to 'python-packages')
5 files changed, 131 insertions, 6 deletions
diff --git a/python-packages/order_utils/setup.py b/python-packages/order_utils/setup.py index 159499ec4..22a5f4c41 100755 --- a/python-packages/order_utils/setup.py +++ b/python-packages/order_utils/setup.py @@ -20,7 +20,7 @@ class TestCommandExtension(TestCommand): """Invoke pytest.""" import pytest - pytest.main() + exit(pytest.main()) # pylint: disable=too-many-ancestors diff --git a/python-packages/order_utils/src/index.rst b/python-packages/order_utils/src/index.rst index 9c26b7924..22d5b0ef9 100644 --- a/python-packages/order_utils/src/index.rst +++ b/python-packages/order_utils/src/index.rst @@ -15,7 +15,9 @@ Python zero_ex.order_utils .. autoclass:: zero_ex.order_utils.asset_data_utils.ERC20AssetData -See source for properties. Sphinx does not easily generate class property docs; pull requests welcome. +.. autoclass:: zero_ex.order_utils.asset_data_utils.ERC721AssetData + +See source for class properties. Sphinx does not easily generate class property docs; pull requests welcome. Indices and tables ================== 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 index 745d014e6..a100da567 100644 --- 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 @@ -31,3 +31,18 @@ def assert_is_list(value: Any, name: str) -> None: 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__}'" + ) 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 index 451de39af..e6f9a07c1 100644 --- 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 @@ -5,10 +5,11 @@ 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 +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 @@ -19,6 +20,14 @@ class ERC20AssetData(TypedDict): 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. @@ -39,7 +48,7 @@ def encode_erc20_asset_data(token_address: str) -> str: 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. + """Decode an ERC20 asset data hex string. :param asset_data: String produced by prior call to encode_erc20_asset_data() @@ -55,7 +64,7 @@ def decode_erc20_asset_data(asset_data: str) -> ERC20AssetData: + f" Got {str(len(asset_data))}." ) - asset_proxy_id: str = asset_data[0:10] + 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" @@ -70,3 +79,65 @@ def decode_erc20_asset_data(asset_data: str) -> ERC20AssetData: )[0] return {"asset_proxy_id": asset_proxy_id, "token_address": token_address} + + +def encode_erc721_asset_data(token_address: str, token_id: int) -> str: + # docstring considered all one line by pylint: disable=line-too-long + """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. + :rtype: 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: + # docstring considered all one line by pylint: disable=line-too-long + """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] + # prefer `black` formatting. pylint: disable=C0330 + 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/test/test_asset_data_utils.py b/python-packages/order_utils/test/test_asset_data_utils.py index eeada5873..079368714 100644 --- a/python-packages/order_utils/test/test_asset_data_utils.py +++ b/python-packages/order_utils/test/test_asset_data_utils.py @@ -3,9 +3,12 @@ import pytest from zero_ex.order_utils.asset_data_utils import ( - encode_erc20_asset_data, decode_erc20_asset_data, + decode_erc721_asset_data, + encode_erc20_asset_data, + encode_erc721_asset_data, ERC20_ASSET_DATA_BYTE_LENGTH, + ERC721_ASSET_DATA_MINIMUM_BYTE_LENGTH, ) @@ -33,3 +36,37 @@ def test_decode_erc20_asset_data_invalid_proxy_id(): decode_erc20_asset_data( "0xffffffff" + (" " * ERC20_ASSET_DATA_BYTE_LENGTH) ) + + +def test_encode_erc721_asset_data_type_error_on_token_address(): + """Test that passing a non-string for token_address raises a TypeError.""" + with pytest.raises(TypeError): + encode_erc721_asset_data(123, 123) + + +def test_encode_erc721_asset_data_type_error_on_token_id(): + """Test that passing a non-int for token_id raises a TypeError.""" + with pytest.raises(TypeError): + encode_erc721_asset_data("asdf", "asdf") + + +def test_decode_erc721_asset_data_type_error(): + """Test that passing a non-string for asset_data raises a TypeError.""" + with pytest.raises(TypeError): + decode_erc721_asset_data(123) + + +def test_decode_erc721_asset_data_with_asset_data_too_short(): + """Test that passing in too short of a string raises a ValueError.""" + with pytest.raises(ValueError): + decode_erc721_asset_data( + " " * (ERC721_ASSET_DATA_MINIMUM_BYTE_LENGTH - 1) + ) + + +def test_decode_erc721_asset_data_invalid_proxy_id(): + """Test that passing in too short of a string raises a ValueError.""" + with pytest.raises(ValueError): + decode_erc721_asset_data( + "0xffffffff" + " " * (ERC721_ASSET_DATA_MINIMUM_BYTE_LENGTH - 1) + ) |