From f3429db5c1bcee263a226e7d874cc69b7b0aaf6b Mon Sep 17 00:00:00 2001 From: mike Date: Thu, 7 Dec 2023 10:18:08 -0800 Subject: [PATCH] Neg Risk (#32) * add ContractConfig class * remove config, update tests * remove contract config import * extra tests * fixing tests * update black, reformat --------- Co-authored-by: Rodrigo <95635797+poly-rodr@users.noreply.github.com> --- py_order_utils/config.py | 38 --------- py_order_utils/model/order.py | 2 +- requirements.txt | 2 +- setup.py | 2 +- tests/test_config.py | 19 ----- tests/test_order_builder.py | 141 ++++++++++++++++++++++++++++++++-- 6 files changed, 137 insertions(+), 67 deletions(-) delete mode 100644 py_order_utils/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 07ad144..0000000 --- a/py_order_utils/config.py +++ /dev/null @@ -1,38 +0,0 @@ -class ContractConfig: - def __init__(self, exchange, collateral, conditional): - self.exchange = exchange - self.collateral = collateral - self.conditional = conditional - - 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: - """ - Get the contract configuration for the chain - """ - CONFIG = { - 137: ContractConfig( - exchange="0x4bFb41d5B3570DeFd03C39a9A4D8dE6Bd8B8982E", - collateral="0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174", - conditional="0x4D97DCd97eC945f40cF65F87097ACe5EA0476045", - ), - 80001: ContractConfig( - exchange="0x4bFb41d5B3570DeFd03C39a9A4D8dE6Bd8B8982E", - collateral="0x2E8DCfE708D44ae2e406a1c02DFE2Fa13012f961", - conditional="0x7D8610E9567d2a6C9FBf66a5A13E9Ba8bb120d43", - ), - } - - config = CONFIG.get(chainID) - if config is None: - raise Exception("Invalid chainID: ${}".format(chainID)) - - return config 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/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 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_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..62d3676 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,12 @@ maker_address = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" salt = 479249096354 chain_id = 80001 -mumbai_contracts = get_contract_config(chain_id) +mumbai_contracts = { + "exchange": "0x4bFb41d5B3570DeFd03C39a9A4D8dE6Bd8B8982E", + "negRiskExchange": "0x87d1A0DdB4C63a6301916F02090A51a7241571e4", + "collateral": "0x2E8DCfE708D44ae2e406a1c02DFE2Fa13012f961", + "conditional": "0x7D8610E9567d2a6C9FBf66a5A13E9Ba8bb120d43", +} def mock_salt_generator(): @@ -23,7 +27,33 @@ 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() + 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_validate_order_neg_risk(self): + builder = OrderBuilder(mumbai_contracts["negRiskExchange"], chain_id, signer) # Valid order data = self.generate_data() @@ -49,7 +79,62 @@ 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 + + # 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["exchange"], 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_order_neg_risk(self): + builder = OrderBuilder(mumbai_contracts["negRiskExchange"], chain_id, signer) invalid_data_input = self.generate_data() invalid_data_input.tokenId = None @@ -83,7 +168,7 @@ def test_build_order(self): # specific salt builder = OrderBuilder( - mumbai_contracts.exchange, chain_id, signer, mock_salt_generator + mumbai_contracts["negRiskExchange"], chain_id, signer, mock_salt_generator ) _order = builder.build_order(self.generate_data()) @@ -105,7 +190,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()) @@ -121,9 +206,28 @@ 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 = ( + "0x053c3169ec6c9e99e3640cb12b9c9245917daf36bd4fd39ea09d201a07b53952" + ) + struct_hash = builder._create_struct_hash(_order) + self.assertEqual(expected_struct_hash, struct_hash.hex()) + + expected_signature = "0xb47e588cfb8630ffa255d1a04a4bb2b996967c2143fc107ab443282ed7dcc123288842968fa29e6f2e789e39ea02f13654d4dd55002b8672e9a91e2ba9045aa21b" + 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 + mumbai_contracts["exchange"], chain_id, signer, mock_salt_generator ) signed_order = builder.build_signed_order(self.generate_data()) @@ -144,6 +248,29 @@ 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 = "0xb47e588cfb8630ffa255d1a04a4bb2b996967c2143fc107ab443282ed7dcc123288842968fa29e6f2e789e39ea02f13654d4dd55002b8672e9a91e2ba9045aa21b" + 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,