diff --git a/binance/async_client.py b/binance/async_client.py index a82b9cf8..94939ff2 100644 --- a/binance/async_client.py +++ b/binance/async_client.py @@ -4,6 +4,7 @@ from urllib.parse import urlencode import time import aiohttp +import yarl from binance.enums import HistoricalKlinesType from binance.exceptions import ( @@ -103,8 +104,14 @@ async def _request( ): kwargs = self._get_request_kwargs(method, signed, force_params, **kwargs) + if method == 'get': + # url encode the query string + if 'params' in kwargs: + uri = f"{uri}?{kwargs['params']}" + kwargs.pop('params') + async with getattr(self.session, method)( - uri, proxy=self.https_proxy, **kwargs + yarl.URL(uri, encoded=True), proxy=self.https_proxy, **kwargs ) as response: self.response = response return await self._handle_response(response) diff --git a/binance/base_client.py b/binance/base_client.py index 9c6bd5c3..34578bd3 100644 --- a/binance/base_client.py +++ b/binance/base_client.py @@ -10,6 +10,7 @@ from Crypto.PublicKey import RSA, ECC from Crypto.Hash import SHA256 from Crypto.Signature import pkcs1_15, eddsa +import urllib.parse as _urlencode from operator import itemgetter from urllib.parse import urlencode @@ -59,6 +60,8 @@ class BaseClient: REQUEST_TIMEOUT: float = 10 + REQUEST_RECVWINDOW: int = 10000 # 10 seconds + SYMBOL_TYPE_SPOT = "SPOT" ORDER_STATUS_NEW = "NEW" @@ -300,13 +303,21 @@ def _rsa_signature(self, query_string: str): assert self.PRIVATE_KEY h = SHA256.new(query_string.encode("utf-8")) signature = pkcs1_15.new(self.PRIVATE_KEY).sign(h) # type: ignore - return b64encode(signature).decode() + res = b64encode(signature).decode() + # return self.encode_uri_component(res) + return res + + @staticmethod + def encode_uri_component(uri, safe="~()*!.'"): + return _urlencode.quote(uri, safe=safe) def _ed25519_signature(self, query_string: str): assert self.PRIVATE_KEY - return b64encode( + res = b64encode( eddsa.new(self.PRIVATE_KEY, "rfc8032").sign(query_string.encode()) ).decode() # type: ignore + # return self.encode_uri_component(res) + return res def _hmac_signature(self, query_string: str) -> str: assert self.API_SECRET, "API Secret required for private endpoints" @@ -317,7 +328,7 @@ def _hmac_signature(self, query_string: str) -> str: ) return m.hexdigest() - def _generate_signature(self, data: Dict) -> str: + def _generate_signature(self, data: Dict, uri_encode=True) -> str: sig_func = self._hmac_signature if self.PRIVATE_KEY: if self._is_rsa: @@ -325,7 +336,8 @@ def _generate_signature(self, data: Dict) -> str: else: sig_func = self._ed25519_signature query_string = "&".join([f"{d[0]}={d[1]}" for d in self._order_params(data)]) - return sig_func(query_string) + res = sig_func(query_string) + return self.encode_uri_component(res) if uri_encode else res def _sign_ws_params(self, params, signature_func): if "signature" in params: @@ -445,6 +457,8 @@ def _get_request_kwargs( kwargs["data"]["timestamp"] = int( time.time() * 1000 + self.timestamp_offset ) + if self.REQUEST_RECVWINDOW: + kwargs["data"]["recvWindow"] = self.REQUEST_RECVWINDOW kwargs["data"]["signature"] = self._generate_signature(kwargs["data"]) # sort get and post params to match signature order diff --git a/tests/test_cryptography.py b/tests/test_cryptography.py index 8048fda3..2bafdcf6 100644 --- a/tests/test_cryptography.py +++ b/tests/test_cryptography.py @@ -46,7 +46,7 @@ def test_encryption(): private_key_pass=case["password"], ping=False, ) - signature = client._generate_signature(data) + signature = client._generate_signature(data, False) assert signature == case["expected_signature"], ( f"Test failed: {case['description']}" )