diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..0687d64 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,53 @@ +name: Test + +on: + push: + branches: [master, develop] + pull_request: + workflow_dispatch: + +jobs: + test: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: ['3.9', '3.10', '3.11', '3.12'] + + steps: + - uses: actions/checkout@v4 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Install system dependencies + run: | + sudo apt-get update + sudo apt-get install -y pkg-config libsecp256k1-dev + + - name: Install dependencies + run: | + python -m pip install --upgrade pip setuptools wheel + pip install -r requirements.txt + pip install -r test-requirements.txt + + # Integration tests are excluded: they require a live Binance Chain node. + - name: Run unit tests + run: pytest tests/ -m "not integration" -v --cov binance_chain --cov-report term-missing + + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: '3.12' + + - name: Install flake8 + run: pip install flake8 + + - name: Run flake8 + run: flake8 --exclude binance_chain/protobuf/dex_pb2.py binance_chain/ diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..18b8949 --- /dev/null +++ b/.gitignore @@ -0,0 +1,23 @@ +__pycache__/ +*.py[cod] + +build/ +dist/ +*.egg-info/ +.eggs/ + +.venv/ +venv/ +env/ + +.tox/ +.pytest_cache/ +.coverage +.coverage.* +htmlcov/ + +docs/_build/ + +.idea/ +.vscode/ +.DS_Store diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 6a3ad29..0000000 --- a/.travis.yml +++ /dev/null @@ -1,18 +0,0 @@ -dist: xenial - -language: python - -python: - - "3.6" - - "3.7" - -install: - - pip install -r test-requirements.txt - - pip install -r requirements.txt - - pip install tox-travis - -script: - - tox - -after_success: - - coveralls diff --git a/README.rst b/README.rst index d7ab837..cd3ae3c 100644 --- a/README.rst +++ b/README.rst @@ -1,5 +1,5 @@ ======================================= -Welcome to python-binance-chain v0.1.19 +Welcome to python-binance-chain v0.1.20 ======================================= .. image:: https://img.shields.io/pypi/v/python-binance-chain.svg @@ -8,11 +8,8 @@ Welcome to python-binance-chain v0.1.19 .. image:: https://img.shields.io/pypi/l/python-binance-chain.svg :target: https://pypi.python.org/pypi/python-binance-chain -.. image:: https://img.shields.io/travis/sammchardy/python-binance-chain.svg - :target: https://travis-ci.org/sammchardy/python-binance-chain - -.. image:: https://img.shields.io/coveralls/sammchardy/python-binance-chain.svg - :target: https://coveralls.io/github/sammchardy/python-binance-chain +.. image:: https://github.com/mchardysam/python-binance-chain/actions/workflows/test.yml/badge.svg + :target: https://github.com/mchardysam/python-binance-chain/actions/workflows/test.yml .. image:: https://img.shields.io/pypi/wheel/python-binance-chain.svg :target: https://pypi.python.org/pypi/python-binance-chain @@ -27,7 +24,7 @@ PyPi https://pypi.python.org/pypi/python-binance-chain Source code - https://github.com/sammchardy/python-binance-chain + https://github.com/mchardysam/python-binance-chain Features @@ -45,7 +42,7 @@ Features - `Sign transactions <#sign-transaction>`_ and use the signed message how you want - `Ledger hardware wallet <#ledger>`_ device (Ledger Blue, Nano S & Nano X) support for signing messages - Async `Depth Cache <#depth-cache>`_ to keep a copy of the order book locally -- `Signing Service Support <#signing-service>`_ for `binance-chain-signing-service `_ +- `Signing Service Support <#signing-service>`_ for `binance-chain-signing-service `_ - Support for HTTP and HTTPS proxies and to override `Requests and AioHTTP settings `_ - `UltraJson `_ the ultra fast JSON parsing library for efficient message handling - Strong Python3 typing to reduce errors @@ -89,8 +86,7 @@ If using the production server there is no need to pass the environment variable # Alternatively pass no env to get production prod_client = HttpApiClient() - # connect client to different URL - client = HttpApiClient(api_url='https://yournet.com') + # connect client to different URL using custom environments, see below # get node time time = client.get_time() @@ -128,16 +124,16 @@ If using the production server there is no need to pass the environment variable # get open orders open_orders = client.get_open_orders('tbnb185tqzq3j6y7yep85lncaz9qeectjxqe5054cgn') - # get open orders + # get ticker ticker = client.get_ticker('NNB-0AD_BNB') - # get open orders + # get trades trades = client.get_trades(limit=2) - # get open orders + # get order order = client.get_order('9D0537108883C68B8F43811B780327CE97D8E01D-2') - # get open orders + # get trades trades = client.get_trades() # get transactions @@ -162,6 +158,7 @@ All methods are otherwise the same as the HttpApiClient from binance_chain.http import AsyncHttpApiClient from binance_chain.environment import BinanceEnvironment + import asyncio loop = None @@ -226,6 +223,8 @@ You may also use the `Ledger Wallet class <#ledger>`_ to utilise your Ledger Har It can be initialised with your private key or your mnemonic phrase. +You can additionally provide BIP39 passphrase and derived wallet id. + Note that the BinanceEnvironment used for the wallet must match that of the HttpApiClient, testnet addresses will not work on the production system. @@ -254,7 +253,10 @@ see examples below from binance_chain.environment import BinanceEnvironment testnet_env = BinanceEnvironment.get_testnet_env() - wallet = Wallet.create_wallet_from_mnemonic('mnemonic word string', env=testnet_env) + wallet = Wallet.create_wallet_from_mnemonic('mnemonic word string', + passphrase='optional passphrase', + child=0, + env=testnet_env) print(wallet.address) print(wallet.private_key) print(wallet.public_key_hex) @@ -292,6 +294,8 @@ General case from binance_chain.http import HttpApiClient from binance_chain.messages import NewOrderMsg from binance_chain.wallet import Wallet + from binance_chain.constants import TimeInForce, OrderSide, OrderType + from decimal import Decimal wallet = Wallet('private_key_string') client = HttpApiClient() @@ -300,7 +304,7 @@ General case new_order_msg = NewOrderMsg( wallet=wallet, symbol="ANN-457_BNB", - time_in_force=TimeInForce.GTE, + time_in_force=TimeInForce.GOOD_TILL_EXPIRE, order_type=OrderType.LIMIT, side=OrderSide.BUY, price=Decimal(0.000396000), @@ -363,6 +367,7 @@ General case from binance_chain.http import HttpApiClient from binance_chain.messages import FreezeMsg from binance_chain.wallet import Wallet + from decimal import Decimal wallet = Wallet('private_key_string') client = HttpApiClient() @@ -384,6 +389,7 @@ General case from binance_chain.http import HttpApiClient from binance_chain.messages import UnFreezeMsg from binance_chain.wallet import Wallet + from decimal import Decimal wallet = Wallet('private_key_string') client = HttpApiClient() @@ -510,7 +516,7 @@ See `API `_ read the docs there +This client interacts with the `binance-chain-signing-service `_ read the docs there to create our own signing service. **Signing and then broadcasting** @@ -858,6 +865,7 @@ to create our own signing service. from binance_chain.messages import NewOrderMsg from binance_chain.signing.http import HttpApiSigningClient + from binance_chain.constants import TimeInForce, OrderSide, OrderType signing_client = HttpApiSigningClient('http://localhost:8000', username='sam', password='mypass') @@ -902,6 +910,7 @@ To sign and broadcast an order use the `broadcast_order` method. This returns th from binance_chain.messages import NewOrderMsg from binance_chain.signing.http import HttpApiSigningClient + from binance_chain.constants import TimeInForce, OrderSide, OrderType signing_client = HttpApiSigningClient('http://localhost:8000', username='sam', password='mypass') @@ -928,6 +937,8 @@ Like all other libraries there is an async version. from binance_chain.signing.http import AsyncHttpApiSigningClient from binance_chain.http import AsyncHttpApiClient, PeerType from binance_chain.environment import BinanceEnvironment + from binance_chain.constants import TimeInForce, OrderSide, OrderType + import asyncio loop = None @@ -1076,13 +1087,31 @@ Running Tests .. code-block:: bash - git clone https://github.com/sammchardy/python-binance-chain.git + git clone https://github.com/mchardysam/python-binance-chain.git cd python-binance-chain + pip install -r requirements.txt pip install -r test-requirements.txt + # unit tests only, no network access required + python -m pytest tests/ -m "not integration" + + # the full suite, including tests that need a live Binance Chain node python -m pytest tests/ +Regenerating Protobuf +--------------------- + +The transaction schema lives in ``binance_chain/protobuf/dex.proto``. If you need to +regenerate ``dex_pb2.py``: + +.. code-block:: bash + + pip install grpcio-tools + python -m grpc_tools.protoc -I binance_chain/protobuf \ + --python_out=binance_chain/protobuf binance_chain/protobuf/dex.proto + + Donate ------ @@ -1102,14 +1131,4 @@ Thanks Other Exchanges --------------- -If you use `Binance `_ check out my `python-binance `_ library. - -If you use `Kucoin `_ check out my `python-kucoin `_ library. - -If you use `Allcoin `_ check out my `python-allucoin `_ library. - -If you use `IDEX `_ check out my `python-idex `_ library. - -If you use `BigONE `_ check out my `python-bigone `_ library. - -.. image:: https://analytics-pixel.appspot.com/UA-111417213-1/github/python-kucoin?pixel +If you use `IDEX `_ check out my `python-idex `_ library. diff --git a/binance_chain/__init__.py b/binance_chain/__init__.py index 069c74c..5c65c53 100644 --- a/binance_chain/__init__.py +++ b/binance_chain/__init__.py @@ -4,4 +4,4 @@ """ -__version__ = '0.1.19' +__version__ = '0.1.20' diff --git a/binance_chain/http.py b/binance_chain/http.py index 3c45e62..f5757ee 100644 --- a/binance_chain/http.py +++ b/binance_chain/http.py @@ -80,7 +80,7 @@ def _get_request_kwargs(self, method, **kwargs): if kwargs['data'] and method == 'get': kwargs['params'] = kwargs['data'] - del(kwargs['data']) + del kwargs['data'] if method == 'post': kwargs['headers']['content-type'] = 'text/plain' @@ -289,7 +289,7 @@ def get_markets(self): :return: API Response """ - return self._get("markets") + return self._get("markets?limit=1000") def get_fees(self): """Gets the current trading fees settings @@ -371,7 +371,7 @@ def broadcast_msg(self, msg: binance_chain.messages.Msg, sync: bool = False): req_path = 'broadcast' if sync: - req_path += f'?sync=1' + req_path += '?sync=1' res = self._post(req_path, data=data) msg.wallet.increment_account_sequence() @@ -419,7 +419,7 @@ def broadcast_hex_msg(self, hex_msg: str, sync: bool = False): """ req_path = 'broadcast' if sync: - req_path += f'?sync=1' + req_path += '?sync=1' res = self._post(req_path, data=hex_msg) return res @@ -864,7 +864,7 @@ async def broadcast_msg(self, msg: binance_chain.messages.Msg, sync: bool = Fals req_path = 'broadcast' if sync: - req_path += f'?sync=1' + req_path += '?sync=1' res = await self._post(req_path, data=data) msg.wallet.increment_account_sequence() @@ -874,7 +874,7 @@ async def broadcast_msg(self, msg: binance_chain.messages.Msg, sync: bool = Fals async def broadcast_hex_msg(self, hex_msg: str, sync: bool = False): req_path = 'broadcast' if sync: - req_path += f'?sync=1' + req_path += '?sync=1' res = await self._post(req_path, data=hex_msg) return res diff --git a/binance_chain/messages.py b/binance_chain/messages.py index 30567e5..7d255ce 100644 --- a/binance_chain/messages.py +++ b/binance_chain/messages.py @@ -41,7 +41,7 @@ def to_protobuf(self): def to_amino(self): proto = self.to_protobuf() - if type(proto) != bytes: + if type(proto) is not bytes: proto = proto.SerializeToString() # wrap with type @@ -149,7 +149,7 @@ def to_dict(self) -> Dict: ]) def to_sign_dict(self) -> Dict: - return{ + return { 'order_type': self._order_type, 'price': self._price, 'quantity': self._quantity, diff --git a/binance_chain/node_rpc/http.py b/binance_chain/node_rpc/http.py index 234ab20..51721f0 100644 --- a/binance_chain/node_rpc/http.py +++ b/binance_chain/node_rpc/http.py @@ -58,7 +58,7 @@ def request_kwargs(self, method, **kwargs): if kwargs['data'] and method == 'get': kwargs['params'] = kwargs['data'] - del(kwargs['data']) + del kwargs['data'] if method == 'post': kwargs['headers']['content-type'] = 'text/plain' diff --git a/binance_chain/protobuf/dex.proto b/binance_chain/protobuf/dex.proto new file mode 100644 index 0000000..570ec8b --- /dev/null +++ b/binance_chain/protobuf/dex.proto @@ -0,0 +1,78 @@ +syntax = "proto3"; + +package transaction; + +option java_package = "com.binance.dex.api.proto"; +option java_outer_classname = "Transaction"; +option java_multiple_files = true; + +message StdTx { + repeated bytes msgs = 1; + repeated bytes signatures = 2; + string memo = 3; + int64 source = 4; + bytes data = 5; +} + +message StdSignature { + message PubKey {} + bytes pub_key = 1; + bytes signature = 2; + int64 account_number = 3; + int64 sequence = 4; +} + +message NewOrder { + bytes sender = 1; + string id = 2; + string symbol = 3; + int64 ordertype = 4; + int64 side = 5; + int64 price = 6; + int64 quantity = 7; + int64 timeinforce = 8; +} + +message CancelOrder { + bytes sender = 1; + string symbol = 2; + string refid = 3; +} + +message TokenFreeze { + bytes from = 1; + string symbol = 2; + int64 amount = 3; +} + +message TokenUnfreeze { + bytes from = 1; + string symbol = 2; + int64 amount = 3; +} + +message Token { + string denom = 1; + int64 amount = 2; +} + +message Input { + bytes address = 1; + repeated Token coins = 2; +} + +message Output { + bytes address = 1; + repeated Token coins = 2; +} + +message Send { + repeated Input inputs = 1; + repeated Output outputs = 2; +} + +message Vote { + int64 proposal_id = 1; + bytes voter = 2; + int64 option = 3; +} diff --git a/binance_chain/protobuf/dex_pb2.py b/binance_chain/protobuf/dex_pb2.py index 5428aaf..d7d0b85 100644 --- a/binance_chain/protobuf/dex_pb2.py +++ b/binance_chain/protobuf/dex_pb2.py @@ -1,12 +1,12 @@ +# -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! # source: dex.proto - -import sys -_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) +# Protobuf Python Version: 4.25.1 +"""Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection +from google.protobuf import descriptor_pool as _descriptor_pool from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -14,665 +14,36 @@ -DESCRIPTOR = _descriptor.FileDescriptor( - name='dex.proto', - package='transaction', - syntax='proto3', - serialized_options=_b('\n\031com.binance.dex.api.protoB\013TransactionP\001'), - serialized_pb=_b('\n\tdex.proto\x12\x0btransaction\"U\n\x05StdTx\x12\x0c\n\x04msgs\x18\x01 \x03(\x0c\x12\x12\n\nsignatures\x18\x02 \x03(\x0c\x12\x0c\n\x04memo\x18\x03 \x01(\t\x12\x0e\n\x06source\x18\x04 \x01(\x03\x12\x0c\n\x04\x64\x61ta\x18\x05 \x01(\x0c\"f\n\x0cStdSignature\x12\x0f\n\x07pub_key\x18\x01 \x01(\x0c\x12\x11\n\tsignature\x18\x02 \x01(\x0c\x12\x16\n\x0e\x61\x63\x63ount_number\x18\x03 \x01(\x03\x12\x10\n\x08sequence\x18\x04 \x01(\x03\x1a\x08\n\x06PubKey\"\x8d\x01\n\x08NewOrder\x12\x0e\n\x06sender\x18\x01 \x01(\x0c\x12\n\n\x02id\x18\x02 \x01(\t\x12\x0e\n\x06symbol\x18\x03 \x01(\t\x12\x11\n\tordertype\x18\x04 \x01(\x03\x12\x0c\n\x04side\x18\x05 \x01(\x03\x12\r\n\x05price\x18\x06 \x01(\x03\x12\x10\n\x08quantity\x18\x07 \x01(\x03\x12\x13\n\x0btimeinforce\x18\x08 \x01(\x03\"<\n\x0b\x43\x61ncelOrder\x12\x0e\n\x06sender\x18\x01 \x01(\x0c\x12\x0e\n\x06symbol\x18\x02 \x01(\t\x12\r\n\x05refid\x18\x03 \x01(\t\";\n\x0bTokenFreeze\x12\x0c\n\x04\x66rom\x18\x01 \x01(\x0c\x12\x0e\n\x06symbol\x18\x02 \x01(\t\x12\x0e\n\x06\x61mount\x18\x03 \x01(\x03\"=\n\rTokenUnfreeze\x12\x0c\n\x04\x66rom\x18\x01 \x01(\x0c\x12\x0e\n\x06symbol\x18\x02 \x01(\t\x12\x0e\n\x06\x61mount\x18\x03 \x01(\x03\"&\n\x05Token\x12\r\n\x05\x64\x65nom\x18\x01 \x01(\t\x12\x0e\n\x06\x61mount\x18\x02 \x01(\x03\";\n\x05Input\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\x0c\x12!\n\x05\x63oins\x18\x02 \x03(\x0b\x32\x12.transaction.Token\"<\n\x06Output\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\x0c\x12!\n\x05\x63oins\x18\x02 \x03(\x0b\x32\x12.transaction.Token\"P\n\x04Send\x12\"\n\x06inputs\x18\x01 \x03(\x0b\x32\x12.transaction.Input\x12$\n\x07outputs\x18\x02 \x03(\x0b\x32\x13.transaction.Output\":\n\x04Vote\x12\x13\n\x0bproposal_id\x18\x01 \x01(\x03\x12\r\n\x05voter\x18\x02 \x01(\x0c\x12\x0e\n\x06option\x18\x03 \x01(\x03\x42*\n\x19\x63om.binance.dex.api.protoB\x0bTransactionP\x01\x62\x06proto3') -) - - - - -_STDTX = _descriptor.Descriptor( - name='StdTx', - full_name='transaction.StdTx', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='msgs', full_name='transaction.StdTx.msgs', index=0, - number=1, type=12, cpp_type=9, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='signatures', full_name='transaction.StdTx.signatures', index=1, - number=2, type=12, cpp_type=9, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='memo', full_name='transaction.StdTx.memo', index=2, - number=3, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='source', full_name='transaction.StdTx.source', index=3, - number=4, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='data', full_name='transaction.StdTx.data', index=4, - number=5, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=26, - serialized_end=111, -) - - -_STDSIGNATURE_PUBKEY = _descriptor.Descriptor( - name='PubKey', - full_name='transaction.StdSignature.PubKey', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=207, - serialized_end=215, -) - -_STDSIGNATURE = _descriptor.Descriptor( - name='StdSignature', - full_name='transaction.StdSignature', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='pub_key', full_name='transaction.StdSignature.pub_key', index=0, - number=1, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='signature', full_name='transaction.StdSignature.signature', index=1, - number=2, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='account_number', full_name='transaction.StdSignature.account_number', index=2, - number=3, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='sequence', full_name='transaction.StdSignature.sequence', index=3, - number=4, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[_STDSIGNATURE_PUBKEY, ], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=113, - serialized_end=215, -) - - -_NEWORDER = _descriptor.Descriptor( - name='NewOrder', - full_name='transaction.NewOrder', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='sender', full_name='transaction.NewOrder.sender', index=0, - number=1, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='id', full_name='transaction.NewOrder.id', index=1, - number=2, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='symbol', full_name='transaction.NewOrder.symbol', index=2, - number=3, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='ordertype', full_name='transaction.NewOrder.ordertype', index=3, - number=4, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='side', full_name='transaction.NewOrder.side', index=4, - number=5, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='price', full_name='transaction.NewOrder.price', index=5, - number=6, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='quantity', full_name='transaction.NewOrder.quantity', index=6, - number=7, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='timeinforce', full_name='transaction.NewOrder.timeinforce', index=7, - number=8, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=218, - serialized_end=359, -) - - -_CANCELORDER = _descriptor.Descriptor( - name='CancelOrder', - full_name='transaction.CancelOrder', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='sender', full_name='transaction.CancelOrder.sender', index=0, - number=1, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='symbol', full_name='transaction.CancelOrder.symbol', index=1, - number=2, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='refid', full_name='transaction.CancelOrder.refid', index=2, - number=3, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=361, - serialized_end=421, -) - - -_TOKENFREEZE = _descriptor.Descriptor( - name='TokenFreeze', - full_name='transaction.TokenFreeze', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='from', full_name='transaction.TokenFreeze.from', index=0, - number=1, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='symbol', full_name='transaction.TokenFreeze.symbol', index=1, - number=2, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='amount', full_name='transaction.TokenFreeze.amount', index=2, - number=3, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=423, - serialized_end=482, -) - - -_TOKENUNFREEZE = _descriptor.Descriptor( - name='TokenUnfreeze', - full_name='transaction.TokenUnfreeze', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='from', full_name='transaction.TokenUnfreeze.from', index=0, - number=1, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='symbol', full_name='transaction.TokenUnfreeze.symbol', index=1, - number=2, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='amount', full_name='transaction.TokenUnfreeze.amount', index=2, - number=3, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=484, - serialized_end=545, -) - - -_TOKEN = _descriptor.Descriptor( - name='Token', - full_name='transaction.Token', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='denom', full_name='transaction.Token.denom', index=0, - number=1, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='amount', full_name='transaction.Token.amount', index=1, - number=2, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=547, - serialized_end=585, -) - - -_INPUT = _descriptor.Descriptor( - name='Input', - full_name='transaction.Input', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='address', full_name='transaction.Input.address', index=0, - number=1, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='coins', full_name='transaction.Input.coins', index=1, - number=2, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=587, - serialized_end=646, -) - - -_OUTPUT = _descriptor.Descriptor( - name='Output', - full_name='transaction.Output', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='address', full_name='transaction.Output.address', index=0, - number=1, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='coins', full_name='transaction.Output.coins', index=1, - number=2, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=648, - serialized_end=708, -) - - -_SEND = _descriptor.Descriptor( - name='Send', - full_name='transaction.Send', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='inputs', full_name='transaction.Send.inputs', index=0, - number=1, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='outputs', full_name='transaction.Send.outputs', index=1, - number=2, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=710, - serialized_end=790, -) - - -_VOTE = _descriptor.Descriptor( - name='Vote', - full_name='transaction.Vote', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='proposal_id', full_name='transaction.Vote.proposal_id', index=0, - number=1, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='voter', full_name='transaction.Vote.voter', index=1, - number=2, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='option', full_name='transaction.Vote.option', index=2, - number=3, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=792, - serialized_end=850, -) - -_STDSIGNATURE_PUBKEY.containing_type = _STDSIGNATURE -_INPUT.fields_by_name['coins'].message_type = _TOKEN -_OUTPUT.fields_by_name['coins'].message_type = _TOKEN -_SEND.fields_by_name['inputs'].message_type = _INPUT -_SEND.fields_by_name['outputs'].message_type = _OUTPUT -DESCRIPTOR.message_types_by_name['StdTx'] = _STDTX -DESCRIPTOR.message_types_by_name['StdSignature'] = _STDSIGNATURE -DESCRIPTOR.message_types_by_name['NewOrder'] = _NEWORDER -DESCRIPTOR.message_types_by_name['CancelOrder'] = _CANCELORDER -DESCRIPTOR.message_types_by_name['TokenFreeze'] = _TOKENFREEZE -DESCRIPTOR.message_types_by_name['TokenUnfreeze'] = _TOKENUNFREEZE -DESCRIPTOR.message_types_by_name['Token'] = _TOKEN -DESCRIPTOR.message_types_by_name['Input'] = _INPUT -DESCRIPTOR.message_types_by_name['Output'] = _OUTPUT -DESCRIPTOR.message_types_by_name['Send'] = _SEND -DESCRIPTOR.message_types_by_name['Vote'] = _VOTE -_sym_db.RegisterFileDescriptor(DESCRIPTOR) - -StdTx = _reflection.GeneratedProtocolMessageType('StdTx', (_message.Message,), dict( - DESCRIPTOR = _STDTX, - __module__ = 'dex_pb2' - # @@protoc_insertion_point(class_scope:transaction.StdTx) - )) -_sym_db.RegisterMessage(StdTx) - -StdSignature = _reflection.GeneratedProtocolMessageType('StdSignature', (_message.Message,), dict( - - PubKey = _reflection.GeneratedProtocolMessageType('PubKey', (_message.Message,), dict( - DESCRIPTOR = _STDSIGNATURE_PUBKEY, - __module__ = 'dex_pb2' - # @@protoc_insertion_point(class_scope:transaction.StdSignature.PubKey) - )) - , - DESCRIPTOR = _STDSIGNATURE, - __module__ = 'dex_pb2' - # @@protoc_insertion_point(class_scope:transaction.StdSignature) - )) -_sym_db.RegisterMessage(StdSignature) -_sym_db.RegisterMessage(StdSignature.PubKey) - -NewOrder = _reflection.GeneratedProtocolMessageType('NewOrder', (_message.Message,), dict( - DESCRIPTOR = _NEWORDER, - __module__ = 'dex_pb2' - # @@protoc_insertion_point(class_scope:transaction.NewOrder) - )) -_sym_db.RegisterMessage(NewOrder) - -CancelOrder = _reflection.GeneratedProtocolMessageType('CancelOrder', (_message.Message,), dict( - DESCRIPTOR = _CANCELORDER, - __module__ = 'dex_pb2' - # @@protoc_insertion_point(class_scope:transaction.CancelOrder) - )) -_sym_db.RegisterMessage(CancelOrder) - -TokenFreeze = _reflection.GeneratedProtocolMessageType('TokenFreeze', (_message.Message,), dict( - DESCRIPTOR = _TOKENFREEZE, - __module__ = 'dex_pb2' - # @@protoc_insertion_point(class_scope:transaction.TokenFreeze) - )) -_sym_db.RegisterMessage(TokenFreeze) - -TokenUnfreeze = _reflection.GeneratedProtocolMessageType('TokenUnfreeze', (_message.Message,), dict( - DESCRIPTOR = _TOKENUNFREEZE, - __module__ = 'dex_pb2' - # @@protoc_insertion_point(class_scope:transaction.TokenUnfreeze) - )) -_sym_db.RegisterMessage(TokenUnfreeze) - -Token = _reflection.GeneratedProtocolMessageType('Token', (_message.Message,), dict( - DESCRIPTOR = _TOKEN, - __module__ = 'dex_pb2' - # @@protoc_insertion_point(class_scope:transaction.Token) - )) -_sym_db.RegisterMessage(Token) - -Input = _reflection.GeneratedProtocolMessageType('Input', (_message.Message,), dict( - DESCRIPTOR = _INPUT, - __module__ = 'dex_pb2' - # @@protoc_insertion_point(class_scope:transaction.Input) - )) -_sym_db.RegisterMessage(Input) - -Output = _reflection.GeneratedProtocolMessageType('Output', (_message.Message,), dict( - DESCRIPTOR = _OUTPUT, - __module__ = 'dex_pb2' - # @@protoc_insertion_point(class_scope:transaction.Output) - )) -_sym_db.RegisterMessage(Output) - -Send = _reflection.GeneratedProtocolMessageType('Send', (_message.Message,), dict( - DESCRIPTOR = _SEND, - __module__ = 'dex_pb2' - # @@protoc_insertion_point(class_scope:transaction.Send) - )) -_sym_db.RegisterMessage(Send) - -Vote = _reflection.GeneratedProtocolMessageType('Vote', (_message.Message,), dict( - DESCRIPTOR = _VOTE, - __module__ = 'dex_pb2' - # @@protoc_insertion_point(class_scope:transaction.Vote) - )) -_sym_db.RegisterMessage(Vote) - - -DESCRIPTOR._options = None +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\tdex.proto\x12\x0btransaction\"U\n\x05StdTx\x12\x0c\n\x04msgs\x18\x01 \x03(\x0c\x12\x12\n\nsignatures\x18\x02 \x03(\x0c\x12\x0c\n\x04memo\x18\x03 \x01(\t\x12\x0e\n\x06source\x18\x04 \x01(\x03\x12\x0c\n\x04\x64\x61ta\x18\x05 \x01(\x0c\"f\n\x0cStdSignature\x12\x0f\n\x07pub_key\x18\x01 \x01(\x0c\x12\x11\n\tsignature\x18\x02 \x01(\x0c\x12\x16\n\x0e\x61\x63\x63ount_number\x18\x03 \x01(\x03\x12\x10\n\x08sequence\x18\x04 \x01(\x03\x1a\x08\n\x06PubKey\"\x8d\x01\n\x08NewOrder\x12\x0e\n\x06sender\x18\x01 \x01(\x0c\x12\n\n\x02id\x18\x02 \x01(\t\x12\x0e\n\x06symbol\x18\x03 \x01(\t\x12\x11\n\tordertype\x18\x04 \x01(\x03\x12\x0c\n\x04side\x18\x05 \x01(\x03\x12\r\n\x05price\x18\x06 \x01(\x03\x12\x10\n\x08quantity\x18\x07 \x01(\x03\x12\x13\n\x0btimeinforce\x18\x08 \x01(\x03\"<\n\x0b\x43\x61ncelOrder\x12\x0e\n\x06sender\x18\x01 \x01(\x0c\x12\x0e\n\x06symbol\x18\x02 \x01(\t\x12\r\n\x05refid\x18\x03 \x01(\t\";\n\x0bTokenFreeze\x12\x0c\n\x04\x66rom\x18\x01 \x01(\x0c\x12\x0e\n\x06symbol\x18\x02 \x01(\t\x12\x0e\n\x06\x61mount\x18\x03 \x01(\x03\"=\n\rTokenUnfreeze\x12\x0c\n\x04\x66rom\x18\x01 \x01(\x0c\x12\x0e\n\x06symbol\x18\x02 \x01(\t\x12\x0e\n\x06\x61mount\x18\x03 \x01(\x03\"&\n\x05Token\x12\r\n\x05\x64\x65nom\x18\x01 \x01(\t\x12\x0e\n\x06\x61mount\x18\x02 \x01(\x03\";\n\x05Input\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\x0c\x12!\n\x05\x63oins\x18\x02 \x03(\x0b\x32\x12.transaction.Token\"<\n\x06Output\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\x0c\x12!\n\x05\x63oins\x18\x02 \x03(\x0b\x32\x12.transaction.Token\"P\n\x04Send\x12\"\n\x06inputs\x18\x01 \x03(\x0b\x32\x12.transaction.Input\x12$\n\x07outputs\x18\x02 \x03(\x0b\x32\x13.transaction.Output\":\n\x04Vote\x12\x13\n\x0bproposal_id\x18\x01 \x01(\x03\x12\r\n\x05voter\x18\x02 \x01(\x0c\x12\x0e\n\x06option\x18\x03 \x01(\x03\x42*\n\x19\x63om.binance.dex.api.protoB\x0bTransactionP\x01\x62\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'dex_pb2', _globals) +if _descriptor._USE_C_DESCRIPTORS == False: + _globals['DESCRIPTOR']._options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\031com.binance.dex.api.protoB\013TransactionP\001' + _globals['_STDTX']._serialized_start=26 + _globals['_STDTX']._serialized_end=111 + _globals['_STDSIGNATURE']._serialized_start=113 + _globals['_STDSIGNATURE']._serialized_end=215 + _globals['_STDSIGNATURE_PUBKEY']._serialized_start=207 + _globals['_STDSIGNATURE_PUBKEY']._serialized_end=215 + _globals['_NEWORDER']._serialized_start=218 + _globals['_NEWORDER']._serialized_end=359 + _globals['_CANCELORDER']._serialized_start=361 + _globals['_CANCELORDER']._serialized_end=421 + _globals['_TOKENFREEZE']._serialized_start=423 + _globals['_TOKENFREEZE']._serialized_end=482 + _globals['_TOKENUNFREEZE']._serialized_start=484 + _globals['_TOKENUNFREEZE']._serialized_end=545 + _globals['_TOKEN']._serialized_start=547 + _globals['_TOKEN']._serialized_end=585 + _globals['_INPUT']._serialized_start=587 + _globals['_INPUT']._serialized_end=646 + _globals['_OUTPUT']._serialized_start=648 + _globals['_OUTPUT']._serialized_end=708 + _globals['_SEND']._serialized_start=710 + _globals['_SEND']._serialized_end=790 + _globals['_VOTE']._serialized_start=792 + _globals['_VOTE']._serialized_end=850 # @@protoc_insertion_point(module_scope) diff --git a/binance_chain/utils/encode_utils.py b/binance_chain/utils/encode_utils.py index eb02c6b..0d049be 100644 --- a/binance_chain/utils/encode_utils.py +++ b/binance_chain/utils/encode_utils.py @@ -1,4 +1,3 @@ -import math from decimal import Decimal from typing import Union @@ -9,10 +8,10 @@ def encode_number(num: Union[float, Decimal]) -> int: :param num: number to encode """ - if type(num) == Decimal: + if type(num) is Decimal: return int(num * (Decimal(10) ** 8)) else: - return int(num * math.pow(10, 8)) + return int(round(num * 1e8)) def varint_encode(num): diff --git a/binance_chain/wallet.py b/binance_chain/wallet.py index 8df1498..0beaf51 100644 --- a/binance_chain/wallet.py +++ b/binance_chain/wallet.py @@ -4,7 +4,7 @@ from secp256k1 import PrivateKey from mnemonic import Mnemonic -from pywallet.utils.bip32 import Wallet as Bip32Wallet +from pycoin.symbols.btc import network from binance_chain.utils.segwit_addr import address_from_public_key, decode_address from binance_chain.environment import BinanceEnvironment @@ -23,7 +23,7 @@ class MnemonicLanguage(str, Enum): class BaseWallet: - HD_PATH = "44'/714'/0'/0/0" + HD_PATH = "44'/714'/0'/0/{id}" def __init__(self, env: Optional[BinanceEnvironment] = None): self._env = env or BinanceEnvironment.get_production_env() @@ -103,8 +103,21 @@ def sign_message(self, msg_bytes): class Wallet(BaseWallet): + """ + Usage example: - HD_PATH = "44'/714'/0'/0/0" + m = Wallet.create_random_mnemonic() # 12 words + p = 'my secret passphrase' # bip39 passphrase + + # Store and

somewhere safe + + wallet1 = Wallet.create_wallet_from_mnemonic(m, passphrase=p, child=0, env=testnet_env) + wallet2 = Wallet.create_wallet_from_mnemonic(m, passphrase=p, child=1, env=testnet_env) + ... + + """ + + HD_PATH = "44'/714'/0'/0/{id}" def __init__(self, private_key, env: Optional[BinanceEnvironment] = None): super().__init__(env) @@ -118,22 +131,39 @@ def create_random_wallet(cls, language: MnemonicLanguage = MnemonicLanguage.ENGL env: Optional[BinanceEnvironment] = None): """Create wallet with random mnemonic code - :return: + :return: initialised Wallet """ - m = Mnemonic(language.value) - phrase = m.generate() + phrase = cls.create_random_mnemonic(language) return cls.create_wallet_from_mnemonic(phrase, env=env) @classmethod - def create_wallet_from_mnemonic(cls, mnemonic: str, env: Optional[BinanceEnvironment] = None): - """Create wallet with random mnemonic code + def create_wallet_from_mnemonic(cls, mnemonic: str, + passphrase: Optional[str] = '', + child: Optional[int] = 0, + env: Optional[BinanceEnvironment] = None): + """Create wallet from mnemonic, passphrase and child wallet id - :return: + :return: initialised Wallet """ - seed = Mnemonic.to_seed(mnemonic) - new_wallet = Bip32Wallet.from_master_secret(seed=seed, network='BTC') - child = new_wallet.get_child_for_path(Wallet.HD_PATH) - return cls(child.get_private_key_hex().decode(), env=env) + if type(child) is not int: + raise TypeError("Child wallet id should be of type int") + + seed = Mnemonic.to_seed(mnemonic, passphrase) + new_wallet = network.keys.bip32_seed(seed) + child_wallet = new_wallet.subkey_for_path(Wallet.HD_PATH.format(id=child)) + # convert secret exponent (private key) int to hex, zero padded to 32 bytes + key_hex = format(child_wallet.secret_exponent(), '064x') + return cls(key_hex, env=env) + + @classmethod + def create_random_mnemonic(cls, language: MnemonicLanguage = MnemonicLanguage.ENGLISH): + """Create random mnemonic code + + :return: str, mnemonic phrase + """ + m = Mnemonic(language.value) + phrase = m.generate() + return phrase @property def private_key(self): diff --git a/docs/changelog.rst b/docs/changelog.rst index 142436e..946800b 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,6 +1,17 @@ Changelog ========= +v0.1.20 - 2019-06-29 +^^^^^^^^^^^^^^^^^^^^ + +**Added** + +- Multi Transfer Msg option + +**Fixed** + +- Response code of 0 for http requests + v0.1.19 - 2019-04-30 ^^^^^^^^^^^^^^^^^^^^ diff --git a/requirements.txt b/requirements.txt index 64a8a9b..e823b90 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,8 @@ -pywallet>=0.1.0 +pycoin>=0.90.20201031 requests>=2.21.0 aiohttp>=3.5.4 websockets>=7.0 secp256k1>=0.13.2 mnemonic>=0.18 -protobuf>=3.6.1 \ No newline at end of file +protobuf>=4.21.0 +ujson>=1.35 diff --git a/setup.cfg b/setup.cfg index e36f073..85c9b71 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,6 @@ -[bdist_wheel] -universal = 1 +[tool:pytest] +markers = + integration: requires a live Binance Chain node, not run in CI [pep8] ignore = E501 diff --git a/setup.py b/setup.py index 1358da2..0bf228b 100644 --- a/setup.py +++ b/setup.py @@ -24,8 +24,8 @@ def find_version(*file_paths): def install_requires(): requires = [ - 'pywallet>=0.1.0', 'requests>=2.21.0', 'websockets>=7.0', 'aiohttp>=3.5.4', - 'secp256k1>=0.13.2', 'protobuf>=3.6.1', 'mnemonic>=0.18', 'ujson>=1.35' + 'pycoin>=0.90.20201031', 'requests>=2.21.0', 'websockets>=7.0', 'aiohttp>=3.5.4', + 'secp256k1>=0.13.2', 'protobuf>=4.21.0', 'mnemonic>=0.18', 'ujson>=1.35' ] return requires @@ -33,9 +33,11 @@ def install_requires(): setup( name='python-binance-chain', version=find_version("binance_chain", "__init__.py"), - packages=find_packages(), + packages=find_packages(exclude=['tests', 'tests.*']), description='Binance Chain HTTP API python implementation', - url='https://github.com/sammchardy/python-binance-chain', + long_description=read('README.rst'), + long_description_content_type='text/x-rst', + url='https://github.com/mchardysam/python-binance-chain', author='Sam McHardy', license='MIT', author_email='', @@ -43,14 +45,17 @@ def install_requires(): extras_require={ 'ledger': ['btchip-python>=0.1.28', ], }, + python_requires='>=3.9', keywords='binance dex exchange rest api bitcoin ethereum btc eth bnb ledger', classifiers=[ 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', 'Operating System :: OS Independent', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10', + 'Programming Language :: Python :: 3.11', + 'Programming Language :: Python :: 3.12', 'Programming Language :: Python', 'Topic :: Software Development :: Libraries :: Python Modules', ], diff --git a/test-requirements.txt b/test-requirements.txt index 3772a5c..c2d7f77 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -3,9 +3,7 @@ flake8 mock pytest pytest-cov -pytest-pep8 pytest-asyncio -python-coveralls requests-mock tox -setuptools \ No newline at end of file +setuptools diff --git a/tests/test_client.py b/tests/test_client.py index 5cd7d7a..a51502c 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -10,6 +10,7 @@ from binance_chain.environment import BinanceEnvironment +@pytest.mark.integration class TestClient: @pytest.fixture @@ -160,6 +161,7 @@ def test_get_klines_url(self, httpclient): assert res == {"message": "success"} +@pytest.mark.integration class TestAsyncClient: @pytest.fixture @@ -167,7 +169,6 @@ def env(self): return BinanceEnvironment.get_testnet_env() @pytest.fixture - @pytest.mark.asyncio async def httpclient(self, env, event_loop): return await AsyncHttpApiClient.create(loop=event_loop, env=env, requests_params={'timeout': 1}) diff --git a/tests/test_depthcache.py b/tests/test_depthcache.py index e5dbaed..14fd181 100644 --- a/tests/test_depthcache.py +++ b/tests/test_depthcache.py @@ -95,6 +95,7 @@ def test_sorted_asks(self): assert len(dc.get_bids()) == 0 +@pytest.mark.integration class TestDepthCacheConnection: @pytest.fixture() diff --git a/tests/test_rpc_client.py b/tests/test_rpc_client.py index a7aebaf..9e22acb 100644 --- a/tests/test_rpc_client.py +++ b/tests/test_rpc_client.py @@ -10,6 +10,7 @@ from binance_chain.constants import PeerType +@pytest.mark.integration class TestHttpRpcClient: peers = None @@ -49,6 +50,7 @@ def test_get_block(self, rpcclient): assert rpcclient.get_block(10000) +@pytest.mark.integration class TestAsyncHttpRpcClient: peers = None @@ -75,7 +77,6 @@ def listen_address(self, httpclient): self.peers = httpclient.get_peers(peer_type=PeerType.NODE) return self.peers[0]['listen_addr'] - @pytest.mark.asyncio @pytest.fixture async def rpcclient(self, listen_address): return await AsyncHttpRpcClient.create(endpoint_url=listen_address) diff --git a/tests/test_rpc_pooled.py b/tests/test_rpc_pooled.py index 842bfdc..cfd27ed 100644 --- a/tests/test_rpc_pooled.py +++ b/tests/test_rpc_pooled.py @@ -4,6 +4,7 @@ from binance_chain.environment import BinanceEnvironment +@pytest.mark.integration class TestRpcPooled: @pytest.fixture() diff --git a/tests/test_rpc_websockets.py b/tests/test_rpc_websockets.py index 20d9adc..65f687d 100644 --- a/tests/test_rpc_websockets.py +++ b/tests/test_rpc_websockets.py @@ -6,6 +6,7 @@ from binance_chain.environment import BinanceEnvironment +@pytest.mark.integration class TestRpcWebsockets: peers = None diff --git a/tests/test_signing.py b/tests/test_signing.py index 22084e0..149a2fe 100644 --- a/tests/test_signing.py +++ b/tests/test_signing.py @@ -12,6 +12,7 @@ def test_initialise(self, _mocker): assert HttpApiSigningClient('https://binance-signing-service.com', 'sam', 'mypass') +@pytest.mark.integration class TestAsyncHttpSigningClient: @mock.patch('binance_chain.signing.http.AsyncHttpApiSigningClient.authenticate') diff --git a/tests/test_wallet.py b/tests/test_wallet.py index 3e77586..c66a69a 100644 --- a/tests/test_wallet.py +++ b/tests/test_wallet.py @@ -27,6 +27,7 @@ def test_wallet_create_from_private_key(self, private_key, env): assert wallet.public_key_hex == b'02cce2ee4e37dc8c65d6445c966faf31ebfe578a90695138947ee7cab8ae9a2c08' assert wallet.address == 'tbnb10a6kkxlf823w9lwr6l9hzw4uyphcw7qzrud5rr' + @pytest.mark.integration def test_wallet_initialise(self, private_key, env): wallet = Wallet(private_key=private_key, env=env) @@ -45,6 +46,25 @@ def test_initialise_from_mnemonic(self, private_key, mnemonic, env): assert wallet.public_key_hex == b'02cce2ee4e37dc8c65d6445c966faf31ebfe578a90695138947ee7cab8ae9a2c08' assert wallet.address == 'tbnb10a6kkxlf823w9lwr6l9hzw4uyphcw7qzrud5rr' + def test_initialise_from_mnemonic_child_id(self, private_key, mnemonic, env): + # child 0 is the default and must match the plain mnemonic derivation + assert Wallet.create_wallet_from_mnemonic(mnemonic, child=0, env=env).private_key == private_key + + # each child id must derive its own distinct key + child_1 = Wallet.create_wallet_from_mnemonic(mnemonic, child=1, env=env) + assert child_1.private_key == 'ea7123859bc54d9eeac1aa1af48093569efa781535571591d9b4b76fd6dd26f2' + assert child_1.address == 'tbnb1yqprw3vkaw72vaw4yws0adxq6qw5nunzny0tvt' + + child_2 = Wallet.create_wallet_from_mnemonic(mnemonic, child=2, env=env) + assert child_2.private_key == 'e5319a7a9a2d961879b3a5960922535875bbd7c9aa7e0627f6d9a8112bffeabb' + assert child_2.address == 'tbnb17y34wwxnl9drc6rc30djjpspkrjuhhnk7ffr4y' + + def test_initialise_from_mnemonic_private_key_is_padded(self, mnemonic, env): + # secret exponents shorter than 32 bytes must still hex encode to 64 chars + for child in range(25): + wallet = Wallet.create_wallet_from_mnemonic(mnemonic, child=child, env=env) + assert len(wallet.private_key) == 64 + def test_initialise_from_random_mnemonic(self, env): wallet = Wallet.create_random_wallet(env=env) @@ -73,6 +93,7 @@ def test_wallet_sequence_decrement(self, private_key, env): assert wallet.sequence == 99 + @pytest.mark.integration def test_wallet_reload_sequence(self, private_key, env): wallet = Wallet(private_key=private_key, env=env) @@ -85,6 +106,7 @@ def test_wallet_reload_sequence(self, private_key, env): assert wallet.sequence == account_sequence + @pytest.mark.integration def test_generate_order_id(self, private_key, env): wallet = Wallet(private_key=private_key, env=env) diff --git a/tests/test_websockets.py b/tests/test_websockets.py index 2d25726..a787461 100644 --- a/tests/test_websockets.py +++ b/tests/test_websockets.py @@ -5,6 +5,7 @@ from binance_chain.environment import BinanceEnvironment +@pytest.mark.integration class TestWebsockets: @pytest.fixture() diff --git a/tox.ini b/tox.ini index d724f4a..907158d 100644 --- a/tox.ini +++ b/tox.ini @@ -1,24 +1,16 @@ [tox] -envlist = py36, py37 +envlist = py39, py310, py311, py312, flake8 [testenv] deps = -rtest-requirements.txt -rrequirements.txt -commands = py.test -v tests/ --doctest-modules --cov binance_chain --cov-report term-missing -passenv = - TRAVIS - TRAVIS_BRANCH - TRAVIS_JOB_ID +commands = py.test -v tests/ -m "not integration" --cov binance_chain --cov-report term-missing [testenv:flake8] commands = flake8 --exclude binance_chain/protobuf/dex_pb2.py binance_chain/ deps = flake8 -[travis] -python = - 3.6: py36, flake8 - [flake8] exclude = .git,