Skip to content

Commit e96cf02

Browse files
authored
Merge pull request #210 from InjectiveLabs/fix/remove_pysha3_dependency
Fix/remove pysha3 dependency
2 parents 26554d9 + 72b2f8a commit e96cf02

File tree

8 files changed

+619
-118
lines changed

8 files changed

+619
-118
lines changed

Pipfile

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,28 @@ verify_ssl = true
44
name = "pypi"
55

66
[packages]
7-
protobuf = "*"
8-
grpcio-tools = "*"
9-
grpcio = "*"
10-
asyncio = "*"
7+
aiocron = "*"
118
aiohttp = "*"
12-
ecdsa = "*"
9+
asyncio = "*"
1310
bech32 = "*"
14-
mnemonic = "*"
15-
hdwallets = "*"
1611
bip32 = "*"
17-
requests = "*"
18-
eip712_structs = "*"
1912
coincurve = "*"
20-
aiocron = "*"
21-
websockets = "*"
13+
ecdsa = "*"
14+
eip712 = "*"
15+
grpcio = "*"
16+
grpcio-tools = "*"
17+
hdwallets = "*"
18+
mnemonic = "*"
19+
protobuf = "*"
20+
requests = "*"
21+
safe-pysha3 = "*"
2222
urllib3 = "<2"
23+
websockets = "*"
2324

2425
[dev-packages]
2526
pytest = "*"
2627
pytest-asyncio = "*"
28+
requests-mock = "*"
2729

2830
[requires]
29-
python_version = "3.9"
31+
python_version = "3"

Pipfile.lock

Lines changed: 446 additions & 43 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,9 @@ make tests
8787
```
8888

8989
### Changelogs
90+
**0.7**
91+
* Removed references to pysha3 library (and also eip712-struct that required it) and replaced it with other implementation to allow the project to work with Python 3.11
92+
9093
**0.6.5**
9194
* Removed `k8s` from the list of supported mainnet nodes (`lb` should be used instead)
9295

pyinjective/orderhash.py

Lines changed: 46 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,48 @@
1-
import sha3
21
import requests
32
from decimal import Decimal
4-
from eip712_structs import make_domain, EIP712Struct, String
5-
6-
class OrderInfo(EIP712Struct):
7-
SubaccountId = String()
8-
FeeRecipient = String()
9-
Price = String()
10-
Quantity = String()
11-
12-
class SpotOrder(EIP712Struct):
13-
MarketId = String()
14-
OrderInfo = OrderInfo
15-
Salt = String()
16-
OrderType = String()
17-
TriggerPrice = String()
18-
19-
class DerivativeOrder(EIP712Struct):
20-
MarketId = String()
21-
OrderInfo = OrderInfo
22-
OrderType = String()
23-
Margin = String()
24-
TriggerPrice = String()
25-
Salt = String()
26-
27-
EIP712_domain = make_domain(
28-
name='Injective Protocol',
29-
version='2.0.0',
30-
chainId=888,
31-
verifyingContract='0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC',
32-
salt='0x0000000000000000000000000000000000000000000000000000000000000000'
33-
)
34-
35-
domain_separator = EIP712_domain.hash_struct()
3+
4+
from eip712.messages import EIP712Message, EIP712Type
5+
from eth_account.messages import _hash_eip191_message as hash_eip191_message
6+
from hexbytes import HexBytes
7+
8+
9+
class OrderInfo(EIP712Type):
10+
SubaccountId: "string"
11+
FeeRecipient: "string"
12+
Price: "string"
13+
Quantity: "string"
14+
15+
16+
class SpotOrder(EIP712Message):
17+
_name_ = "Injective Protocol"
18+
_version_ = "2.0.0"
19+
_chainId_ = 888
20+
_verifyingContract_ = "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
21+
_salt_ = HexBytes("0x0000000000000000000000000000000000000000000000000000000000000000")
22+
23+
MarketId: "string"
24+
OrderInfo: OrderInfo
25+
Salt: "string"
26+
OrderType: "string"
27+
TriggerPrice: "string"
28+
29+
30+
class DerivativeOrder(EIP712Message):
31+
_name_ = "Injective Protocol"
32+
_version_ = "2.0.0"
33+
_chainId_ = 888
34+
_verifyingContract_ = "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
35+
_salt_ = HexBytes("0x0000000000000000000000000000000000000000000000000000000000000000")
36+
37+
MarketId: "string"
38+
OrderInfo: OrderInfo
39+
OrderType: "string"
40+
Margin: "string"
41+
TriggerPrice: "string"
42+
Salt: "string"
43+
44+
45+
# domain_separator = EIP712_domain.hash_struct()
3646
order_type_dict = {0: '\x00', 1: '\x01', 2: '\x02', 3: '\x03', 4: '\x04', 5: '\x05', 6: '\x06', 7: '\x07', 8: '\x08'}
3747

3848
class OrderHashResponse:
@@ -127,8 +137,6 @@ def build_eip712_msg(order, nonce):
127137
)
128138

129139
def hash_order(msg):
130-
typed_data_hash = msg.hash_struct()
131-
typed_bytes = b'\x19\x01' + domain_separator + typed_data_hash
132-
keccak256 = sha3.keccak_256()
133-
keccak256.update(typed_bytes)
134-
return '0x' + keccak256.hexdigest()
140+
signable_message = msg.signable_message
141+
hex_digest = hash_eip191_message(signable_message=signable_message).hex()
142+
return "0x" + hex_digest

pyinjective/wallet.py

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
import sys
21
import sha3
32
import hashlib
4-
import bech32
53
import aiohttp
64
import json
75
import requests
86
from typing import Tuple
7+
98
from bech32 import bech32_encode, bech32_decode, convertbits
109
from bip32 import BIP32
1110
from ecdsa import SigningKey, VerifyingKey, SECP256k1, BadSignatureError
@@ -25,6 +24,7 @@
2524

2625
DEFAULT_DERIVATION_PATH = "m/44'/60'/0'/0/0"
2726

27+
2828
class PrivateKey:
2929
"""
3030
Class for wrapping SigningKey that is used for signature creation and public key derivation.
@@ -99,9 +99,8 @@ def sign(self, msg: bytes) -> bytes:
9999
100100
:return: a signature of this private key over the given message
101101
"""
102-
# return self.signing_key.sign_deterministic(msg, hashfunc=hashlib.sha256, sigencode=sigencode_string_canonize)
103-
return self.signing_key.sign_deterministic(msg, hashfunc=sha3.keccak_256, sigencode=sigencode_string_canonize)
104102

103+
return self.signing_key.sign_deterministic(msg, hashfunc=sha3.keccak_256, sigencode=sigencode_string_canonize)
105104

106105
class PublicKey:
107106
"""
@@ -174,15 +173,10 @@ def to_cons_bech32(self) -> str:
174173

175174
def to_address(self) -> "Address":
176175
"""Return address instance from this public key"""
177-
# pubkey = self.verify_key.to_string("compressed")
178-
# s = hashlib.new("sha256", pubkey).digest()
179-
# r = hashlib.new("ripemd160", s).digest()
180-
# return Address(r)
181-
182176
pubkey = self.verify_key.to_string("uncompressed")
183-
k = sha3.keccak_256()
184-
k.update(pubkey[1:])
185-
addr = k.digest()[12:]
177+
keccak_hash = sha3.keccak_256()
178+
keccak_hash.update(pubkey[1:])
179+
addr = keccak_hash.digest()[12:]
186180
return Address(addr)
187181

188182
def verify(self, msg: bytes, sig: bytes) -> bool:

setup.py

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,30 +16,33 @@
1616
URL = "https://github.com/InjectiveLabs/sdk-python"
1717
1818
AUTHOR = "Injective Labs"
19-
REQUIRES_PYTHON = ">=3.7, <3.11"
20-
VERSION = "0.6.5"
19+
REQUIRES_PYTHON = ">=3.9"
20+
VERSION = "0.7"
2121

2222
REQUIRED = [
23-
"protobuf",
24-
"grpcio-tools",
25-
"grpcio",
26-
"asyncio",
2723
"aiohttp",
28-
"ecdsa",
24+
"aiocron",
25+
"asyncio",
2926
"bech32",
30-
"mnemonic",
31-
"hdwallets",
3227
"bip32",
33-
"requests",
34-
"eip712_structs",
3528
"coincurve",
36-
"aiocron",
37-
"websockets"
29+
"ecdsa",
30+
"eip712",
31+
"grpcio",
32+
"grpcio-tools",
33+
"hdwallets",
34+
"mnemonic",
35+
"protobuf",
36+
"requests",
37+
"safe-pysha3",
38+
"urllib3",
39+
"websockets",
3840
]
3941

4042
DEV_REQUIRED = [
4143
"pytest",
4244
"pytest-asyncio",
45+
"request-mock",
4346
]
4447

4548
# The rest you shouldn't have to touch too much :)

tests/test_orderhash.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
from pyinjective import PrivateKey
2+
from pyinjective.composer import Composer
3+
from pyinjective.constant import Network
4+
from pyinjective.orderhash import OrderHashManager
5+
6+
7+
class TestOrderHashManager:
8+
9+
def test_spot_order_hash(self, requests_mock):
10+
network = Network.devnet()
11+
composer = Composer(network=network.string())
12+
priv_key = PrivateKey.from_mnemonic("test one few words")
13+
pub_key = priv_key.to_public_key()
14+
address = pub_key.to_address()
15+
16+
subaccount_id = address.get_subaccount_id(index=0)
17+
18+
url = network.lcd_endpoint + "/injective/exchange/v1beta1/exchange/" + subaccount_id
19+
requests_mock.get(url, json={"nonce": 0})
20+
order_hash_manager = OrderHashManager(
21+
address=address,
22+
network=network,
23+
subaccount_indexes=[0]
24+
)
25+
26+
spot_market_id = "0xa508cb32923323679f29a032c70342c147c17d0145625922b0ef22e955c844c0"
27+
fee_recipient = "inj1hkhdaj2a2clmq5jq6mspsggqs32vynpk228q3r"
28+
29+
spot_orders = [
30+
composer.SpotOrder(
31+
market_id=spot_market_id,
32+
subaccount_id=subaccount_id,
33+
fee_recipient=fee_recipient,
34+
price=0.524,
35+
quantity=0.01,
36+
is_buy=True,
37+
is_po=False
38+
),
39+
composer.SpotOrder(
40+
market_id=spot_market_id,
41+
subaccount_id=subaccount_id,
42+
fee_recipient=fee_recipient,
43+
price=27.92,
44+
quantity=0.01,
45+
is_buy=False,
46+
is_po=False
47+
),
48+
]
49+
50+
order_hashes_response = order_hash_manager.compute_order_hashes(
51+
spot_orders=spot_orders,
52+
derivative_orders=[],
53+
subaccount_index=0
54+
)
55+
56+
assert (len(order_hashes_response.spot) == 2)
57+
assert (len(order_hashes_response.derivative) == 0)
58+
assert (order_hashes_response.spot[0] == "0x6b1e4d1fb3012735dd5e386175eb4541c024e0d8dbfeb452767b973d70ae0924")
59+
assert (order_hashes_response.spot[1] == "0xb36146f913955d989269732d167ec554e6d0d544411d82d7f86aef18350b252b")

tests/test_wallet.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
from eth_hash.auto import keccak
2+
3+
from pyinjective import PrivateKey, Address
4+
5+
6+
class TestPrivateKey:
7+
8+
def test_private_key_signature_generation(self):
9+
private_key = PrivateKey.from_mnemonic("test mnemonic never use other places")
10+
11+
signature = private_key.sign(msg="test message".encode())
12+
expected_signature = b'\x8f\xae\xcb#z\x9a+\x12\x88\xea\xb5xZ"\x8f\x98y\xb8\x97\xa7F\xd5\xdd\x15s\x05;\x04\x1d\xbaY|rw\x8b\xbb\x19\xfc\x8e\x15\x8b\xf1\x18\x08\xba\xc7\x15\xed\xb0\xee\x95\x0e|Ch\x7f\xaf\x9cH\xc6\x9f\xbf\x14\xa5'
13+
14+
assert (expected_signature == signature)
15+
16+
17+
class TestPublicKey:
18+
19+
def test_convert_public_key_to_address(self):
20+
private_key = PrivateKey.from_mnemonic("test mnemonic never use other places")
21+
public_key = private_key.to_public_key()
22+
address = public_key.to_address()
23+
24+
key = public_key.verify_key.to_string("uncompressed")
25+
hashed_value = keccak(key[1:])
26+
expected_address = Address(hashed_value[12:])
27+
28+
assert (expected_address == address)
29+

0 commit comments

Comments
 (0)