feat(order_utils.py) generate_order_hash_hex() (#1234)
This commit is contained in:
parent
5c21d3f6af
commit
7b4f63a39c
@ -136,5 +136,28 @@ describe('signTypedDataUtils', () => {
|
|||||||
const hashHex = `0x${hash}`;
|
const hashHex = `0x${hash}`;
|
||||||
expect(hashHex).to.be.eq(orderSignTypedDataHashHex);
|
expect(hashHex).to.be.eq(orderSignTypedDataHashHex);
|
||||||
});
|
});
|
||||||
|
it('creates a hash of an uninitialized order', () => {
|
||||||
|
const uninitializedOrder = {
|
||||||
|
...orderSignTypedData,
|
||||||
|
message: {
|
||||||
|
makerAddress: '0x0000000000000000000000000000000000000000',
|
||||||
|
takerAddress: '0x0000000000000000000000000000000000000000',
|
||||||
|
makerAssetAmount: 0,
|
||||||
|
takerAssetAmount: 0,
|
||||||
|
expirationTimeSeconds: 0,
|
||||||
|
makerFee: 0,
|
||||||
|
takerFee: 0,
|
||||||
|
feeRecipientAddress: '0x0000000000000000000000000000000000000000',
|
||||||
|
senderAddress: '0x0000000000000000000000000000000000000000',
|
||||||
|
salt: 0,
|
||||||
|
makerAssetData: '0x0000000000000000000000000000000000000000',
|
||||||
|
takerAssetData: '0x0000000000000000000000000000000000000000',
|
||||||
|
exchangeAddress: '0x0000000000000000000000000000000000000000',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const hash = signTypedDataUtils.generateTypedDataHash(uninitializedOrder).toString('hex');
|
||||||
|
const hashHex = `0x${hash}`;
|
||||||
|
expect(hashHex).to.be.eq('0xfaa49b35faeb9197e9c3ba7a52075e6dad19739549f153b77dfcf59408a4b422');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -160,7 +160,13 @@ setup(
|
|||||||
"publish": PublishCommand,
|
"publish": PublishCommand,
|
||||||
"ganache": GanacheCommand,
|
"ganache": GanacheCommand,
|
||||||
},
|
},
|
||||||
install_requires=["eth-abi", "eth_utils", "mypy_extensions", "web3"],
|
install_requires=[
|
||||||
|
"eth-abi",
|
||||||
|
"eth_utils",
|
||||||
|
"ethereum",
|
||||||
|
"mypy_extensions",
|
||||||
|
"web3",
|
||||||
|
],
|
||||||
extras_require={
|
extras_require={
|
||||||
"dev": [
|
"dev": [
|
||||||
"bandit",
|
"bandit",
|
||||||
|
@ -10,8 +10,8 @@ from typing import Any, List
|
|||||||
|
|
||||||
from mypy_extensions import TypedDict
|
from mypy_extensions import TypedDict
|
||||||
|
|
||||||
from eth_abi import encode_abi
|
|
||||||
from web3 import Web3
|
from web3 import Web3
|
||||||
|
from eth_abi import encode_abi
|
||||||
|
|
||||||
from .type_assertions import assert_is_string, assert_is_list
|
from .type_assertions import assert_is_string, assert_is_list
|
||||||
|
|
||||||
|
@ -9,3 +9,157 @@ just this purpose. To start it: ``docker run -d -p 8545:8545 0xorg/ganache-cli
|
|||||||
--networkId 50 -m "concert load couple harbor equip island argue ramp clarify
|
--networkId 50 -m "concert load couple harbor equip island argue ramp clarify
|
||||||
fence smart topic"``.
|
fence smart topic"``.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
from typing import Dict
|
||||||
|
from pkg_resources import resource_string
|
||||||
|
|
||||||
|
from mypy_extensions import TypedDict
|
||||||
|
|
||||||
|
from eth_utils import is_address, keccak, to_checksum_address, to_bytes
|
||||||
|
from web3 import Web3
|
||||||
|
from web3.utils import datatypes
|
||||||
|
import web3.exceptions
|
||||||
|
|
||||||
|
|
||||||
|
class Constants: # pylint: disable=too-few-public-methods
|
||||||
|
"""Static data used by order utilities."""
|
||||||
|
|
||||||
|
contract_name_to_abi = {
|
||||||
|
"Exchange": json.loads(
|
||||||
|
resource_string(
|
||||||
|
"zero_ex.contract_artifacts", "artifacts/Exchange.json"
|
||||||
|
)
|
||||||
|
)["compilerOutput"]["abi"]
|
||||||
|
}
|
||||||
|
|
||||||
|
network_to_exchange_addr: Dict[str, str] = {
|
||||||
|
"1": "0x4f833a24e1f95d70f028921e27040ca56e09ab0b",
|
||||||
|
"3": "0x4530c0483a1633c7a1c97d2c53721caff2caaaaf",
|
||||||
|
"42": "0x35dd2932454449b14cee11a94d3674a936d5d7b2",
|
||||||
|
"50": "0x48bacb9266a570d521063ef5dd96e61686dbe788",
|
||||||
|
}
|
||||||
|
|
||||||
|
null_address = "0x0000000000000000000000000000000000000000"
|
||||||
|
|
||||||
|
eip191_header = b"\x19\x01"
|
||||||
|
|
||||||
|
eip712_domain_separator_schema_hash = keccak(
|
||||||
|
b"EIP712Domain(string name,string version,address verifyingContract)"
|
||||||
|
)
|
||||||
|
|
||||||
|
eip712_domain_struct_header = (
|
||||||
|
eip712_domain_separator_schema_hash
|
||||||
|
+ keccak(b"0x Protocol")
|
||||||
|
+ keccak(b"2")
|
||||||
|
)
|
||||||
|
|
||||||
|
eip712_order_schema_hash = keccak(
|
||||||
|
b"Order("
|
||||||
|
+ b"address makerAddress,"
|
||||||
|
+ b"address takerAddress,"
|
||||||
|
+ b"address feeRecipientAddress,"
|
||||||
|
+ b"address senderAddress,"
|
||||||
|
+ b"uint256 makerAssetAmount,"
|
||||||
|
+ b"uint256 takerAssetAmount,"
|
||||||
|
+ b"uint256 makerFee,"
|
||||||
|
+ b"uint256 takerFee,"
|
||||||
|
+ b"uint256 expirationTimeSeconds,"
|
||||||
|
+ b"uint256 salt,"
|
||||||
|
+ b"bytes makerAssetData,"
|
||||||
|
+ b"bytes takerAssetData"
|
||||||
|
+ b")"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Order(TypedDict): # pylint: disable=too-many-instance-attributes
|
||||||
|
"""Object representation of a 0x order."""
|
||||||
|
|
||||||
|
maker_address: str
|
||||||
|
taker_address: str
|
||||||
|
fee_recipient_address: str
|
||||||
|
sender_address: str
|
||||||
|
maker_asset_amount: int
|
||||||
|
taker_asset_amount: int
|
||||||
|
maker_fee: int
|
||||||
|
taker_fee: int
|
||||||
|
expiration_time_seconds: int
|
||||||
|
salt: int
|
||||||
|
maker_asset_data: str
|
||||||
|
taker_asset_data: str
|
||||||
|
|
||||||
|
|
||||||
|
def make_empty_order() -> Order:
|
||||||
|
"""Construct an empty order."""
|
||||||
|
return {
|
||||||
|
"maker_address": Constants.null_address,
|
||||||
|
"taker_address": Constants.null_address,
|
||||||
|
"sender_address": Constants.null_address,
|
||||||
|
"fee_recipient_address": Constants.null_address,
|
||||||
|
"maker_asset_data": Constants.null_address,
|
||||||
|
"taker_asset_data": Constants.null_address,
|
||||||
|
"salt": 0,
|
||||||
|
"maker_fee": 0,
|
||||||
|
"taker_fee": 0,
|
||||||
|
"maker_asset_amount": 0,
|
||||||
|
"taker_asset_amount": 0,
|
||||||
|
"expiration_time_seconds": 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def generate_order_hash_hex(order: Order, exchange_address: str) -> str:
|
||||||
|
# docstring considered all one line by pylint: disable=line-too-long
|
||||||
|
"""Calculate the hash of the given order as a hexadecimal string.
|
||||||
|
|
||||||
|
>>> generate_order_hash_hex(
|
||||||
|
... {
|
||||||
|
... 'maker_address': "0x0000000000000000000000000000000000000000",
|
||||||
|
... 'taker_address': "0x0000000000000000000000000000000000000000",
|
||||||
|
... 'fee_recipient_address': "0x0000000000000000000000000000000000000000",
|
||||||
|
... 'sender_address': "0x0000000000000000000000000000000000000000",
|
||||||
|
... 'maker_asset_amount': 1000000000000000000,
|
||||||
|
... 'taker_asset_amount': 1000000000000000000,
|
||||||
|
... 'maker_fee': 0,
|
||||||
|
... 'taker_fee': 0,
|
||||||
|
... 'expiration_time_seconds': 12345,
|
||||||
|
... 'salt': 12345,
|
||||||
|
... 'maker_asset_data': "0000000000000000000000000000000000000000",
|
||||||
|
... 'taker_asset_data': "0000000000000000000000000000000000000000",
|
||||||
|
... },
|
||||||
|
... exchange_address="0x0000000000000000000000000000000000000000",
|
||||||
|
... )
|
||||||
|
'55eaa6ec02f3224d30873577e9ddd069a288c16d6fb407210eecbc501fa76692'
|
||||||
|
""" # noqa: E501 (line too long)
|
||||||
|
# TODO: use JSON schema validation to validate order. pylint: disable=fixme
|
||||||
|
def pad_20_bytes_to_32(twenty_bytes: bytes):
|
||||||
|
return bytes(12) + twenty_bytes
|
||||||
|
|
||||||
|
def int_to_32_big_endian_bytes(i: int):
|
||||||
|
return i.to_bytes(32, byteorder="big")
|
||||||
|
|
||||||
|
eip712_domain_struct_hash = keccak(
|
||||||
|
Constants.eip712_domain_struct_header
|
||||||
|
+ pad_20_bytes_to_32(to_bytes(hexstr=exchange_address))
|
||||||
|
)
|
||||||
|
|
||||||
|
eip712_order_struct_hash = keccak(
|
||||||
|
Constants.eip712_order_schema_hash
|
||||||
|
+ pad_20_bytes_to_32(to_bytes(hexstr=order["maker_address"]))
|
||||||
|
+ pad_20_bytes_to_32(to_bytes(hexstr=order["taker_address"]))
|
||||||
|
+ pad_20_bytes_to_32(to_bytes(hexstr=order["fee_recipient_address"]))
|
||||||
|
+ pad_20_bytes_to_32(to_bytes(hexstr=order["sender_address"]))
|
||||||
|
+ int_to_32_big_endian_bytes(order["maker_asset_amount"])
|
||||||
|
+ int_to_32_big_endian_bytes(order["taker_asset_amount"])
|
||||||
|
+ int_to_32_big_endian_bytes(order["maker_fee"])
|
||||||
|
+ int_to_32_big_endian_bytes(order["taker_fee"])
|
||||||
|
+ int_to_32_big_endian_bytes(order["expiration_time_seconds"])
|
||||||
|
+ int_to_32_big_endian_bytes(order["salt"])
|
||||||
|
+ keccak(to_bytes(hexstr=order["maker_asset_data"]))
|
||||||
|
+ keccak(to_bytes(hexstr=order["taker_asset_data"]))
|
||||||
|
)
|
||||||
|
|
||||||
|
return keccak(
|
||||||
|
Constants.eip191_header
|
||||||
|
+ eip712_domain_struct_hash
|
||||||
|
+ eip712_order_struct_hash
|
||||||
|
).hex()
|
||||||
|
@ -1,30 +1,16 @@
|
|||||||
"""Signature utilities."""
|
"""Signature utilities."""
|
||||||
|
|
||||||
from typing import Dict, Tuple
|
from typing import Tuple
|
||||||
import json
|
|
||||||
from pkg_resources import resource_string
|
|
||||||
|
|
||||||
from eth_utils import is_address, to_checksum_address
|
from eth_utils import is_address, to_checksum_address
|
||||||
from web3 import Web3
|
from web3 import Web3
|
||||||
import web3.exceptions
|
import web3.exceptions
|
||||||
from web3.utils import datatypes
|
from web3.utils import datatypes
|
||||||
|
|
||||||
|
from zero_ex.order_utils import Constants
|
||||||
from zero_ex.dev_utils.type_assertions import assert_is_hex_string
|
from zero_ex.dev_utils.type_assertions import assert_is_hex_string
|
||||||
|
|
||||||
|
|
||||||
# prefer `black` formatting. pylint: disable=C0330
|
|
||||||
EXCHANGE_ABI = json.loads(
|
|
||||||
resource_string("zero_ex.contract_artifacts", "artifacts/Exchange.json")
|
|
||||||
)["compilerOutput"]["abi"]
|
|
||||||
|
|
||||||
network_to_exchange_addr: Dict[str, str] = {
|
|
||||||
"1": "0x4f833a24e1f95d70f028921e27040ca56e09ab0b",
|
|
||||||
"3": "0x4530c0483a1633c7a1c97d2c53721caff2caaaaf",
|
|
||||||
"42": "0x35dd2932454449b14cee11a94d3674a936d5d7b2",
|
|
||||||
"50": "0x48bacb9266a570d521063ef5dd96e61686dbe788",
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# prefer `black` formatting. pylint: disable=C0330
|
# prefer `black` formatting. pylint: disable=C0330
|
||||||
def is_valid_signature(
|
def is_valid_signature(
|
||||||
provider: Web3.HTTPProvider, data: str, signature: str, signer_address: str
|
provider: Web3.HTTPProvider, data: str, signature: str, signer_address: str
|
||||||
@ -63,10 +49,11 @@ def is_valid_signature(
|
|||||||
web3_instance = Web3(provider)
|
web3_instance = Web3(provider)
|
||||||
# false positive from pylint: disable=no-member
|
# false positive from pylint: disable=no-member
|
||||||
network_id = web3_instance.net.version
|
network_id = web3_instance.net.version
|
||||||
contract_address = network_to_exchange_addr[network_id]
|
contract_address = Constants.network_to_exchange_addr[network_id]
|
||||||
# false positive from pylint: disable=no-member
|
# false positive from pylint: disable=no-member
|
||||||
contract: datatypes.Contract = web3_instance.eth.contract(
|
contract: datatypes.Contract = web3_instance.eth.contract(
|
||||||
address=to_checksum_address(contract_address), abi=EXCHANGE_ABI
|
address=to_checksum_address(contract_address),
|
||||||
|
abi=Constants.contract_name_to_abi["Exchange"],
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
return (
|
return (
|
||||||
|
0
python-packages/order_utils/stubs/sha3/__init__.pyi
Normal file
0
python-packages/order_utils/stubs/sha3/__init__.pyi
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
"""Test zero_ex.order_utils.get_order_hash_hex()."""
|
||||||
|
|
||||||
|
from zero_ex.order_utils import (
|
||||||
|
generate_order_hash_hex,
|
||||||
|
make_empty_order,
|
||||||
|
Constants,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_order_hash_hex__empty_order():
|
||||||
|
"""Test the hashing of an uninitialized order."""
|
||||||
|
expected_hash_hex = (
|
||||||
|
"faa49b35faeb9197e9c3ba7a52075e6dad19739549f153b77dfcf59408a4b422"
|
||||||
|
)
|
||||||
|
actual_hash_hex = generate_order_hash_hex(
|
||||||
|
make_empty_order(), Constants.null_address
|
||||||
|
)
|
||||||
|
assert actual_hash_hex == expected_hash_hex
|
Loading…
x
Reference in New Issue
Block a user