From 0a916c3a92e9895ca9853eaeabc0e27a96ad870a Mon Sep 17 00:00:00 2001 From: mike Date: Tue, 24 Oct 2023 11:34:04 -0400 Subject: [PATCH 1/6] add ContractConfig class --- py_order_utils/config.py | 36 ++++++++++++++----------- py_order_utils/model/__init__.py | 1 + py_order_utils/model/contract_config.py | 22 +++++++++++++++ 3 files changed, 43 insertions(+), 16 deletions(-) create mode 100644 py_order_utils/model/contract_config.py diff --git a/py_order_utils/config.py b/py_order_utils/config.py index 07ad144..1ab9ed8 100644 --- a/py_order_utils/config.py +++ b/py_order_utils/config.py @@ -1,23 +1,11 @@ -class ContractConfig: - def __init__(self, exchange, collateral, conditional): - self.exchange = exchange - self.collateral = collateral - self.conditional = conditional +from .model import ContractConfig - def get_exchange(self): - return self.exchange - def get_collateral(self): - return self.collateral - - def get_conditional(self): - return self.conditional - - -def get_contract_config(chainID: int) -> ContractConfig: +def get_contract_config(chainID: int, neg_risk: bool=False) -> ContractConfig: """ Get the contract configuration for the chain """ + CONFIG = { 137: ContractConfig( exchange="0x4bFb41d5B3570DeFd03C39a9A4D8dE6Bd8B8982E", @@ -31,7 +19,23 @@ def get_contract_config(chainID: int) -> ContractConfig: ), } - config = CONFIG.get(chainID) + NEG_RISK_CONFIG = { + 137: ContractConfig( + exchange="", # TODO + collateral="", # TODO + conditional="0x4D97DCd97eC945f40cF65F87097ACe5EA0476045", + ), + 80001: ContractConfig( + exchange="", # TODO + collateral="", # TODO + conditional="0x7D8610E9567d2a6C9FBf66a5A13E9Ba8bb120d43", + ), + } + + if neg_risk: + config = NEG_RISK_CONFIG.get(chainID) + else: + config = CONFIG.get(chainID) if config is None: raise Exception("Invalid chainID: ${}".format(chainID)) diff --git a/py_order_utils/model/__init__.py b/py_order_utils/model/__init__.py index 3e1b0ea..e94640f 100644 --- a/py_order_utils/model/__init__.py +++ b/py_order_utils/model/__init__.py @@ -1,3 +1,4 @@ from py_order_utils.model.order import OrderData, Order, SignedOrder from py_order_utils.model.signatures import EOA, POLY_PROXY, POLY_GNOSIS_SAFE from py_order_utils.model.sides import BUY, SELL +from py_order_utils.model.contract_config import ContractConfig diff --git a/py_order_utils/model/contract_config.py b/py_order_utils/model/contract_config.py new file mode 100644 index 0000000..4214193 --- /dev/null +++ b/py_order_utils/model/contract_config.py @@ -0,0 +1,22 @@ +from dataclasses import dataclass + +@dataclass +class ContractConfig: + """ + Contract Configuration + """ + + exchange: str + """ + The exchange contract responsible for matching orders + """ + + collateral: str + """ + The ERC20 token used as collateral for the exchange's markets + """ + + conditional: str + """ + The ERC1155 conditional tokens contract + """ From 5bf397a5f84f9b117f1fa4e30352ea3a30ff18bd Mon Sep 17 00:00:00 2001 From: mike Date: Wed, 15 Nov 2023 13:03:15 -0800 Subject: [PATCH 2/6] remove config, update tests --- py_order_utils/config.py | 42 ------------------------- py_order_utils/model/contract_config.py | 22 ------------- tests/test_config.py | 19 ----------- tests/test_order_builder.py | 17 +++++----- 4 files changed, 10 insertions(+), 90 deletions(-) delete mode 100644 py_order_utils/config.py delete mode 100644 py_order_utils/model/contract_config.py delete mode 100644 tests/test_config.py diff --git a/py_order_utils/config.py b/py_order_utils/config.py deleted file mode 100644 index 1ab9ed8..0000000 --- a/py_order_utils/config.py +++ /dev/null @@ -1,42 +0,0 @@ -from .model import ContractConfig - - -def get_contract_config(chainID: int, neg_risk: bool=False) -> ContractConfig: - """ - Get the contract configuration for the chain - """ - - CONFIG = { - 137: ContractConfig( - exchange="0x4bFb41d5B3570DeFd03C39a9A4D8dE6Bd8B8982E", - collateral="0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174", - conditional="0x4D97DCd97eC945f40cF65F87097ACe5EA0476045", - ), - 80001: ContractConfig( - exchange="0x4bFb41d5B3570DeFd03C39a9A4D8dE6Bd8B8982E", - collateral="0x2E8DCfE708D44ae2e406a1c02DFE2Fa13012f961", - conditional="0x7D8610E9567d2a6C9FBf66a5A13E9Ba8bb120d43", - ), - } - - NEG_RISK_CONFIG = { - 137: ContractConfig( - exchange="", # TODO - collateral="", # TODO - conditional="0x4D97DCd97eC945f40cF65F87097ACe5EA0476045", - ), - 80001: ContractConfig( - exchange="", # TODO - collateral="", # TODO - conditional="0x7D8610E9567d2a6C9FBf66a5A13E9Ba8bb120d43", - ), - } - - if neg_risk: - config = NEG_RISK_CONFIG.get(chainID) - else: - config = CONFIG.get(chainID) - if config is None: - raise Exception("Invalid chainID: ${}".format(chainID)) - - return config diff --git a/py_order_utils/model/contract_config.py b/py_order_utils/model/contract_config.py deleted file mode 100644 index 4214193..0000000 --- a/py_order_utils/model/contract_config.py +++ /dev/null @@ -1,22 +0,0 @@ -from dataclasses import dataclass - -@dataclass -class ContractConfig: - """ - Contract Configuration - """ - - exchange: str - """ - The exchange contract responsible for matching orders - """ - - collateral: str - """ - The ERC20 token used as collateral for the exchange's markets - """ - - conditional: str - """ - The ERC1155 conditional tokens contract - """ diff --git a/tests/test_config.py b/tests/test_config.py deleted file mode 100644 index f7c033a..0000000 --- a/tests/test_config.py +++ /dev/null @@ -1,19 +0,0 @@ -from unittest import TestCase -from py_order_utils.config import get_contract_config - - -class TestConfig(TestCase): - def test_get_config(self): - valid_config = get_contract_config(80001) - self.assertIsNotNone(valid_config) - self.assertIsNotNone(valid_config.get_exchange()) - self.assertIsNotNone(valid_config.get_collateral()) - - valid_config = get_contract_config(137) - self.assertIsNotNone(valid_config) - self.assertIsNotNone(valid_config.get_exchange()) - self.assertIsNotNone(valid_config.get_collateral()) - - # invalid config - with self.assertRaises(Exception): - get_contract_config(2190239023902) diff --git a/tests/test_order_builder.py b/tests/test_order_builder.py index d02ce47..35b3e1a 100644 --- a/tests/test_order_builder.py +++ b/tests/test_order_builder.py @@ -5,7 +5,6 @@ from py_order_utils.builders import OrderBuilder from py_order_utils.model.signatures import EOA from py_order_utils.signer import Signer -from py_order_utils.config import get_contract_config from py_order_utils.constants import ZERO_ADDRESS # publicly known private key @@ -14,7 +13,11 @@ maker_address = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" salt = 479249096354 chain_id = 80001 -mumbai_contracts = get_contract_config(chain_id) +mumbai_contracts = { + "exchange": "0x4bFb41d5B3570DeFd03C39a9A4D8dE6Bd8B8982E", + "collateral": "0x2E8DCfE708D44ae2e406a1c02DFE2Fa13012f961", + "conditional": "0x7D8610E9567d2a6C9FBf66a5A13E9Ba8bb120d43", +} def mock_salt_generator(): @@ -23,7 +26,7 @@ def mock_salt_generator(): class TestOrderBuilder(TestCase): def test_validate_order(self): - builder = OrderBuilder(mumbai_contracts.exchange, chain_id, signer) + builder = OrderBuilder(mumbai_contracts["exchange"], chain_id, signer) # Valid order data = self.generate_data() @@ -49,7 +52,7 @@ def test_validate_order(self): self.assertFalse(builder._validate_inputs(data)) def test_build_order(self): - builder = OrderBuilder(mumbai_contracts.exchange, chain_id, signer) + builder = OrderBuilder(mumbai_contracts["exchange"], chain_id, signer) invalid_data_input = self.generate_data() invalid_data_input.tokenId = None @@ -83,7 +86,7 @@ def test_build_order(self): # specific salt builder = OrderBuilder( - mumbai_contracts.exchange, chain_id, signer, mock_salt_generator + mumbai_contracts["exchange"], chain_id, signer, mock_salt_generator ) _order = builder.build_order(self.generate_data()) @@ -105,7 +108,7 @@ def test_build_order(self): def test_build_prder_signature(self): builder = OrderBuilder( - mumbai_contracts.exchange, chain_id, signer, mock_salt_generator + mumbai_contracts["exchange"], chain_id, signer, mock_salt_generator ) _order = builder.build_order(self.generate_data()) @@ -123,7 +126,7 @@ def test_build_prder_signature(self): def test_build_signed_order(self): builder = OrderBuilder( - mumbai_contracts.exchange, chain_id, signer, mock_salt_generator + mumbai_contracts["exchange"], chain_id, signer, mock_salt_generator ) signed_order = builder.build_signed_order(self.generate_data()) From e7de2d87ebeda87900ea7279b697e9d69b57adc9 Mon Sep 17 00:00:00 2001 From: mike Date: Wed, 15 Nov 2023 13:04:06 -0800 Subject: [PATCH 3/6] remove contract config import --- py_order_utils/model/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/py_order_utils/model/__init__.py b/py_order_utils/model/__init__.py index e94640f..3e1b0ea 100644 --- a/py_order_utils/model/__init__.py +++ b/py_order_utils/model/__init__.py @@ -1,4 +1,3 @@ from py_order_utils.model.order import OrderData, Order, SignedOrder from py_order_utils.model.signatures import EOA, POLY_PROXY, POLY_GNOSIS_SAFE from py_order_utils.model.sides import BUY, SELL -from py_order_utils.model.contract_config import ContractConfig From 02f687dbc433613a8d668f7313e5c0435b41b70e Mon Sep 17 00:00:00 2001 From: Rodrigo <95635797+poly-rodr@users.noreply.github.com> Date: Thu, 7 Dec 2023 10:25:07 -0300 Subject: [PATCH 4/6] extra tests --- py_order_utils/model/order.py | 2 +- setup.py | 2 +- tests/test_order_builder.py | 131 +++++++++++++++++++++++++++++++++- 3 files changed, 130 insertions(+), 5 deletions(-) diff --git a/py_order_utils/model/order.py b/py_order_utils/model/order.py index 8048658..35d23ef 100644 --- a/py_order_utils/model/order.py +++ b/py_order_utils/model/order.py @@ -170,7 +170,7 @@ def dict(self): d["signature"] = self.signature if d["side"] == 0: d["side"] = "BUY" - else: + else: d["side"] = "SELL" d["expiration"] = str(d["expiration"]) d["nonce"] = str(d["nonce"]) diff --git a/setup.py b/setup.py index 5357611..5daa8f3 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setuptools.setup( name="py_order_utils", - version="0.1.3", + version="0.2.0", author="Polymarket Engineering", author_email="engineering@polymarket.com", maintainer="Polymarket Engineering", diff --git a/tests/test_order_builder.py b/tests/test_order_builder.py index 35b3e1a..4561d37 100644 --- a/tests/test_order_builder.py +++ b/tests/test_order_builder.py @@ -14,9 +14,10 @@ salt = 479249096354 chain_id = 80001 mumbai_contracts = { - "exchange": "0x4bFb41d5B3570DeFd03C39a9A4D8dE6Bd8B8982E", - "collateral": "0x2E8DCfE708D44ae2e406a1c02DFE2Fa13012f961", - "conditional": "0x7D8610E9567d2a6C9FBf66a5A13E9Ba8bb120d43", + "exchange": "0x4bFb41d5B3570DeFd03C39a9A4D8dE6Bd8B8982E", + "negRiskExchange": "0x87d1A0DdB4C63a6301916F02090A51a7241571e4", + "collateral": "0x2E8DCfE708D44ae2e406a1c02DFE2Fa13012f961", + "conditional": "0x7D8610E9567d2a6C9FBf66a5A13E9Ba8bb120d43", } @@ -51,6 +52,32 @@ def test_validate_order(self): data.signatureType = 100 self.assertFalse(builder._validate_inputs(data)) + def test_validate_order_neg_risk(self): + builder = OrderBuilder(mumbai_contracts["negRiskExchange"], chain_id, signer) + + # Valid order + data = self.generate_data() + self.assertTrue(builder._validate_inputs(data)) + + # Invalid if any of the required fields are missing + data = self.generate_data() + data.maker = None + self.assertFalse(builder._validate_inputs(data)) + + # Invalid if any of the required fields are invalid + data = self.generate_data() + data.nonce = "-1" + self.assertFalse(builder._validate_inputs(data)) + + data = self.generate_data() + data.expiration = "not a number" + self.assertFalse(builder._validate_inputs(data)) + + # Invalid signature type + data = self.generate_data() + data.signatureType = 100 + self.assertFalse(builder._validate_inputs(data)) + def test_build_order(self): builder = OrderBuilder(mumbai_contracts["exchange"], chain_id, signer) @@ -106,6 +133,61 @@ def test_build_order(self): self.assertEqual(BUY, _order["side"]) self.assertEqual(EOA, _order["signatureType"]) + def test_build_order_neg_risk(self): + builder = OrderBuilder(mumbai_contracts["negRiskExchange"], chain_id, signer) + + invalid_data_input = self.generate_data() + invalid_data_input.tokenId = None + + # throw if invalid order input + with self.assertRaises(ValidationException): + builder.build_order(invalid_data_input) + + invalid_data_input = self.generate_data() + invalid_data_input.signer = ZERO_ADDRESS + + # throw if invalid signer + with self.assertRaises(ValidationException): + builder.build_order(invalid_data_input) + + _order = builder.build_order(self.generate_data()) + + # Ensure correct values on order + self.assertTrue(isinstance(_order["salt"], int)) + self.assertEqual(maker_address, _order["maker"]) + self.assertEqual(maker_address, _order["signer"]) + self.assertEqual(ZERO_ADDRESS, _order["taker"]) + self.assertEqual(1234, _order["tokenId"]) + self.assertEqual(100000000, _order["makerAmount"]) + self.assertEqual(50000000, _order["takerAmount"]) + self.assertEqual(0, _order["expiration"]) + self.assertEqual(0, _order["nonce"]) + self.assertEqual(100, _order["feeRateBps"]) + self.assertEqual(BUY, _order["side"]) + self.assertEqual(EOA, _order["signatureType"]) + + # specific salt + builder = OrderBuilder( + mumbai_contracts["negRiskExchange"], chain_id, signer, mock_salt_generator + ) + + _order = builder.build_order(self.generate_data()) + + # Ensure correct values on order + self.assertTrue(isinstance(_order["salt"], int)) + self.assertEqual(salt, _order["salt"]) + self.assertEqual(maker_address, _order["maker"]) + self.assertEqual(maker_address, _order["signer"]) + self.assertEqual(ZERO_ADDRESS, _order["taker"]) + self.assertEqual(1234, _order["tokenId"]) + self.assertEqual(100000000, _order["makerAmount"]) + self.assertEqual(50000000, _order["takerAmount"]) + self.assertEqual(0, _order["expiration"]) + self.assertEqual(0, _order["nonce"]) + self.assertEqual(100, _order["feeRateBps"]) + self.assertEqual(BUY, _order["side"]) + self.assertEqual(EOA, _order["signatureType"]) + def test_build_prder_signature(self): builder = OrderBuilder( mumbai_contracts["exchange"], chain_id, signer, mock_salt_generator @@ -124,6 +206,25 @@ def test_build_prder_signature(self): sig = builder.build_order_signature(_order) self.assertEqual(expected_signature, sig) + def test_build_prder_signature_neg_risk(self): + builder = OrderBuilder( + mumbai_contracts["negRiskExchange"], chain_id, signer, mock_salt_generator + ) + + _order = builder.build_order(self.generate_data()) + + # Ensure struct hash is expected(generated via ethers) + expected_struct_hash = ( + "0xbf58957703791db2ab057528d03d1cff5375d9a475b14a9073bb7d892398dc96" + ) + struct_hash = builder._create_struct_hash(_order) + self.assertEqual(expected_struct_hash, struct_hash.hex()) + + expected_signature = "0x3874d2cfe79c0e82577f4f76ec58d950522ee5923a6f08441927d382c8178a5a2190fd4d5c49705e94d75999a58ec843f94a432e87ebc15cdb2186d315b3cc201b" + sig = builder.build_order_signature(_order) + print(sig) + self.assertEqual(expected_signature, sig) + def test_build_signed_order(self): builder = OrderBuilder( mumbai_contracts["exchange"], chain_id, signer, mock_salt_generator @@ -147,6 +248,30 @@ def test_build_signed_order(self): self.assertEqual(BUY, signed_order.order["side"]) self.assertEqual(EOA, signed_order.order["signatureType"]) + def test_build_signed_order_neg_risk(self): + builder = OrderBuilder( + mumbai_contracts["negRiskExchange"], chain_id, signer, mock_salt_generator + ) + + signed_order = builder.build_signed_order(self.generate_data()) + + expected_signature = "0x3874d2cfe79c0e82577f4f76ec58d950522ee5923a6f08441927d382c8178a5a2190fd4d5c49705e94d75999a58ec843f94a432e87ebc15cdb2186d315b3cc201b" + print("scond: " + signed_order.signature) + self.assertEqual(expected_signature, signed_order.signature) + self.assertTrue(isinstance(signed_order.order["salt"], int)) + self.assertEqual(salt, signed_order.order["salt"]) + self.assertEqual(maker_address, signed_order.order["maker"]) + self.assertEqual(maker_address, signed_order.order["signer"]) + self.assertEqual(ZERO_ADDRESS, signed_order.order["taker"]) + self.assertEqual(1234, signed_order.order["tokenId"]) + self.assertEqual(100000000, signed_order.order["makerAmount"]) + self.assertEqual(50000000, signed_order.order["takerAmount"]) + self.assertEqual(0, signed_order.order["expiration"]) + self.assertEqual(0, signed_order.order["nonce"]) + self.assertEqual(100, signed_order.order["feeRateBps"]) + self.assertEqual(BUY, signed_order.order["side"]) + self.assertEqual(EOA, signed_order.order["signatureType"]) + def generate_data(self) -> OrderData: return OrderData( maker=maker_address, From a60e91414a27e5aab7c917982125e14f13462528 Mon Sep 17 00:00:00 2001 From: Rodrigo <95635797+poly-rodr@users.noreply.github.com> Date: Thu, 7 Dec 2023 10:29:26 -0300 Subject: [PATCH 5/6] fixing tests --- tests/test_order_builder.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/tests/test_order_builder.py b/tests/test_order_builder.py index 4561d37..62d3676 100644 --- a/tests/test_order_builder.py +++ b/tests/test_order_builder.py @@ -215,12 +215,12 @@ def test_build_prder_signature_neg_risk(self): # Ensure struct hash is expected(generated via ethers) expected_struct_hash = ( - "0xbf58957703791db2ab057528d03d1cff5375d9a475b14a9073bb7d892398dc96" + "0x053c3169ec6c9e99e3640cb12b9c9245917daf36bd4fd39ea09d201a07b53952" ) struct_hash = builder._create_struct_hash(_order) self.assertEqual(expected_struct_hash, struct_hash.hex()) - expected_signature = "0x3874d2cfe79c0e82577f4f76ec58d950522ee5923a6f08441927d382c8178a5a2190fd4d5c49705e94d75999a58ec843f94a432e87ebc15cdb2186d315b3cc201b" + expected_signature = "0xb47e588cfb8630ffa255d1a04a4bb2b996967c2143fc107ab443282ed7dcc123288842968fa29e6f2e789e39ea02f13654d4dd55002b8672e9a91e2ba9045aa21b" sig = builder.build_order_signature(_order) print(sig) self.assertEqual(expected_signature, sig) @@ -255,8 +255,7 @@ def test_build_signed_order_neg_risk(self): signed_order = builder.build_signed_order(self.generate_data()) - expected_signature = "0x3874d2cfe79c0e82577f4f76ec58d950522ee5923a6f08441927d382c8178a5a2190fd4d5c49705e94d75999a58ec843f94a432e87ebc15cdb2186d315b3cc201b" - print("scond: " + signed_order.signature) + expected_signature = "0xb47e588cfb8630ffa255d1a04a4bb2b996967c2143fc107ab443282ed7dcc123288842968fa29e6f2e789e39ea02f13654d4dd55002b8672e9a91e2ba9045aa21b" self.assertEqual(expected_signature, signed_order.signature) self.assertTrue(isinstance(signed_order.order["salt"], int)) self.assertEqual(salt, signed_order.order["salt"]) From 66ef75fbdcc9ed4840a156c8004dcecbbbc5d28e Mon Sep 17 00:00:00 2001 From: mike Date: Thu, 7 Dec 2023 10:13:27 -0800 Subject: [PATCH 6/6] update black, reformat --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index cbdd31b..a19b59a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -46,4 +46,4 @@ varint==1.0.2 web3==5.26.0 websockets==9.1 yarl==1.7.2 -black==22.1.0 +black==22.3.0