diff --git a/.circleci/config.yml b/.circleci/config.yml index c470f2e8fa..ff299feecc 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -195,9 +195,9 @@ jobs: - image: nikolaik/python-nodejs:python3.7-nodejs8 - image: 0xorg/ganache-cli:4.4.0-beta.1 environment: - VERSION: 4.4.0-beta.1 + VERSION: latest SNAPSHOT_NAME: 0x_ganache_snapshot-v3-beta - - image: 0xorg/mesh:6.0.0-beta-0xv3 + - image: 0xorg/mesh:0xV3 environment: ETHEREUM_RPC_URL: 'http://localhost:8545' ETHEREUM_NETWORK_ID: '50' diff --git a/.gitignore b/.gitignore index ec76ea0659..07f4af0350 100644 --- a/.gitignore +++ b/.gitignore @@ -171,6 +171,7 @@ python-packages/contract_wrappers/src/zero_ex/contract_wrappers/dummy_erc721_tok python-packages/contract_wrappers/src/zero_ex/contract_wrappers/dutch_auction/__init__.py python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc1155_mintable/__init__.py python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc1155_proxy/__init__.py +python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc20_bridge_proxy/__init__.py python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc20_proxy/__init__.py python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc721_proxy/__init__.py python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc721_token/__init__.py @@ -185,6 +186,7 @@ python-packages/contract_wrappers/src/zero_ex/contract_wrappers/staking_proxy/__ python-packages/contract_wrappers/src/zero_ex/contract_wrappers/static_call_proxy/__init__.py python-packages/contract_wrappers/src/zero_ex/contract_wrappers/weth9/__init__.py python-packages/contract_wrappers/src/zero_ex/contract_wrappers/zrx_token/__init__.py +python-packages/contract_wrappers/src/zero_ex/contract_wrappers/zrx_vault/__init__.py # solc-bin in sol-compiler packages/sol-compiler/solc_bin/ diff --git a/python-packages/contract_addresses/setup.py b/python-packages/contract_addresses/setup.py index 0d4dee62c3..7081f7c5f8 100755 --- a/python-packages/contract_addresses/setup.py +++ b/python-packages/contract_addresses/setup.py @@ -151,7 +151,7 @@ def run_tests(self): setup( name="0x-contract-addresses", - version="2.2.0", + version="3.0.0.dev3", description="Addresses at which the 0x smart contracts have been deployed", long_description=README_MD, long_description_content_type="text/markdown", diff --git a/python-packages/contract_addresses/tox.ini b/python-packages/contract_addresses/tox.ini index 944049af60..0158737ace 100644 --- a/python-packages/contract_addresses/tox.ini +++ b/python-packages/contract_addresses/tox.ini @@ -14,5 +14,5 @@ commands = [testenv:run_tests_against_deployment] setenv = PY_IGNORE_IMPORTMISMATCH = 1 commands= - pip install 0x-contract-addresses[dev] + pip install --pre 0x-contract-addresses[dev] pytest --doctest-modules src diff --git a/python-packages/contract_artifacts/setup.py b/python-packages/contract_artifacts/setup.py index d4ea9b5577..74a2f8cf0d 100755 --- a/python-packages/contract_artifacts/setup.py +++ b/python-packages/contract_artifacts/setup.py @@ -152,7 +152,7 @@ def run_tests(self): setup( name="0x-contract-artifacts", - version="3.0.0", + version="3.0.0.dev2", description="0x smart contract compilation artifacts", long_description=README_MD, long_description_content_type="text/markdown", diff --git a/python-packages/contract_artifacts/tox.ini b/python-packages/contract_artifacts/tox.ini index 4fd5f65639..2f91203288 100644 --- a/python-packages/contract_artifacts/tox.ini +++ b/python-packages/contract_artifacts/tox.ini @@ -14,5 +14,5 @@ commands = [testenv:run_tests_against_deployment] setenv = PY_IGNORE_IMPORTMISMATCH = 1 commands= - pip install 0x-contract-artifacts[dev] + pip install --pre 0x-contract-artifacts[dev] pytest --doctest-modules src diff --git a/python-packages/contract_wrappers/setup.py b/python-packages/contract_wrappers/setup.py index 6442036d1b..1f3cdc27b2 100755 --- a/python-packages/contract_wrappers/setup.py +++ b/python-packages/contract_wrappers/setup.py @@ -195,7 +195,7 @@ def run(self): setup( name="0x-contract-wrappers", - version="2.0.0", + version="2.0.0.dev10", description="Python wrappers for 0x smart contracts", long_description=README_MD, long_description_content_type="text/markdown", @@ -216,10 +216,10 @@ def run(self): "ganache": GanacheCommand, }, install_requires=[ - "0x-contract-addresses", - "0x-contract-artifacts", - "0x-json-schemas", - "0x-order-utils", + "0x-contract-addresses==3.0.0.dev3", + "0x-contract-artifacts==3.0.0.dev2", + "0x-json-schemas==2.1.0.dev2", + "0x-order-utils==4.0.0.dev8", "web3", "attrs", "eth_utils", diff --git a/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/__init__.py b/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/__init__.py index 23e9f591ee..40690e78b3 100644 --- a/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/__init__.py +++ b/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/__init__.py @@ -178,6 +178,14 @@ case WETH) that the taker wants to fill. This example fills the order completely, but partial fills are possible too. +Note that sending value in the fill transaction is a way to pay the protocol +fee. The value to send is a function of the gas price, so we'll need some help +in that determination: + +>>> from web3.gas_strategies.rpc import rpc_gas_price_strategy +>>> web3 = Web3(ganache) +>>> web3.eth.setGasPriceStrategy(rpc_gas_price_strategy) + One may wish to first call the method in a read-only way, to ensure that it will not revert, and to validate that the return data is as expected: @@ -186,11 +194,13 @@ ... order=order, ... taker_asset_fill_amount=order["takerAssetAmount"], ... signature=maker_signature, -... tx_params=TxParams(from_=taker_address) +... tx_params=TxParams( +... from_=taker_address, value=web3.eth.generateGasPrice() * 150000 +... ), ... )) {'makerAssetFilledAmount': 100000000000000000, 'makerFeePaid': 0, - 'protocolFeePaid': 0, + 'protocolFeePaid': ..., 'takerAssetFilledAmount': 100000000000000000, 'takerFeePaid': 0} @@ -200,7 +210,9 @@ ... order=order, ... taker_asset_fill_amount=order["takerAssetAmount"], ... signature=maker_signature, -... tx_params=TxParams(from_=taker_address) +... tx_params=TxParams( +... from_=taker_address, value=web3.eth.generateGasPrice() * 150000 +... ) ... ) Once the transaction is mined, we can get the details of our exchange through @@ -333,7 +345,11 @@ ... orders=[order_1, order_2], ... taker_asset_fill_amounts=[1, 2], ... signatures=[signature_1, signature_2], -... tx_params=TxParams(from_=taker_address)) +... tx_params=TxParams( +... from_=taker_address, +... value=2*web3.eth.generateGasPrice()*150000 +... ) +... ) HexBytes('0x...') Estimating gas consumption diff --git a/python-packages/contract_wrappers/stubs/web3/gas_strategies/__init__.pyi b/python-packages/contract_wrappers/stubs/web3/gas_strategies/__init__.pyi new file mode 100644 index 0000000000..e69de29bb2 diff --git a/python-packages/contract_wrappers/stubs/web3/gas_strategies/rpc.pyi b/python-packages/contract_wrappers/stubs/web3/gas_strategies/rpc.pyi new file mode 100644 index 0000000000..34d58f9786 --- /dev/null +++ b/python-packages/contract_wrappers/stubs/web3/gas_strategies/rpc.pyi @@ -0,0 +1,2 @@ +def rpc_gas_price_strategy(): + ... diff --git a/python-packages/contract_wrappers/test/test_exchange_wrapper.py b/python-packages/contract_wrappers/test/test_exchange_wrapper.py index e4bc644f10..c0b67e9600 100644 --- a/python-packages/contract_wrappers/test/test_exchange_wrapper.py +++ b/python-packages/contract_wrappers/test/test_exchange_wrapper.py @@ -4,6 +4,8 @@ import pytest from eth_utils import remove_0x_prefix +from web3 import Web3 +from web3.gas_strategies.rpc import rpc_gas_price_strategy from zero_ex.contract_addresses import chain_to_addresses, ChainId from zero_ex.contract_wrappers import TxParams @@ -85,30 +87,128 @@ def test_exchange_wrapper__fill_order( ) order_signature = sign_hash(ganache_provider, maker, order_hash) + web3 = Web3(ganache_provider) + web3.eth.setGasPriceStrategy( # pylint: disable=no-member + rpc_gas_price_strategy + ) + fill_results = exchange_wrapper.fill_order.call( order=order, taker_asset_fill_amount=order["takerAssetAmount"], signature=order_signature, - tx_params=TxParams(from_=taker), + tx_params=TxParams( + from_=taker, + value=web3.eth.generateGasPrice() # pylint: disable=no-member + * 150000, + ), ) assert fill_results["makerAssetFilledAmount"] == 1 assert fill_results["takerAssetFilledAmount"] == 1 assert fill_results["makerFeePaid"] == 0 assert fill_results["takerFeePaid"] == 0 - assert fill_results["protocolFeePaid"] == 0 + assert ( + fill_results["protocolFeePaid"] + == web3.eth.generateGasPrice() * 150000 # pylint: disable=no-member + ) tx_hash = exchange_wrapper.fill_order.send_transaction( order=order, taker_asset_fill_amount=order["takerAssetAmount"], signature=order_signature, - tx_params=TxParams(from_=taker), + tx_params=TxParams( + from_=taker, + value=web3.eth.generateGasPrice() # pylint: disable=no-member + * 150000, + ), + ) + assert_valid(tx_hash.hex(), "/hexSchema") + + fill_event = exchange_wrapper.get_fill_event(tx_hash) + assert_fill_log(fill_event[0].args, maker, taker, order, order_hash) + + +def test_exchange_wrapper__fill_order__build_then_send( + accounts, + exchange_wrapper, # pylint: disable=redefined-outer-name + ganache_provider, + weth_asset_data, + zrx_asset_data, +): + """Test filling an order.""" + taker = accounts[0] + maker = accounts[1] + exchange_address = exchange_wrapper.contract_address + order = create_test_order(maker, 1, weth_asset_data, 1, zrx_asset_data) + order_hash = generate_order_hash_hex( + order=order, exchange_address=exchange_address, chain_id=1337 ) + order_signature = sign_hash(ganache_provider, maker, order_hash) + + web3 = Web3(ganache_provider) + web3.eth.setGasPriceStrategy( # pylint: disable=no-member + rpc_gas_price_strategy + ) + + tx_hash = Web3(ganache_provider).eth.sendTransaction( + exchange_wrapper.fill_order.build_transaction( + order=order, + taker_asset_fill_amount=order["takerAssetAmount"], + signature=order_signature, + tx_params=TxParams( + from_=taker, + value=web3.eth.generateGasPrice() # pylint: disable=no-member + * 150000, + ), + ) + ) + assert_valid(tx_hash.hex(), "/hexSchema") fill_event = exchange_wrapper.get_fill_event(tx_hash) assert_fill_log(fill_event[0].args, maker, taker, order, order_hash) +def test_exchange_wrapper__fill_order__without_from_tx_param( + accounts, + exchange_wrapper, # pylint: disable=redefined-outer-name + ganache_provider, + weth_asset_data, + zrx_asset_data, +): + """Test filling an order.""" + maker = accounts[1] + exchange_address = exchange_wrapper.contract_address + order = create_test_order(maker, 1, weth_asset_data, 1, zrx_asset_data) + order_hash = generate_order_hash_hex( + order=order, exchange_address=exchange_address, chain_id=1337 + ) + order_signature = sign_hash(ganache_provider, maker, order_hash) + + web3 = Web3(ganache_provider) + web3.eth.setGasPriceStrategy( # pylint: disable=no-member + rpc_gas_price_strategy + ) + + exchange_wrapper._web3_eth.defaultAccount = ( # pylint: disable=protected-access + None + ) + exchange_wrapper._web3_eth.accounts.clear() # pylint: disable=protected-access + + built_tx = exchange_wrapper.fill_order.build_transaction( + order=order, + taker_asset_fill_amount=order["takerAssetAmount"], + signature=order_signature, + tx_params=TxParams( + value=web3.eth.generateGasPrice() # pylint: disable=no-member + * 150000, + ), + ) + assert ( + built_tx["data"][:824] + == "0x9b44d5560000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000003a00000000000000000000000006ecbe1db9ef729cbe972c83fb886247691fb6beb000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005af3107a40000000000000000000000000000000000000000000000000" + ) + + # pylint: disable=too-many-locals def test_exchange_wrapper__batch_fill_orders( accounts, @@ -136,11 +236,22 @@ def test_exchange_wrapper__batch_fill_orders( for order_hash in order_hashes ] taker_amounts = [order["takerAssetAmount"] for order in orders] + + web3 = Web3(ganache_provider) + web3.eth.setGasPriceStrategy( # pylint: disable=no-member + rpc_gas_price_strategy + ) + tx_hash = exchange_wrapper.batch_fill_orders.send_transaction( orders=orders, taker_asset_fill_amounts=taker_amounts, signatures=order_signatures, - tx_params=TxParams(from_=taker), + tx_params=TxParams( + from_=taker, + value=2 + * web3.eth.generateGasPrice() # pylint: disable=no-member + * 150000, + ), ) assert_valid(tx_hash.hex(), "/hexSchema") diff --git a/python-packages/contract_wrappers/tox.ini b/python-packages/contract_wrappers/tox.ini index c3e7029329..37e903015b 100644 --- a/python-packages/contract_wrappers/tox.ini +++ b/python-packages/contract_wrappers/tox.ini @@ -22,5 +22,5 @@ commands = [testenv:run_tests_against_deployment] setenv = PY_IGNORE_IMPORTMISMATCH = 1 commands = - pip install 0x-contract-wrappers[dev] + pip install --pre 0x-contract-wrappers[dev] pytest --doctest-modules src test diff --git a/python-packages/json_schemas/setup.py b/python-packages/json_schemas/setup.py index 1a2d5d6836..737536b3c1 100755 --- a/python-packages/json_schemas/setup.py +++ b/python-packages/json_schemas/setup.py @@ -143,7 +143,7 @@ def run(self): setup( name="0x-json-schemas", - version="1.1.1", + version="2.1.0.dev2", description="JSON schemas for 0x applications", long_description=README_MD, long_description_content_type="text/markdown", @@ -165,7 +165,7 @@ def run(self): install_requires=["jsonschema", "mypy_extensions", "stringcase"], extras_require={ "dev": [ - "0x-contract-addresses", + "0x-contract-addresses==3.0.0.dev3", "bandit", "black", "coverage", diff --git a/python-packages/json_schemas/tox.ini b/python-packages/json_schemas/tox.ini index 87ec5e8473..1c009aa470 100644 --- a/python-packages/json_schemas/tox.ini +++ b/python-packages/json_schemas/tox.ini @@ -22,5 +22,5 @@ commands = [testenv:run_tests_against_deployment] setenv = PY_IGNORE_IMPORTMISMATCH = 1 commands = - pip install 0x-json-schemas[dev] + pip install --pre 0x-json-schemas[dev] pytest --doctest-modules src test diff --git a/python-packages/middlewares/setup.py b/python-packages/middlewares/setup.py index 53b78a8023..f586d0c046 100755 --- a/python-packages/middlewares/setup.py +++ b/python-packages/middlewares/setup.py @@ -136,7 +136,7 @@ def run(self): setup( name="0x-middlewares", - version="1.0.0", + version="1.0.0.dev0", description="Web3 middlewares for 0x applications", long_description=README_MD, long_description_content_type="text/markdown", diff --git a/python-packages/middlewares/tox.ini b/python-packages/middlewares/tox.ini index cb4d2548e6..42b384e668 100644 --- a/python-packages/middlewares/tox.ini +++ b/python-packages/middlewares/tox.ini @@ -20,6 +20,7 @@ commands = pytest test [testenv:run_tests_against_deployment] +setenv = PY_IGNORE_IMPORTMISMATCH = 1 commands = - pip install 0x-middlewares - pytest test + pip install --pre 0x-middlewares[dev] + pytest --doctest-modules test diff --git a/python-packages/order_utils/setup.py b/python-packages/order_utils/setup.py index 8a899270ca..ec1afbcecb 100755 --- a/python-packages/order_utils/setup.py +++ b/python-packages/order_utils/setup.py @@ -156,7 +156,7 @@ def run(self): setup( name="0x-order-utils", - version="4.0.0", + version="4.0.0.dev8", description="Order utilities for 0x applications", long_description=README_MD, long_description_content_type="text/markdown", @@ -176,9 +176,9 @@ def run(self): "ganache": GanacheCommand, }, install_requires=[ - "0x-contract-addresses", - "0x-contract-artifacts", - "0x-json-schemas", + "0x-contract-addresses==3.0.0.dev3", + "0x-contract-artifacts==3.0.0.dev2", + "0x-json-schemas==2.1.0.dev2", "deprecated", "web3", "eth-abi", diff --git a/python-packages/order_utils/tox.ini b/python-packages/order_utils/tox.ini index c51a7e14f0..f3a1dd2710 100644 --- a/python-packages/order_utils/tox.ini +++ b/python-packages/order_utils/tox.ini @@ -22,5 +22,5 @@ commands = [testenv:run_tests_against_deployment] setenv = PY_IGNORE_IMPORTMISMATCH = 1 commands = - pip install 0x-order-utils[dev] + pip install --pre 0x-order-utils[dev] pytest --doctest-modules src test diff --git a/python-packages/sra_client/setup.py b/python-packages/sra_client/setup.py index 4e2d6a78a8..4ced8e3d51 100755 --- a/python-packages/sra_client/setup.py +++ b/python-packages/sra_client/setup.py @@ -19,7 +19,7 @@ from setuptools.command.test import test as TestCommand NAME = "0x-sra-client" -VERSION = "4.0.0" +VERSION = "4.0.0.dev0" # To install the library, run the following # # python setup.py install @@ -92,6 +92,9 @@ class StartTestRelayerCommand(distutils.command.build_py.build_py): def run(self): """Run `docker-compose up`.""" + subprocess.call( # nosec + ("docker-compose -f test/relayer/docker-compose.yml pull").split() + ) subprocess.call( # nosec ("docker-compose -f test/relayer/docker-compose.yml up -d").split() ) @@ -210,9 +213,10 @@ def run(self): }, extras_require={ "dev": [ - "0x-contract-artifacts", - "0x-contract-addresses", - "0x-order-utils", + "0x-contract-artifacts==3.0.0.dev2", + "0x-contract-addresses==3.0.0.dev3", + "0x-contract-wrappers==2.0.0.dev10", + "0x-order-utils==4.0.0.dev8", "web3", "bandit", "black", diff --git a/python-packages/sra_client/src/zero_ex/sra_client/__init__.py b/python-packages/sra_client/src/zero_ex/sra_client/__init__.py index a97cce2a56..8d47adb389 100644 --- a/python-packages/sra_client/src/zero_ex/sra_client/__init__.py +++ b/python-packages/sra_client/src/zero_ex/sra_client/__init__.py @@ -260,8 +260,20 @@ Select an order from the orderbook ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +We'll select the order we just submitted, which must be referred to by order +hash. To calculate an order hash, we'll use the Exchange contract: + +>>> from zero_ex.contract_wrappers.exchange import Exchange +>>> exchange = Exchange( +... web3_or_provider=eth_node, +... contract_address=chain_to_addresses(ChainId.GANACHE).exchange +... ) >>> from zero_ex.contract_wrappers.order_conversions import jsdict_to_order ->>> order = jsdict_to_order(orderbook.bids.records[0].order) +>>> order = jsdict_to_order( +... relayer.get_order( +... '0x' + exchange.get_order_info.call(order)["orderHash"].hex() +... ).order +... ) >>> from pprint import pprint >>> pprint(order) {'chainId': 1337, @@ -326,23 +338,48 @@ book. Now let's have the taker fill it: >>> from zero_ex.contract_wrappers import TxParams ->>> from zero_ex.contract_wrappers.exchange import Exchange >>> from zero_ex.order_utils import Order ->>> exchange = Exchange( -... web3_or_provider=eth_node, -... contract_address=chain_to_addresses(ChainId.GANACHE).exchange -... ) (Due to `an Issue with the Launch Kit Backend `_, we need to checksum the address in the order before filling it.) >>> order['makerAddress'] = Web3.toChecksumAddress(order['makerAddress']) +Finally, filling an order requires paying a protocol fee, which can be sent as +value in the transaction. The calculation of the amount to send is a function +of the gas price, so we need some help from Web3.py for that: + +>>> from web3.gas_strategies.rpc import rpc_gas_price_strategy +>>> web3 = Web3(eth_node) +>>> web3.eth.setGasPriceStrategy(rpc_gas_price_strategy) + +Before actually executing the fill, it's a good idea to run it as read-only +(non-transactional) so that we can get intelligible errors in case there's +something wrong: + +>>> pprint(exchange.fill_order.call( +... order=order, +... taker_asset_fill_amount=order['takerAssetAmount']/2, # note the half fill +... signature=bytes.fromhex(order['signature'].replace('0x', '')), +... tx_params=TxParams( +... from_=taker_address, value=web3.eth.generateGasPrice()*150000, +... ), +... )) +{'makerAssetFilledAmount': 1, + 'makerFeePaid': 0, + 'protocolFeePaid': ..., + 'takerAssetFilledAmount': 1, + 'takerFeePaid': 0} + +Now we're finally ready to execute the fill: + >>> exchange.fill_order.send_transaction( ... order=order, -... taker_asset_fill_amount=order['makerAssetAmount']/2, # note the half fill +... taker_asset_fill_amount=order['takerAssetAmount']/2, # note the half fill ... signature=bytes.fromhex(order['signature'].replace('0x', '')), -... tx_params=TxParams(from_=taker_address) +... tx_params=TxParams( +... from_=taker_address, value=web3.eth.generateGasPrice()*150000, +... ), ... ) HexBytes('0x...') diff --git a/python-packages/sra_client/test/relayer/docker-compose.yml b/python-packages/sra_client/test/relayer/docker-compose.yml index 12353c3b34..b0776662bb 100644 --- a/python-packages/sra_client/test/relayer/docker-compose.yml +++ b/python-packages/sra_client/test/relayer/docker-compose.yml @@ -6,10 +6,10 @@ services: ports: - "8545:8545" environment: - - VERSION=4.4.0-beta.1 + - VERSION=latest - SNAPSHOT_NAME=0x_ganache_snapshot-v3-beta mesh: - image: 0xorg/mesh:6.0.0-beta-0xv3 + image: 0xorg/mesh:0xV3 depends_on: - ganache environment: diff --git a/python-packages/sra_client/tox.ini b/python-packages/sra_client/tox.ini index 2f40a07c89..92492179c0 100644 --- a/python-packages/sra_client/tox.ini +++ b/python-packages/sra_client/tox.ini @@ -21,4 +21,5 @@ commands = setenv = PY_IGNORE_IMPORTMISMATCH = 1 commands = pip install 0x-sra-client[dev] + pip install --pre 0x-sra-client pytest --doctest-modules src test