From 46522b3f9ebc817c50a8b96aad9ef55cbb3750bd Mon Sep 17 00:00:00 2001 From: paplo75 Date: Fri, 14 Aug 2026 15:33:41 +0200 Subject: [PATCH] Fix market order price rounding for builder-deployed perp dexs _slippage_price classifies an asset as spot with `asset >= 10_000`, but builder-deployed (HIP-3) perp dex assets start at 110000, so they are also caught by that check. Those perps are then rounded to 8 - szDecimals decimals instead of 6 - szDecimals, and market_open/market_close can produce a price with too many decimal places, which the exchange rejects. Spot asset ids live in [10000, 110000), so bound the check on both sides. --- hyperliquid/exchange.py | 4 +-- tests/exchange_test.py | 57 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 2 deletions(-) create mode 100644 tests/exchange_test.py diff --git a/hyperliquid/exchange.py b/hyperliquid/exchange.py index 41a2f66b..ad6c0737 100644 --- a/hyperliquid/exchange.py +++ b/hyperliquid/exchange.py @@ -123,8 +123,8 @@ def _slippage_price( px = float(self.info.all_mids(dex)[coin]) asset = self.info.coin_to_asset[coin] - # spot assets start at 10000 - is_spot = asset >= 10_000 + # spot assets start at 10000 and builder-deployed perp dex assets start at 110000 + is_spot = 10_000 <= asset < 110_000 # Calculate Slippage px *= (1 + slippage) if is_buy else (1 - slippage) diff --git a/tests/exchange_test.py b/tests/exchange_test.py new file mode 100644 index 00000000..4f85bcd2 --- /dev/null +++ b/tests/exchange_test.py @@ -0,0 +1,57 @@ +import eth_account + +from hyperliquid.exchange import Exchange +from hyperliquid.utils.types import Meta, SpotMeta + +TEST_META: Meta = {"universe": [{"name": "ABC", "szDecimals": 0}]} +TEST_SPOT_META: SpotMeta = { + "universe": [{"name": "@1", "tokens": [1, 0], "index": 1, "isCanonical": False}], + "tokens": [ + { + "name": "USDC", + "szDecimals": 8, + "weiDecimals": 8, + "index": 0, + "tokenId": "0x6d1e7cde53ba9467b783cb7c530ce054", + "isCanonical": True, + "evmContract": None, + "fullName": None, + }, + { + "name": "PURR", + "szDecimals": 0, + "weiDecimals": 5, + "index": 1, + "tokenId": "0xc1fb593aeffbeb02f85e0308e9956a90", + "isCanonical": True, + "evmContract": None, + "fullName": None, + }, + ], +} + + +def make_exchange() -> Exchange: + wallet = eth_account.Account.from_key("0x0123456789012345678901234567890123456789012345678901234567890123") + return Exchange(wallet, meta=TEST_META, spot_meta=TEST_SPOT_META) + + +def test_slippage_price_perp(): + exchange = make_exchange() + # 5 significant figures, then at most 6 - szDecimals decimals + assert exchange._slippage_price("ABC", True, 0.05, 0.0012345678) == 0.001296 + + +def test_slippage_price_spot(): + exchange = make_exchange() + # spot allows 8 - szDecimals decimals + assert exchange._slippage_price("@1", True, 0.05, 0.0012345678) == 0.0012963 + + +def test_slippage_price_builder_deployed_perp(): + exchange = make_exchange() + # builder-deployed perp dex assets start at 110000, above the spot range, but are still perps + exchange.info.name_to_coin["test:ABC"] = "test:ABC" + exchange.info.coin_to_asset["test:ABC"] = 110000 + exchange.info.asset_to_sz_decimals[110000] = 0 + assert exchange._slippage_price("test:ABC", True, 0.05, 0.0012345678) == 0.001296