PerpOB#51
Conversation
Aligns with reya-api-specs feat/perpOB (v2.3.0) and the off-chain perpOB migration tracked in Reya-Labs/reya-off-chain-monorepo#2575. Perpetual futures move from AMM execution to the matching engine, sharing the spot order envelope. EIP-712 signing rewritten for the new flat 13-field Order/OrderDetails typehash (signatures.py); the old ConditionalOrder envelope, composite perp nonce, personal_sign cancel hack, and inputs:bytes blob are gone. client.py collapses to a single signing path with no spot-vs-perp branching, and mass_cancel works for both market types. WebSocket channels switch to the unified executionBusts stream; sdk/open_api/ and sdk/async_api/ regenerated against the new spec. Tests: shared lifecycle tests parametrized over [spot, perp] under tests/test_orderbook/; deliberate-bust scenarios in test_spot/ deleted in favour of unified bust tests there. AMM-only tests (test_perps/test_dynamic_pricing.py) deleted; tests that need maker/taker fixtures or a sign_order rewrite (test_perps/test_limit_orders.py, test_perps/test_position_management.py, test_spot/test_api_validation.py) marked pytest.mark.skip with explicit migration TODOs. RPC layer (sdk/reya_rpc/) intentionally untouched — on-chain settlement helpers are matching-engine territory and out of scope for this PR. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Important Review skippedAuto incremental reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughWalkthroughUpdates SDK from v2.1.7.0 to v2.3.0, consolidating "SpotExecutionBust" into unified "ExecutionBust" for both spot/perp markets, replacing OrderType shorthand (TP/SL) with explicit names (TAKE_PROFIT/STOP_LOSS), introducing SpotMarketSummary endpoints, refactoring signature generation and client order logic, and reorganizing test suites with some marking for rewrite. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 inconclusive)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
|
🤖 SDK Version Check ✅ Version check passed
|
There was a problem hiding this comment.
Actionable comments posted: 22
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (4)
sdk/open_api/models/create_order_response.py (1)
34-34: 🧹 Nitpick | 🔵 TrivialConsider configuring the OpenAPI generator to enforce the 120-character line length limit.
Line 34 exceeds the 120-character guideline (~157 characters). Since this is a generated file marked "Do not edit the class manually," the line length should be addressed in the OpenAPI generator configuration rather than through manual edits. As per coding guidelines, line length of 120 characters should be enforced for code formatting.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdk/open_api/models/create_order_response.py` at line 34, The generated line for the field "order_id" in create_order_response.py exceeds the 120-char limit; instead of editing this generated line, update the OpenAPI generator/template configuration to enforce a 120-character max line length (wrap long Field descriptions or enable line wrapping) so generated model fields like order_id: Optional[StrictStr] = Field(..., description="...", alias="orderId") are emitted within 120 chars; adjust the generator's mustache template or code formatter settings used in generation so all future outputs conform to the 120-character guideline.sdk/open_api/models/market_definition.py (1)
40-40:⚠️ Potential issue | 🟠 MajorFix line length violation:
__propertiesdeclaration exceeds 120-character limit.Line 40 contains 184 characters, exceeding the 120-character limit by 64 characters. The
__propertieslist declaration must be reformatted or split across multiple lines to comply with project standards.Since this file is auto-generated, update the generator configuration to ensure compliance with the 120-character line length requirement for all
**/*.pyfiles before regenerating.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdk/open_api/models/market_definition.py` at line 40, The __properties class variable declaration in MarketDefinition exceeds the 120-character limit; split the list literal across multiple lines so each line is ≤120 chars (e.g., one element per line or grouped) and then update the OpenAPI/python generator template or its configuration that produces sdk/open_api/models/market_definition.py so all generated **/*.py files format __properties (and similar long lists) with line wrapping under 120 chars, then regenerate the models to apply the fix; target symbols: __properties in the MarketDefinition model (and the generator template that emits the class property list).sdk/open_api/__init__.py (1)
10-17:⚠️ Potential issue | 🟡 MinorVersion mismatch between OpenAPI document and package version.
The OpenAPI document version was updated to
2.3.0(line 10), but__version__remains"2.1.7.0"(line 17). This inconsistency could cause confusion when debugging or verifying SDK compatibility with the API spec.Consider updating
__version__to align with the v2.3.0 migration.🔧 Proposed fix
-__version__ = "2.1.7.0" +__version__ = "2.3.0.0"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdk/open_api/__init__.py` around lines 10 - 17, Update the package version constant to match the OpenAPI document change: replace the current __version__ value ("2.1.7.0") with "2.3.0" in the module where __version__ is defined, and ensure the module docstring/comment referencing the OpenAPI document version remains consistent with the new value.sdk/reya_rest_api/client.py (1)
186-237:⚠️ Potential issue | 🟠 MajorRetain market-type validation in the unified order path.
After the refactor these methods only resolve
market_id, so they no longer enforce the two market-specific rules called out in their own docstrings: trigger orders are perp-only, andreduceOnlymust be omitted on spot. As written, a spot trigger order will still be signed/submitted, and a spot limit order withreduce_only=Falsestill serializesreduceOnly=False, which the API rejects.Proposed fix
class ReyaTradingClient: @@ def __init__(self, config: Optional[TradingConfig] = None): self._symbol_to_market_id: dict[str, int] = {} + self._symbol_to_market_type: dict[str, str] = {} self._initialized = False @@ async def _load_market_definitions(self) -> None: """Load both perp and spot market definitions.""" market_definitions: list[MarketDefinition] = await self.reference.get_market_definitions() self._symbol_to_market_id = {market.symbol: market.market_id for market in market_definitions} + self._symbol_to_market_type = {market.symbol: "perp" for market in market_definitions} @@ spot_market_definitions = await self.reference.get_spot_market_definitions() for market in spot_market_definitions: self._symbol_to_market_id[market.symbol] = market.market_id + self._symbol_to_market_type[market.symbol] = "spot" @@ async def create_limit_order(self, params: LimitOrderParameters) -> CreateOrderResponse: @@ market_id = self._get_market_id_from_symbol(params.symbol) + market_type = self._symbol_to_market_type[params.symbol] nonce = self._get_next_nonce() @@ + if market_type == "spot" and params.reduce_only is not None: + raise ValueError("reduce_only is only supported for perp markets") + order_request = CreateOrderRequest( @@ - reduceOnly=reduce_only if params.reduce_only is not None else None, + reduceOnly=reduce_only if market_type == "perp" and params.reduce_only is not None else None, @@ async def create_trigger_order(self, params: TriggerOrderParameters) -> CreateOrderResponse: @@ market_id = self._get_market_id_from_symbol(params.symbol) + market_type = self._symbol_to_market_type[params.symbol] + if market_type != "perp": + raise ValueError("Trigger orders are only supported for perp markets") nonce = self._get_next_nonce()Also applies to: 242-305
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdk/reya_rest_api/client.py` around lines 186 - 237, The unified order methods (create_limit_order and the trigger-order method around lines 242-305) must reintroduce market-type validation: use _get_market_id_from_symbol(params.symbol) then fetch the market type (e.g., via an existing helper like _get_market_by_id or a new _is_perp_market/_is_spot_market) and enforce the rules — if creating a trigger order ensure the market is perp and raise ValueError if not; for limit (and other non-trigger) orders ensure that reduceOnly is omitted (pass None) when the market is spot (do not serialize reduceOnly=False), otherwise allow reduceOnly for perp markets; update the CreateOrderRequest construction (the reduceOnly and any trigger-specific fields) accordingly so spot orders never include reduceOnly and trigger orders are rejected on spot.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@sdk/async_api/market_summary.py`:
- Around line 37-43: The long list literals assigned to known_object_properties
and known_json_properties in market_summary.py exceed the 120-char line limit;
split each list across multiple lines (one element per line or grouped
logically) or run the repo formatter so each list entry is on its own line,
preserving the same element order and names for known_object_properties and
known_json_properties so the behavior of the validation logic (the
unknown_object_properties check and later uses) is unchanged.
In `@sdk/async_api/order.py`:
- Line 18: The single long declaration for the dataclass field order_type (the
Field(...) call with alias='orderType') exceeds 120 characters; break the
Field(...) call across multiple lines so the description, alias and other
arguments are on separate indented lines (for example place description on its
own line and alias on another) to keep each line under 120 chars and preserve
the same parameters and types in the order_type definition.
In `@sdk/async_api/perp_execution.py`:
- Around line 11-12: The new Field declarations (taker_order_id and
maker_order_id) and the regenerated allowlist lines exceed the 120-character
line-length limit; rewrap the Field calls and long allowlist entries so they
stay within 120 columns (for example break long description/alias strings across
lines, use implicit line continuation via parentheses or split the allowlist
into multiple shorter lines) and then run the repository formatter (e.g., Black
with the project's line-length setting) to ensure the changes for symbols
taker_order_id, maker_order_id and the allowlist entries referenced around the
block at lines 49-55 conform to the 120-character rule.
In `@sdk/async_api/spot_market_summary.py`:
- Line 20: Replace non-idiomatic membership checks with Python's preferred
operators: change occurrences of "not key in serialized_self" to "key not in
serialized_self" and replace "known_json_properties.__contains__(obj_key)" with
"obj_key in known_json_properties". Update these checks where they appear around
the serialization logic (references: serialized_self, key,
known_json_properties, obj_key) to use the "in" / "not in" operators for clarity
and consistency.
- Around line 1-3: Remove the unexpected leading whitespace before the
module-level imports so the statement "from __future__ import annotations" and
the subsequent imports (e.g., model_serializer, model_validator, BaseModel,
Field) start at column 0; open the file spot_market_summary.py and fix the
top-of-file indentation so all import lines have no leading spaces and then run
a quick import/test to confirm the SyntaxError is resolved.
In `@sdk/open_api/api_client.py`:
- Line 8: The User-Agent sent at runtime is still hardcoded to
"OpenAPI-Generator/2.1.7.0/python"; locate the code in
sdk/open_api/api_client.py that builds request headers (look for the ApiClient
class and the place that sets "User-Agent" or the string
"OpenAPI-Generator/2.1.7.0/python") and update that literal to match the OpenAPI
document version (e.g., "OpenAPI-Generator/2.3.0/python"); ensure any
helper/constant used for the runtime user-agent is updated consistently so
requests from this SDK report the new 2.3.0 version.
In `@sdk/open_api/api/market_data_api.py`:
- Around line 2542-2584: The OpenAPI description for the spot-only method
get_spot_market_summary still uses the perpetual example "BTCRUSDPERP"; update
the parameter description/example to a spot symbol (e.g., "BTCUSD" or another
spot market symbol) so generated SDK docs show a spot symbol; locate and change
the annotated parameter in get_spot_market_summary (symbol: Annotated[str,
Field(... description="Trading symbol (e.g., BTCRUSDPERP")]) to use a spot
example, and apply the same edit pattern to the other spot-summary methods
referenced (the similar symbol Field descriptions in the blocks around lines
2611-2653 and 2680-2722) so all spot-only endpoints advertise a spot symbol
example.
In `@sdk/open_api/configuration.py`:
- Around line 496-501: The return statement in the debug report function uses
backslash line continuation causing implicit multiline string concatenation
(ISC002); replace the backslash-continued string with an explicit parenthesized
string literal so the format call still applies (i.e., rewrite the return in the
method that builds the "Python SDK Debug Report" to use a single parenthesized
string across lines and then call .format(env=sys.platform,
pyversion=sys.version)); ensure the produced string content and the .format
arguments (env and pyversion) remain unchanged.
In `@sdk/open_api/models/create_order_request.py`:
- Line 30: The module-level docstring in create_order_request.py (the
CreateOrderRequest model) has lines exceeding the 120-char policy; update the
OpenAPI generator configuration to set docstringMaxLineLength (or equivalent) so
generated docstrings are wrapped, then regenerate; alternatively, post-process
the generated create_order_request.py docstring to wrap long lines to <=120
characters (ensure the multi-line comment that documents OrderDetails,
isBuy/qty, deadline/expiresAfter and the EIP-712 reference is wrapped) and
commit the fixed file so the CreateOrderRequest documentation lines 30, 35, 41,
42, and 49 no longer exceed 120 chars.
In `@sdk/open_api/models/order_type.py`:
- Line 23: The docstring for the generated OrderType enum is longer than the
120-char line limit (it documents the on-chain OrderDetails.orderType values) —
fix this by updating the OpenAPI generator/template to wrap or hard-wrap long
enum descriptions (or enable formatter wrapping) so the generated OrderType
docstring is broken into lines ≤120 chars; adjust the generator config or the
template that emits the OrderType enum documentation to produce multi-line
docstrings for the OrderType symbol (referencing OrderDetails.orderType in the
description) rather than relying on a single long line.
In `@sdk/open_api/models/perp_execution.py`:
- Around line 36-55: The generated PerpExecution model contains several lines
exceeding the 120-char limit (notably declarations like taker_order_id,
maker_order_id, qty, price, the various fee/pnl fields and the __properties
ClassVar); run the repository Python formatter (e.g., black) or manually reflow
those long Field/Annotated declarations so each parameter and alias sits on its
own wrapped line (keep names: taker_order_id, maker_order_id, qty, price,
taker_fee, maker_fee, taker_opening_fee, maker_opening_fee, taker_realized_pnl,
maker_realized_pnl, taker_price_variation_pnl, maker_price_variation_pnl,
taker_funding_pnl, maker_funding_pnl, and __properties) to ensure no code line
exceeds 120 characters and retain existing types/aliases.
In `@sdk/reya_rest_api/auth/signatures.py`:
- Around line 51-54: Reject negative or lossy inputs up-front: in
_scale_e18(value) convert to Decimal, reject negatives and require exact
18-decimal scaling by computing scaled = dec * 10**18 and raising ValueError if
scaled is not an integer (i.e. scaled != scaled.to_integral_value()); in
sign_order(...) validate the qty argument is non-negative and that calling
_scale_e18(qty) will not silently truncate precision (propagate the exception)
before building typed data so inputs like 0.1234567890123456789 or a negative
sell quantity fail fast.
In `@sdk/reya_rest_api/client.py`:
- Around line 119-134: The _get_next_nonce method currently calls
self._config.owner_wallet_address.lower() unconditionally which raises
AttributeError when owner_wallet_address is unset; update
ReyaTradingClient._get_next_nonce to first validate that
self._config.owner_wallet_address is present/non-empty and if not raise a clear
configuration error (e.g., ValueError or a specific ConfigError) with a
descriptive message like "owner_wallet_address must be set to use nonce-based
operations" before calling .lower(), then proceed with the existing locking and
nonce logic using the validated/lowered wallet_address.
In `@sdk/reya_websocket/resources/market.py`:
- Around line 272-294: Add missing docstrings to the
MarketExecutionBustsResource and MarketExecutionBustsSubscription classes to
match the style of other resources: add class-level one-line descriptions for
MarketExecutionBustsResource and MarketExecutionBustsSubscription, and small
docstrings on their __init__ methods describing parameters (socket, symbol), the
for_symbol method describing input and return type (returns
MarketExecutionBustsSubscription), and subscribe/unsubscribe methods describing
behavior and parameters (batched for subscribe). Follow the same brief phrasing
and parameter/return conventions used by existing classes like
MarketSummaryResource and MarketDepthResource to ensure consistency.
In `@sdk/reya_websocket/resources/wallet.py`:
- Around line 297-319: Add missing docstrings to match the style used by other
wallet resource classes: document the WalletExecutionBustsResource class (brief
class description) and its __init__ and for_wallet methods, and document
WalletExecutionBustsSubscription and its __init__, subscribe, and unsubscribe
methods (describe parameters like socket, address, and batched, and what each
method does). Use the same concise phrasing and punctuation style as other
resources so the docstrings for WalletExecutionBustsResource,
WalletExecutionBustsSubscription, __init__, for_wallet, subscribe, and
unsubscribe are consistent with the rest of the file.
In `@specs`:
- Line 1: The specs entry records a pinned submodule SHA (653d108...) that
doesn't match the repository's current submodule HEAD (e2d33e4...), causing a
dirty submodule state; update the pinned SHA in the specs file to the actual
submodule commit (e2d33e4...) or reset the submodule to the pinned SHA, ensure
the working tree is clean, verify the submodule contains the expected files
(e.g., execution_bust.py and spot_market_summary.py) and then commit the
corrected submodule pointer (or update the specs entry) so the repo and specs
stay in sync before merging.
In `@tests/helpers/builders/order_builder.py`:
- Around line 299-305: Add missing docstrings to the TriggerOrderBuilder methods
reduce_only() and client_order_id(): for reduce_only, add a concise description
stating it marks the order as reduce-only, document the parameter value: bool
(default True), and the return type TriggerOrderBuilder; for client_order_id,
add a concise description saying it sets a numeric client-side order identifier,
document the parameter client_order_id: int and the return type
TriggerOrderBuilder. Keep wording and formatting consistent with the existing
builder method docstrings in the class.
In `@tests/test_orderbook/conftest.py`:
- Around line 40-47: The pytest CLI option --orderbook-perp-asset is never read;
update the perp_market_config fixture to use
request.config.getoption("orderbook-perp-asset") (or fallback to
os.environ.get("ORDERBOOK_PERP_ASSET")) instead of only reading the environment,
or alternatively remove the unused pytest_addoption definition; locate
pytest_addoption and perp_market_config in tests/test_orderbook/conftest.py and
implement the getoption path so the CLI flag is honored.
- Around line 97-101: The current except block in the fixture swallows errors
and sets a hardcoded oracle_price = 3000.0 which can cause flakiness; update the
except handling for maker_tester_session.data.current_price(symbol) so it either
(a) uses an asset-specific fallback map (e.g., ASSET_FALLBACKS.get(symbol) and
default to a safe sentinel) and logs which fallback was chosen via
logger.warning, or (b) re-raises or raises a custom exception to fail the
fixture instead of continuing with a possibly incorrect price; change the
oracle_price assignment and the logger.warning call accordingly to reference the
chosen fallback or the raised error.
In `@tests/test_orderbook/test_limit_orders.py`:
- Around line 57-85: The test_mass_cancel_clears_open_orders currently accepts
market_type but never uses it (Ruff ARG001); update the test to include
market_type in assertion messages for better diagnostics by (1) changing the
assert response.order_id is not None to include a message that references
market_type and market_config.symbol (so failures show which market type failed)
and (2) after each await maker_tester.wait.for_order_state(order_id,
OrderStatus.CANCELLED) add an explicit assert (or augment any existing
assertion) that the order state is CANCELLED with a failure message containing
market_type and order_id (use the test function name
test_mass_cancel_clears_open_orders and symbols like response.order_id,
order_id, market_type, market_config.symbol to locate where to add the
messages).
In `@tests/test_perps/test_position_management.py`:
- Line 12: Remove the module-level skip by deleting or disabling the pytestmark
= pytest.mark.skip(...) in test_position_management.py; instead, mark only the
specific failing tests (e.g., with `@pytest.mark.skip` or `@pytest.mark.xfail` on
the individual test functions that need it) or gate them behind a conditional
check for the matching-engine version, so the rest of the module still runs in
CI; ensure tests use pytest-recording/vcrpy decorators or fixtures (e.g.,
`@pytest.mark.vcr` or provided vcr_fixture) and keep coverage by running under
pytest-cov as per project test guidelines.
In `@tests/test_spot/test_api_validation.py`:
- Around line 13-21: The test module is entirely skipped via the pytestmark skip
marker which removes critical signature/nonce/deadline validation coverage;
remove the pytestmark skip and update the disabled tests to use the new signing
helpers (replace SignatureGenerator.encode_inputs_limit_order and
SignatureGenerator.sign_raw_order with sign_order, sign_cancel_order,
sign_mass_cancel) and the unified CreateOrderRequest/Cancels schema (rename
expiresAfter -> deadline where the signature deadline is populated), ensure
tests use pytest-recording/vcrpy for HTTP fixtures and include pytest-cov hooks
so they run under CI; locate the pytestmark variable and the old tests that
construct orders manually and rewrite them to call the new sign_* APIs and
unified request builders before re-enabling.
---
Outside diff comments:
In `@sdk/open_api/__init__.py`:
- Around line 10-17: Update the package version constant to match the OpenAPI
document change: replace the current __version__ value ("2.1.7.0") with "2.3.0"
in the module where __version__ is defined, and ensure the module
docstring/comment referencing the OpenAPI document version remains consistent
with the new value.
In `@sdk/open_api/models/create_order_response.py`:
- Line 34: The generated line for the field "order_id" in
create_order_response.py exceeds the 120-char limit; instead of editing this
generated line, update the OpenAPI generator/template configuration to enforce a
120-character max line length (wrap long Field descriptions or enable line
wrapping) so generated model fields like order_id: Optional[StrictStr] =
Field(..., description="...", alias="orderId") are emitted within 120 chars;
adjust the generator's mustache template or code formatter settings used in
generation so all future outputs conform to the 120-character guideline.
In `@sdk/open_api/models/market_definition.py`:
- Line 40: The __properties class variable declaration in MarketDefinition
exceeds the 120-character limit; split the list literal across multiple lines so
each line is ≤120 chars (e.g., one element per line or grouped) and then update
the OpenAPI/python generator template or its configuration that produces
sdk/open_api/models/market_definition.py so all generated **/*.py files format
__properties (and similar long lists) with line wrapping under 120 chars, then
regenerate the models to apply the fix; target symbols: __properties in the
MarketDefinition model (and the generator template that emits the class property
list).
In `@sdk/reya_rest_api/client.py`:
- Around line 186-237: The unified order methods (create_limit_order and the
trigger-order method around lines 242-305) must reintroduce market-type
validation: use _get_market_id_from_symbol(params.symbol) then fetch the market
type (e.g., via an existing helper like _get_market_by_id or a new
_is_perp_market/_is_spot_market) and enforce the rules — if creating a trigger
order ensure the market is perp and raise ValueError if not; for limit (and
other non-trigger) orders ensure that reduceOnly is omitted (pass None) when the
market is spot (do not serialize reduceOnly=False), otherwise allow reduceOnly
for perp markets; update the CreateOrderRequest construction (the reduceOnly and
any trigger-specific fields) accordingly so spot orders never include reduceOnly
and trigger orders are rejected on spot.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 15256755-34d8-4b77-9d78-2410319b37f9
📒 Files selected for processing (92)
.openapi-generator/FILESexamples/rest_api/perps/order_entry.pypyproject.tomlsdk/async_api/execution_bust.pysdk/async_api/execution_type.pysdk/async_api/market_execution_bust_update_payload.pysdk/async_api/market_summary.pysdk/async_api/order.pysdk/async_api/order_type.pysdk/async_api/perp_execution.pysdk/async_api/spot_market_summary.pysdk/async_api/spot_market_summary_update_payload.pysdk/async_api/spot_markets_summary_channel.pysdk/async_api/spot_markets_summary_update_payload.pysdk/async_api/wallet_execution_bust_update_payload.pysdk/open_api/__init__.pysdk/open_api/api/market_data_api.pysdk/open_api/api/order_entry_api.pysdk/open_api/api/reference_data_api.pysdk/open_api/api/specs_api.pysdk/open_api/api/wallet_data_api.pysdk/open_api/api_client.pysdk/open_api/configuration.pysdk/open_api/exceptions.pysdk/open_api/models/__init__.pysdk/open_api/models/account.pysdk/open_api/models/account_balance.pysdk/open_api/models/account_type.pysdk/open_api/models/asset_definition.pysdk/open_api/models/cancel_order_request.pysdk/open_api/models/cancel_order_response.pysdk/open_api/models/candle_history_data.pysdk/open_api/models/create_order_request.pysdk/open_api/models/create_order_response.pysdk/open_api/models/depth.pysdk/open_api/models/depth_type.pysdk/open_api/models/execution_bust.pysdk/open_api/models/execution_bust_list.pysdk/open_api/models/execution_type.pysdk/open_api/models/fee_tier_parameters.pysdk/open_api/models/global_fee_parameters.pysdk/open_api/models/level.pysdk/open_api/models/liquidity_parameters.pysdk/open_api/models/market_definition.pysdk/open_api/models/market_summary.pysdk/open_api/models/mass_cancel_request.pysdk/open_api/models/mass_cancel_response.pysdk/open_api/models/order.pysdk/open_api/models/order_status.pysdk/open_api/models/order_type.pysdk/open_api/models/pagination_meta.pysdk/open_api/models/perp_execution.pysdk/open_api/models/perp_execution_list.pysdk/open_api/models/position.pysdk/open_api/models/price.pysdk/open_api/models/request_error.pysdk/open_api/models/request_error_code.pysdk/open_api/models/server_error.pysdk/open_api/models/server_error_code.pysdk/open_api/models/side.pysdk/open_api/models/spot_execution.pysdk/open_api/models/spot_execution_list.pysdk/open_api/models/spot_market_definition.pysdk/open_api/models/spot_market_summary.pysdk/open_api/models/tier_type.pysdk/open_api/models/time_in_force.pysdk/open_api/models/wallet_configuration.pysdk/open_api/rest.pysdk/reya_rest_api/auth/signatures.pysdk/reya_rest_api/client.pysdk/reya_rest_api/config.pysdk/reya_rest_api/constants/enums.pysdk/reya_rest_api/models/orders.pysdk/reya_websocket/resources/market.pysdk/reya_websocket/resources/wallet.pysdk/reya_websocket/socket.pyspecstests/helpers/builders/order_builder.pytests/helpers/reya_tester/checks.pytests/helpers/reya_tester/data.pytests/helpers/reya_tester/waiters.pytests/helpers/reya_tester/websocket.pytests/test_orderbook/__init__.pytests/test_orderbook/conftest.pytests/test_orderbook/test_execution_busts.pytests/test_orderbook/test_limit_orders.pytests/test_perps/test_dynamic_pricing.pytests/test_perps/test_limit_orders.pytests/test_perps/test_position_management.pytests/test_perps/test_trigger_orders.pytests/test_spot/test_api_validation.pytests/test_spot/test_spot_execution_busts.py
💤 Files with no reviewable changes (4)
- sdk/reya_rest_api/constants/enums.py
- tests/test_spot/test_spot_execution_busts.py
- sdk/reya_rest_api/config.py
- tests/test_perps/test_dynamic_pricing.py
Reflects the new devnet1 environment in reya-devops (perpOB-enabled testnet that replaces cronos). Defaults flip to api-devnet.reya-cronos.network / websocket-devnet.reya-cronos.network; chain id stays 89346162. Resolves the three pytest.mark.skip TODOs from the previous commit: - tests/test_perps/test_limit_orders.py — rewritten with the maker/taker pattern (PERP_ACCOUNT_ID_1 rests GTC, PERP_ACCOUNT_ID_2 IOCs through it). Covers IOC fills, GTC resting, reduce_only-without-position rejection, and perp mass-cancel (newly supported under perpOB). - tests/test_perps/test_position_management.py — rewritten as maker/taker open-long, open-short, increase-long, and reduce-only close. - tests/test_spot/test_api_validation.py — wholesale skip removed; all 26 signature/nonce/deadline tests ported to sign_order / sign_cancel_order and the renamed `deadline` field on CreateOrder/Cancel/MassCancel requests. test_spot_order_missing_expiration → test_spot_order_missing_deadline to reflect the v2.3.0 schema rename. Infra: - ReyaTester gains a perp_account_number arg (mutually exclusive with spot_account_number) and a shared _create_client_for_account helper. - tests/conftest.py adds perp_maker_tester_session / perp_taker_tester_session and their function-scoped wrappers, mirroring the spot maker/taker pattern. - .env.example documents PERP_ACCOUNT_ID_2 / PRIVATE_KEY_2 / WALLET_ADDRESS_2. Also fixes a Modelina codegen indent bug in sdk/async_api/spot_market_summary.py. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
🤖 SDK Version Check ✅ Version check passed
|
… AMM tests test_perps/test_market_data.py - Port to the v2.3.0 MarketSummary schema: oi_qty (single, no long/short split), no funding_rate_velocity, throttled_oracle_price → mark_price, throttled_pool_price → throttled_mid_price. - Tolerate Price.pool_price being None on perpOB-only markets where the AMM pool-price publisher no longer ticks. - Update test_market_perp_executions for v2.3.0 PerpExecution: taker_fee / taker_account_id renames + DUST execution type. test_orderbook/ — five new shared lifecycle test files - conftest.py: PerpTestConfig now mirrors SpotTestConfig's full liquidity- detection surface (refresh_order_book, has_*_liquidity, best_*_price, circuit_breaker_*, get_usable_*_price_for_qty, safe-no-match prices). Adds parametrized `maker` and `taker` fixtures that select the right ReyaTester based on `market_type`. - test_order_cancellation.py: cancel by order_id, mass-cancel, cancel-of- cancelled, cancel of unknown order_id (the last from old AMM coverage). - test_self_match_prevention.py: GTC/IOC self-match prevention plus a cross-account sanity match. Skips when external liquidity could interfere with the controlled-book assertions. - test_websocket_events.py: orderChanges on create + cancel, depth subscription liveness. Balance-update verification stays in test_spot/ (perp settles to positions, not asset balances). - test_ioc_orders.py: full fill against resting maker, no-liquidity IOC unfills immediately, partial-fill IOC drops the remainder. - test_maker_taker_matching.py: end-to-end maker→taker match with REST depth check and WS order-changes / execution events. Spot-specific balance-delta assertions stay in tests/test_spot/. test_perps/test_position_management.py — recover transferrable AMM coverage Adds increase_short, partial_close_long / _short, and decrease_without_ reduce_only — adapted from the old AMM single-account flow to the new maker/taker pattern using the perp_maker_tester / perp_taker_tester fixtures. GitHub issue #52 tracks the RPC-layer follow-up (OrdersGatewayProxy ABI, settle_fill / cancel_nonce actions, PassivePerpExecutionV3 decoder). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
🤖 SDK Version Check ✅ Version check passed
|
Run pre-commit equivalents in a fresh venv (Python 3.9.6 — only version available locally; project targets 3.12+ for execution but 3.10 for mypy). Mechanical fixes: - black reformat: 8 files, all length/wrap differences picked up by black 24.10 vs whatever ran before. - isort fix: signatures.py import ordering. - mypy: drop the now-unused ``# type: ignore[attr-defined]`` on the websocket import (newer websocket-client exposes WebSocket/WebSocketApp directly), and silence four ``arg-type`` mismatches in ``ReyaSocket.__init__`` where the public callbacks are typed against ``WebSocket`` for callsite ergonomics while WebSocketApp expects ``WebSocketApp`` as the first arg — interchangeable at runtime. - flake8: drop two F401 unused imports introduced by my recent ports (``OrderStatus`` in test_ioc_orders.py, ``ReyaTester`` in test_orderbook conftest). - pylint: add ``# pylint: disable=redefined-outer-name`` on the market_config/maker/taker fixtures (standard pytest fixture-injection pattern, mirrors how tests/conftest.py handles it). Use ``_ = market_type`` in the two test bodies that depend on the [spot, perp] parametrization but don't reference the param explicitly, so unused-argument doesn't fire. Final state with the project flake8 ignores (E501, E203, W503): - black --check: 120 files unchanged - flake8: clean - mypy: 16 source files, no issues - pylint on test_orderbook: 10.00/10 Pre-existing pylint quirks not addressed (unrelated to this migration): - ``no-value-for-parameter`` false positive on ``Account.from_key(...)`` — pylint doesn't pick up eth_account's classmethod signature. - ``unsupported-binary-operation`` (E1131) on ``int | None`` annotations in pre-existing test files — only fires on Python 3.9; project runs 3.12. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
🤖 SDK Version Check ✅ Version check passed
|
Run the project's full pre-commit hook chain (pyupgrade, isort, black, flake8, bandit, pylint, mypy, poetry-check, yamlfmt, jsonschema) end-to-end on Python 3.12. Everything passes. Fixes the migration introduced or exposed: - pyupgrade auto-rewrote ``Union[A, B]`` → ``A | B`` across the new test_orderbook/ files; dropped the now-unused ``from typing import Union``. - sdk/reya_websocket/socket.py: restored the ``# type: ignore[attr-defined]`` on the websocket-client import (mypy stubs in the project's poetry env still don't expose ``WebSocket``/``WebSocketApp`` directly), dropped the ``# type: ignore[arg-type]`` shims I added in the previous lint pass (no longer needed once the attr-defined ignore is back), and ``cast(...)`` the five ``model_validate`` returns in ``_parse_message`` to match the existing channel_data branch. - sdk/reya_rest_api/client.py: ``cast(...)`` every ``await self.<api>.<m>(...)`` return so the public methods stop bleeding ``Any`` from the openapi-generated surface (mypy.overrides ignores ``sdk.open_api.*``, which makes its return types Any to the rest of the codebase). - tests/test_orderbook/conftest.py: assert ``market_def is not None`` after the pytest.skip narrows the Optional, drop a stale ``# type: ignore``, and pin ``request.param`` to ``str`` via an annotated local. - tests/test_orderbook/test_execution_busts.py: bind the websocket reference to a local + assert it's not None after the early skip so the loop body type- checks against ``ReyaSocket``. - tests/test_perps/test_trigger_orders.py + examples/rest_api/perps/order_entry.py: pass the now-required ``symbol`` (and ``account_id``) to ``cancel_order``; matches the v2.3.0 client signature. - tests/test_spot/test_api_validation.py: assert balance is not None after the Optional pytest.skip pattern in the two insufficient-balance tests. Pre-existing-on-main fixes hoovered up while in the area: - sdk/reya_rpc/utils/execute_core_commands.py: drop a ``# type: ignore[no-any-return]`` that's now unused (mypy resolves the Web3 receipt type without it). - tests/test_spot/test_state_resilience.py: assert ``order_id is not None`` before using it as a dict key / EventStore lookup arg. Config: - pyproject.toml: bump pylint ``max-module-lines`` from 2000 to 2050. The ported test_api_validation.py is at 2002 lines after the v2.3.0 sign_order rewrite added per-test ``Decimal()`` wrappers; the file's coherent and the former limit was arbitrary. Verified end-to-end with ``pre-commit run --all-files`` on Python 3.12.13 in a clean venv with the project's poetry deps installed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
🤖 SDK Version Check ✅ Version check passed
|
…tgun
Adversarial review surfaced two real issues; rest of the agent's findings were
either already covered or refuted by checking the canonical TS SDK at
reya-off-chain-monorepo/packages/sdk/src/services/orders/orderV2.ts.
1. sdk/reya_websocket/socket.py — wire up the spot market summary channels.
The AsyncAPI spec defines /v2/spotMarkets/summary and /v2/spotMarket/{symbol}/summary
and the regen produces SpotMarketSummaryUpdatePayload + SpotMarketsSummaryUpdatePayload,
but the dispatcher had no case for them. Subscribing today raised
``WebSocketDataError: Unknown channel`` even though the channel is valid. Adds the
imports, extends WebSocketMessage, the exact-match table, and the parameterized
``/v2/spotMarket/.../summary`` branch in _get_payload_type.
2. sdk/reya_rest_api/models/orders.py — make TriggerOrderParameters.qty required.
The previous default of qty="0.01" existed only to keep the inherited
test_perps/test_trigger_orders.py compiling without rewrites. Production users
calling create_trigger_order(...) without qty would silently sign a 0.01-sized
stop-loss / take-profit even when their actual position was larger, leaving them
with the wrong risk after the trigger fires. There's no safe default for "size of
my open position" — make it explicit. Updates the 14 callsites in
test_trigger_orders.py and the 4 in examples/rest_api/perps/order_entry.py to
pass qty="0.01" matching the 0.01-sized positions those tests actually open.
Verified that pre-commit (pyupgrade, isort, black, flake8, bandit, pylint, mypy,
poetry-check, yamlfmt, jsonschema) is clean on Python 3.12.13.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
🤖 SDK Version Check ✅ Version check passed
|
Confirms the Python sign_order / sign_cancel_order / sign_mass_cancel helpers
in sdk/reya_rest_api/auth/signatures.py produce byte-identical signatures to
the canonical TypeScript impl in
reya-off-chain-monorepo/packages/common/src/transactions/sign.ts.
Layout:
- tests/parity/sign_ts.mjs — node script using ethers v6 signTypedData against
pinned typed-data definitions (orderTypes, orderCancelTypes, massCancelTypes
copied verbatim from the TS source) and a fixed payload + hardhat test key.
Outputs JSON with the three signature hex strings.
- tests/parity/test_signature_parity.py — pytest with the TS-generated hex
hardcoded in EXPECTED_SIGNATURES; exercises the Python helpers with the
same inputs and asserts byte equality.
- tests/parity/README.md — how to run and how to regenerate when the TS impl
evolves.
- tests/parity/package.json + package-lock.json — pin ethers v6 for the harness.
node_modules/ is already in .gitignore.
Test vector: hardhat well-known key 0xac09…ff80 (signer 0xf39F…2266) signing
against the testnet OrdersGateway 0x5a0a…7ca5 on chain id 89346162. The Order
covers a LIMIT IOC perp buy with all 13 OrderDetails fields populated; the
OrderCancel and MassCancel exercise the compact uint64 envelopes including
mass_cancel's marketId=0 ("cancel all markets") fallback that matches the TS
SDK's params.marketId ?? 0 in massCancelMEOrders.
Verified locally: all three parity assertions pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
🤖 SDK Version Check ✅ Version check passed
|
…ketSummary PerpExecution split account_id/fee into taker_*/maker_*; MarketSummary lost long_oi_qty/short_oi_qty/throttled_oracle_price/throttled_pool_price and gained mark_price/throttled_mid_price. Update the WS monitoring examples and ReyaTester helpers (matchers, perp_trade_context, checks, websocket) to read from the new schema. Match perp executions on either taker or maker account_id since the expected order's account is on one side of the trade. Fixes mypy attr-defined errors in CI Lint / pre-commit (3.10).
- sdk/reya_rest_api/client.py: drop 12 cast(...) wrappers — the openapi client methods already return the typed result, mypy was flagging them as redundant-cast. - tests/helpers/reya_tester/data.py: same for cast(Depth, ...). - sdk/reya_rpc/utils/execute_core_commands.py: web3.eth.* returns Any, cast the receipt to TxReceipt at the boundary so the annotated return type is honored (no-any-return). - tests/parity/test_signature_parity.py: black wanted the assert messages inlined instead of paren-wrapped; also pin a file-level pylint disable for redefined-outer-name (the standard pytest-fixture pattern that pylint W0621 can't tell apart from real shadowing). Fixes the remaining mypy errors and the black + pylint hooks in CI.
Pylint on lower Python versions (and on user-class A | B unions in non- annotation contexts) flags E1131 unsupported-binary-operation. Adding `from __future__ import annotations` makes annotations lazy strings (PEP 563), so pylint never tries to runtime-evaluate the union and the issue disappears regardless of pylint host version. This matches the convention already used elsewhere in the repo (e.g. tests/test_perps/test_position_management.py).
…trings
- sdk/reya_rest_api/auth/signatures.py: reject negative qty in sign_order.
Caller is expected to pass unsigned qty + is_buy to set direction; a
negative qty would silently flip direction via `qty if is_buy else -qty`.
Fail fast with ValueError instead.
- tests/test_orderbook/conftest.py: wire --orderbook-perp-asset CLI flag
through request.config.getoption so it actually does something (was dead
code; fixture only read the env var). Drop the silent 3000.0 oracle-price
fallback — a wrong oracle price invalidates every downstream test, so
let the OSError/ValueError surface.
- sdk/reya_websocket/resources/{market,wallet}.py: add docstrings on the
new ExecutionBusts resource/subscription classes for parity with the
surrounding resources.
- tests/helpers/builders/order_builder.py: docstrings on new reduce_only()
and client_order_id() trigger-builder methods.
|
🤖 SDK Version Check ✅ Version check passed
|
|
@coderabbitai review CI is green except |
|
Re-review requested by ✅ Actions performedFull review triggered. |
CI's pyupgrade hook rewrote Optional[X]/Union[A,B] → X | None / A | B in the three files we just touched with `from __future__ import annotations`, which made the corresponding `from typing import Optional, Union` imports truly unused (flake8 F401, pylint W0611). Apply the rewrites locally and drop the now-unused imports so pre-commit stays green from a clean tree.
|
🤖 SDK Version Check ✅ Version check passed
|
|
🤖 SDK Version Check ✅ Version check passed
|
|
🤖 SDK Version Check ✅ Version check passed
|
The non-mainnet default verifyingContract (0x5a0a...) is the older reya_cronos deployment and is stale for devnet1, where the live OrdersGateway is 0x7Ec89E555c771D2B5939aBE5C4E4291852633D4D — confirmed by reya-devops (ORDERS_GATEWAY_PROXY_ADDRESS across every devnet1 service incl. ws-exec) and the reya-deployments reya_devnet fork test. With REYA_ORDERS_GATEWAY unset, the SDK signed against the wrong contract and the matching engine rejected every order with an opaque "invalid signature" error. - config.py: point the non-mainnet baked-in default at the devnet1 gateway. - .env.example: set REYA_ORDERS_GATEWAY for devnet1 (+ commented mainnet value) so following the example works out of the box. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…test_spot_extreme_price The ~1e9 "Insufficient balance for spot order" warns on devnet1 trace back to this error-handling test signing a 1e12 limit price (qty × 1e12 ≈ 1e9), not a real bug (Linear PRO-160, closed not-a-bug). Server-side the spot balance check runs before px/qty validation, so an out-of-bounds price surfaces as "Insufficient balance" rather than "price exceeds maximum". Noted inline with a pointer to the low-priority server reorder (reya-off-chain-monorepo#2669) so the next person seeing those warns finds the answer instead of re-investigating. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…id collision Per review — the previous commit dropped the cronos testnet address. There are only two chains (cronos 89346162, mainnet 1729); devnet1 reuses the cronos chain id with a different OrdersGateway, so they can't be auto-distinguished by chain id. Restructure to keep all three addresses explicit (mainnet / cronos_testnet / devnet1) in one map, default non-mainnet to devnet1 (the current perpOB target), and document that cronos is reachable via REYA_ORDERS_GATEWAY. .env.example: devnet1 now works by default, so the override is commented (shown for clarity). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
🤖 SDK Version Check 📝 Manual version changes detected ✅ Version validation passed:
|
Per review — cronos is a separate (not legacy) environment. It shares devnet1's chain id (89346162) but has its own endpoints (api-cronos.reya.xyz, websocket-testnet/ws-exec-testnet.reya.xyz) and gateway, sourced from the reya-devops cronos deployment. Give it a distinct, parallel .env.example block instead of burying it in the devnet1 section's comment, and call it "cronos testnet" rather than "legacy". Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…llback Drop the dict whose "cronos_testnet" key was never selected. Define the three gateways as named module constants, resolve via REYA_ORDERS_GATEWAY (set per environment in .env.example) with a simple mainnet/devnet1 fallback by chain id; cronos is reached via the explicit override (it shares devnet1's chain id). .env.example: the devnet1 gateway is now set explicitly alongside the URLs. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Fix devnet1 OrdersGateway address (stale default broke EIP-712 signing)
|
🤖 SDK Version Check 📝 Manual version changes detected ✅ Version validation passed:
|
…ified on devnet The ME now echoes the cancelled order's resolved orderId on a successful clientOrderId cancel (PRO-143, deployed to devnet1), so the server response satisfies the orderId-required CancelOrderResponse schema. Removed the xfail marker so `test_spot_cancel_by_client_order_id` is a real regression guard — confirmed passing in the full devnet1 suite (175 passed, 0 failed). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
🤖 SDK Version Check 📝 Manual version changes detected ✅ Version validation passed:
|
…y (PRO-164) PRO-164 changed the devnet1 OrdersGateway (config now returns 0x7Ec89E…633D4D) — the EIP-712 verifyingContract for order/cancel signing. The offline signature-parity vectors were pinned to the old 0x5a0a… address, so the setup assertion (config == pinned OrdersGateway) errored on all 5 parity tests after the PRO-164 merge (#56). Regenerated the TS-reference signatures via `node sign_ts.mjs` against the new OrdersGateway and re-pinned them; updated ORDERS_GATEWAY in both sign_ts.mjs and test_signature_parity.py. All 5 parity tests pass — Python↔TS EIP-712 parity re-confirmed at the new address (no signing drift from the OrdersGateway change). 0x7Ec8… is the verified-correct devnet1 OrdersGateway (the full devnet1 suite signs + settles orders with it). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
🤖 SDK Version Check 📝 Manual version changes detected ✅ Version validation passed:
|
…dity `test_perp_reduce_only_rejected_without_position` rests a setup maker SELL at oracle*1.04 to give the reduce-only IOC a counterparty. When the shared devnet book has external liquidity (an MM bot quoting near pool price, ~+16% over oracle), that maker SELL crosses immediately instead of resting, and settling the resulting fill against the external counterparty currently 500s (off-chain settlement-robustness bug, PRO-191) — so the test fails hard instead of skipping. Add the same `skip_if_external_liquidity` guard its siblings already use (e.g. `test_perp_ioc_taker_buy_matches_maker_sell`) so it self-skips on a dirty book. Verified: the test now SKIPS cleanly against the live devnet book. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
🤖 SDK Version Check 📝 Manual version changes detected ✅ Version validation passed:
|
…from expiresAfter - Sign postOnly as part of the EIP-712 OrderDetails (immediately after reduceOnly) via a shared _ORDER_DETAILS_TYPE; add GTT to the signing TimeInForce enum; sign_order takes an optional post_only. - Decouple deadline (entry-only signature validity) from expiresAfter (order lifetime; 0 = perpetual) and drop the far-future-lifetime stopgap. Only GTT carries a non-zero expiresAfter (which must exceed the deadline); GTC and IOC always sign 0. - Gate post_only: rejected on IOC, and rejected pending end-to-end wire + settlement support (postOnly=false signs and verifies identically today). - Add post_only to LimitOrderParameters. - Tests: golden OrderDetails struct-hash parity vs the on-chain canonical value; regenerate TS<->Py signature parity vectors to 14 fields (+ a postOnly vector); wire-serialization tests for the decoupling and the post_only gates. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
🤖 SDK Version Check 📝 Manual version changes detected ✅ Version validation passed:
|
…es pending off-chain - Bump the specs submodule to api-specs feat/perpOB (postOnly on CreateOrderRequest, GTT in the TimeInForce enum) and regenerate open_api/. The wire model now transports postOnly (previously dropped) and exposes GTT. The sync also pulls in the branch's perp-route aliases and a doc-version bump to 3.0.1 — generated-code churn, not behavioral. - client.py: add a GTT entry gate (reject pending off-chain support, avoiding a KeyError on the GTC/IOC-only TIF map) and refresh the postOnly gate rationale now that the model carries the field. The only remaining blocker for post_only=True / GTT is the off-chain 14-field digest reconstruction; default-False postOnly and GTC/IOC are unaffected. - tests: add a GTT-rejection guard; update the postOnly-gate test wording. Staging only — un-gating is a one-line change once the off-chain side reconstructs 14 fields. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
🤖 SDK Version Check ✅ Version check passed
|
|
🤖 SDK Version Check 📝 Manual version changes detected ✅ Version validation passed:
|
The specs submodule now pins the api-specs 3.0.1 release tag (postOnly/GTT), so the SDK package version is bumped to match the spec version prefix per the Version Check policy. Regenerated open_api/ version strings accordingly. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
🤖 SDK Version Check 📝 Manual version changes detected ✅ Version validation passed:
|
The ws-exec createOrder frame $refs CreateOrderRequest, so the AsyncAPI-generated models also carry postOnly + GTT now. Keeps the committed generated code in sync with a fresh generate-ws.sh run (CI enforces this). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
🤖 SDK Version Check 📝 Manual version changes detected ✅ Version validation passed:
|
Cover the order/cancel validation gates without a devnet round-trip: - reduce_only rejected on spot and on TP/SL trigger orders - cancel rejected with neither order_id nor client_order_id - cancel accepts both ids and carries both on the wire (server resolves precedence) Complements the existing post_only / GTT / IOC entry-gate guards. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
🤖 SDK Version Check 📝 Manual version changes detected ✅ Version validation passed:
|
…r rule The five signature/nonce validation tests hand-build raw GTC order requests (to inject a bad signature or nonce) and hardcoded expiresAfter=deadline, signing the same value. Now that lifetime is decoupled from deadline, the server rejects a non-zero expiresAfter on IOC/GTC/TP-SL orders with an input validation error before reaching the signature/nonce check each test targets, so all five failed on the wrong layer. Set expiresAfter=0 on the wire and expires_after=0 in the matching signature (so it recovers against the same envelope), and replace the now-inverted comments. This fixes the tests and confirms the decoupling is enforced end-to-end on the server. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
🤖 SDK Version Check 📝 Manual version changes detected ✅ Version validation passed:
|
danigr99727
left a comment
There was a problem hiding this comment.
Review — perpOB migration (signing + client rewrite)
✅ The EIP-712 signing rewrite is cryptographically correct. I verified OrderDetails (14 fields), the chainId-free domain, the OrderCancel/MassCancel envelopes, and the OrderType/TimeInForce enums byte-for-byte against the canonical sources: reya-network orders-gateway/src/libraries/OrderHashing.sol + utils/contracts/src/storage/Signature.sol (feat/perpOB, OrderHashing.sol@6d9b3b3fa) and the off-chain cancel verifier common-backend/src/eip712/cancel-types.ts. The golden-vector + TS↔Python parity harness is the right way to guard this.
Inline comments below cover the Critical + Important findings. Two items have no single line to attach to:
🔴 Critical — signature tests don't run in CI. The only workflows are pylint, version-consistency, publish-to-pypi, create-release — none invoke pytest. The golden/parity suite in tests/parity/ (the sole automated guard against EIP-712 field drift) is fully offline and runs in <10s. Please add poetry run pytest tests/parity/ to a PR workflow — otherwise a reordering of _ORDER_DETAILS_TYPE stays green in CI and only breaks on devnet.
📝 The PR description is stale in 3 places (will mislead whoever merges): "13-field" → actually 14 (postOnly added on-chain 2026-06-04); specs pin 653d108 → actually 62055120; the three test files described as "marked pytest.mark.skip" are not skipped (they hold 4 / 8 / 26 live tests).
post_only=True/GTT off "until off-chain reconstructs 14-field". A cross-repo check found the off-chain verifier is still 13-field — and because adding postOnly changes the OrderDetails typehash, a 13-field reconstruction would reject even postOnly=False signatures (this only reconciles if create-sigs are verified solely on-chain at settlement, which is 14-field and matches). Please confirm the create-path verification site + deploy sequencing.
Automated review via Claude Code — please apply judgment and flag any false positives.
| payload, _nonce = self.build_create_trigger_order_payload(params) | ||
| order_request = CreateOrderRequest(**payload) | ||
| return await self.orders.create_order(create_order_request=order_request) | ||
| return await self.orders.create_order(create_order_request=CreateOrderRequest(**payload)) |
There was a problem hiding this comment.
🔴 Critical — a silently-rejected order returns as success. CreateOrderResponse.status is a required OrderStatus that includes REJECTED, and order_id is Optional, but this returns the response unchecked. A 200 OK body carrying status=REJECTED, orderId=null is indistinguishable from a placed order — the trader believes they're resting/filled when they aren't.
resp = await self.orders.create_order(create_order_request=CreateOrderRequest(**payload))
if resp.status == OrderStatus.REJECTED:
raise OrderRejectedError(f"Order rejected by matching engine: {resp}")
return respCheap and correct whether or not the server uses 200 for rejects. (Compounds with the 13/14-field off-chain question in the review summary: if a 14-field sig is bounced by a 13-field verifier and the bounce comes back as 200+REJECTED, every order silently "succeeds".)
| supported by the API. | ||
| """ | ||
| payload, _nonce = self.build_create_trigger_order_payload(params) | ||
| return await self.orders.create_order(create_order_request=CreateOrderRequest(**payload)) |
There was a problem hiding this comment.
🔴 Critical — same class as the create_limit_order comment, and the stakes are higher here. create_trigger_order also returns unchecked, and it hardcodes expiresAfter=0 precisely so the TP/SL protects the position indefinitely. A silently-rejected stop-loss (200+REJECTED) leaves the trader believing they're protected when they're not — the worst failure mode for a trading client. Guard on resp.status == OrderStatus.REJECTED (and order_id is None) before returning.
| """Unsubscribe from wallet execution busts.""" | ||
| self.socket.send_unsubscribe(channel=self.path) | ||
|
|
||
|
|
There was a problem hiding this comment.
🟠 Important — WalletBalancesResource registers a channel it can't parse. Just below, __init__ (line 356) registers the template "/v2/wallet/{address}/balances", but WalletBalancesSubscription.path (line 382) is "/v2/wallet/{address}/accountBalances", and socket.py _get_payload_type only recognizes the …/accountBalances suffix. The live path (WalletResource.balances() → the Subscription) round-trips fine, but the inherited WalletBalancesResource.subscribe() would send …/balances → server rejects / WebSocketDataError("Unknown channel"). Dead today only because nothing calls it directly — a trap for the next caller. Fix: line 356 should use "/v2/wallet/{address}/accountBalances" to match the Subscription + parser.
| return REYA_DEX_ID | ||
|
|
||
| @property | ||
| def default_orders_gateway_address(self) -> str: |
There was a problem hiding this comment.
🟠 Important — an unknown chain_id silently signs against the wrong gateway. TradingConfig is not frozen, and chain_id has no valid-range type: an unrecognized value falls through here to the devnet1 gateway, and cronos shares chain-id 89346162 with devnet1 (so it relies on the REYA_ORDERS_GATEWAY override). A wrong verifyingContract silently invalidates every signature. Suggest @dataclass(frozen=True) and raising on an unknown chain instead of a silent fallback.
| @@ -0,0 +1,75 @@ | |||
| """ | |||
There was a problem hiding this comment.
🟠 Important — execution-bust field coverage regressed. The deleted tests/test_spot/test_spot_execution_busts.py had ~12 tests asserting per-record fields (reason, side ∈ {B,A}, qty>0, timestamp>0, wallet-vs-market consistency, pagination). This replacement (3 tests) checks only the list-envelope shape. The docstring acknowledges busts aren't deliberately triggered yet — reasonable as a transitional gap, but please file the follow-up so the field-level assertions come back.
| return hash_uint256 | ||
|
|
||
| def sign_raw_order( | ||
| def sign_order( |
There was a problem hiding this comment.
🟠 Important — the IntEnums are defined but bypassed at the boundary that matters. sign_order(order_type: int, ..., time_in_force: int) takes raw int, so OrderTypeInt/TimeInForceInt (above) provide no safety here — a wrong int = a wrong signed order type. IntEnum is an int subtype, so annotating order_type: OrderTypeInt, time_in_force: TimeInForceInt is a runtime no-op but lets mypy catch a bad caller (it would also flag the bare order_type=0 literals in the tests).
| is_buy: bool | ||
| qty: str | ||
| trigger_px: str | ||
| trigger_type: OrderType |
There was a problem hiding this comment.
🟠 Important — TriggerOrderParameters can be constructed in an illegal state. trigger_type: OrderType admits OrderType.LIMIT, which is incoherent for a trigger order and is only caught later at runtime inside the builder. Move the invariant to construction:
def __post_init__(self):
if self.trigger_type not in (OrderType.STOP_LOSS, OrderType.TAKE_PROFIT):
raise ValueError(f"trigger_type must be STOP_LOSS or TAKE_PROFIT, got {self.trigger_type!r}")|
|
||
| Create `.env` file with: | ||
| ``` | ||
| ACCOUNT_ID=your_account_id |
There was a problem hiding this comment.
🟠 Important — this setup block won't start the SDK. TradingConfig.from_env() reads PERP_ACCOUNT_ID_1 / PERP_PRIVATE_KEY_1 / PERP_WALLET_ADDRESS_1, not ACCOUNT_ID / PRIVATE_KEY — following this verbatim raises "PERP_WALLET_ADDRESS_1 ... is required". The example module paths above (examples.rest_api.wallet_example, examples.websocket.market_monitoring) are also missing the perps/ segment → ModuleNotFoundError. Please align with .env.example and the actual example layout.
|
|
||
| ## How it works | ||
|
|
||
| - [sign_ts.mjs](sign_ts.mjs) signs three v2.3.0 envelopes (Order, OrderCancel, |
There was a problem hiding this comment.
🟠 Important — "three" should be five. sign_ts.mjs emits five vectors (order, order_sell, order_post_only, order_cancel, mass_cancel), but this README says three envelopes / "copy the three hex strings" — a maintainer regenerating would silently drop the sell + post-only parity vectors. (Separately, the OrdersGateway address in the test-vector section is the cronos address 0x5a0a…7ca5, while the vectors actually sign against the devnet1 address 0x7Ec8…3D4D.)
| return True, required_margin(price_decimal, Decimal(qty), market_params.max_leverage) | ||
| except RECOVERABLE_EXC as e: | ||
| err = str(e).lower() | ||
| if "insufficient" in err or "margin" in err or "balance" in err: |
There was a problem hiding this comment.
🟠 Important — retry decisions from substring-matching exception text are brittle. Classifying recoverability via "insufficient"/"margin"/"balance" in str(e) breaks when the server's wording changes (a "position would exceed initial margin requirement" reject misses the keywords → the level is abandoned with a single WARNING) and false-positives on any error whose body happens to contain "balance". Prefer branching on e.status / a structured error code. It's example code, but it's the pattern users copy.
Aligns with reya-api-specs feat/perpOB (v2.3.0) and the off-chain perpOB migration tracked in Reya-Labs/reya-off-chain-monorepo#2575. Perpetual futures move from AMM execution to the matching engine, sharing the spot order envelope.
EIP-712 signing rewritten for the new flat 13-field Order/OrderDetails typehash (signatures.py); the old ConditionalOrder envelope, composite perp nonce, personal_sign cancel hack, and inputs:bytes blob are gone. client.py collapses to a single signing path with no spot-vs-perp branching, and mass_cancel works for both market types. WebSocket channels switch to the unified executionBusts stream; sdk/open_api/ and sdk/async_api/ regenerated against the new spec.
Tests: shared lifecycle tests parametrized over [spot, perp] under tests/test_orderbook/; deliberate-bust scenarios in test_spot/ deleted in favour of unified bust tests there. AMM-only tests (test_perps/test_dynamic_pricing.py) deleted; tests that need maker/taker fixtures or a sign_order rewrite (test_perps/test_limit_orders.py, test_perps/test_position_management.py, test_spot/test_api_validation.py) marked pytest.mark.skip with explicit migration TODOs.
RPC layer (sdk/reya_rpc/) intentionally untouched — on-chain settlement helpers are matching-engine territory and out of scope for this PR.
Known issues
version-consistencyCI check is expected red on this PR. Thespecssubmodule is pinned to commit653d108— the v2.3.0 schema this SDK targets — but that commit isn't tagged onReya-Labs/specs. The pin itself is intentional and correct; only the missing tag is blocking the check. Follow-up: tag653d108on the specs repo (e.g. as2.3.0) and re-run this job before final merge. Not blocking review.Summary by CodeRabbit
New Features
executionBustsAPI for both spot and perp markets.API Updates
TP/SL→TAKE_PROFIT/STOP_LOSS.deadlinefield to order requests.Version