Skip to content

chore: sync exchange proto types for per-market margin modes - #370

Open
ze97286 wants to merge 1 commit into
devfrom
feat/per-market-margin-mode-protos
Open

chore: sync exchange proto types for per-market margin modes#370
ze97286 wants to merge 1 commit into
devfrom
feat/per-market-margin-mode-protos

Conversation

@ze97286

@ze97286 ze97286 commented Jul 9, 2026

Copy link
Copy Markdown

Syncs the generated exchange types from injective-core's hybrid margin branch (per-market margin modes on a single subaccount):

  • CrossMarginParams gains max_isolated_active_derivative_markets_per_subaccount_per_denom (field 15) and includes the util_ratio line (field 14)
  • MsgUpdateSubaccountMarketRiskMode + response + RPC, and EventSubaccountMarketRiskModeUpdated
  • SubaccountMarketRiskModes (records list) and EffectiveSubaccountMarketRiskMode (per-market resolution) queries
  • SubaccountMarketRiskModeRecord genesis plumbing
  • Adjacent oracle/peggy/stream regeneration drift from the same core tree; message list refreshed (183 messages)

Tagged v1.61.0-cross-margin.8 for the interchaintest dependency in the core branch.

Summary by CodeRabbit

  • New Features

    • Added support for updating subaccount market risk modes, with new query and transaction endpoints.
    • Added new cross-margin settings, including liquidation cooldowns and spot order caps.
    • Introduced queries for resolving effective market risk mode and viewing stored overrides.
  • Bug Fixes

    • Improved validation for genesis data, cross-margin parameters, and order-related records.
    • Tightened authorization and order-handling logic for more consistent limit checking and partial liquidation support.
  • Chores

    • Deprecated older oracle types and updated peg/rate-limit messages to use Pyth-based identifiers.

@ze97286
ze97286 requested a review from dbrajovic July 9, 2026 03:19
@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds a subaccount market risk mode feature (message, store keys, genesis validation, and proto definitions), expands cross-margin parameters and validation, changes authz spend-limit gating logic to treat SpendLimit as optional, adds derivative order/fee-discount staking helpers, and deprecates several oracle types while removing oracle fields from peggy rate-limit messages.

Changes

Subaccount Market Risk Mode Feature

Layer / File(s) Summary
Proto definitions
proto/injective/exchange/v2/tx.proto, proto/injective/exchange/v2/events.proto, proto/injective/exchange/v2/genesis.proto, proto/injective/exchange/v2/query.proto, proto/injective/exchange/v2/exchange.proto
Adds MsgUpdateSubaccountMarketRiskMode, EventSubaccountMarketRiskModeUpdated, genesis record, query RPCs, and new CrossMarginParams fields.
Message registration and validation
chain/exchange/types/v2/codec.go, chain/exchange/types/v2/msgs.go, injective_data/chain_messages_list.json
Registers the new message in Amino/interface registries and implements Route, Type, ValidateBasic, GetSignBytes, GetSigners.
Store keys
chain/exchange/types/key.go
Adds prefix constants and key builders for risk profiles, market discovery, liquidation tracking, spot order denom indexing, and risk-mode overrides.
Genesis validation
chain/exchange/types/v2/genesis.go
Adds validation of spot orderbook markets, derivative settlement schedule markers, and subaccount market risk mode records with duplicate/shape checks.

Cross-Margin Parameters

Layer / File(s) Summary
Params constants and validation
chain/exchange/types/v2/params.go, chain/exchange/types/errors.go
Adds new limit constants, extends DefaultCrossMarginParams/Validate with backstop ratio, liquidation cooldown, spot order cap checks, and two new registered errors.

Authz Spend-Limit Gating

Layer / File(s) Summary
Optional SpendLimit handling
chain/exchange/types/authz_exchange_generic.go, chain/exchange/types/v2/authz_exchange_generic.go
Accept now treats SpendLimit == nil as unlimited and requires getHold to succeed before enforcing spend limits.

Derivative Orders and Fee Discounts

Layer / File(s) Summary
Order helpers
chain/exchange/types/v2/derivative_orders.go
Adds NewMarketOrderForPartialLiquidation, DerivativeLimitOrder.Copy, and simplifies cancel-refund calculation.
Fee-discount staking utilities
chain/exchange/types/v2/derivative.go, chain/exchange/types/v2/fee_discounts.go
Adds CrossMarginPoolKey, clone/merge helpers for FeeDiscountStakingInfo, new batch execution fields, and memoized read-only rate lookups.

Oracle Type Deprecation & Peggy Cleanup

Layer / File(s) Summary
Oracle validation and enum
chain/exchange/types/proposal.go, chain/exchange/types/v2/proposal.go, proto/injective/oracle/v1beta1/oracle.proto
Removes Razor/Dia/API3/Uma from accepted oracle validation branches and marks them deprecated in the enum.
Peggy rate limit fields
proto/injective/peggy/v1/msgs.proto, proto/injective/peggy/v1/rate_limit.proto
Removes token_oracle_type/new_token_oracle_type fields and the oracle proto import; updates price ID comments.

Estimated code review effort: 4 (Complex) | ~75 minutes

Possibly related PRs

  • InjectiveLabs/sdk-go#369: Both PRs modify GenericExchangeAuthorization.Accept in the same files, changing spend-limit/hold gating behavior.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 60.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: syncing exchange proto types for per-market margin modes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/per-market-margin-mode-protos

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (5)
chain/exchange/types/v2/derivative_orders.go (1)

47-75: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Optional: fold NewMarketOrderForLiquidation into the partial variant to remove duplication.

The two constructors are identical apart from the quantity argument; NewMarketOrderForLiquidation could delegate to NewMarketOrderForPartialLiquidation(position, position.Quantity, ...).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@chain/exchange/types/v2/derivative_orders.go` around lines 47 - 75,
`NewMarketOrderForLiquidation` and `NewMarketOrderForPartialLiquidation`
duplicate the same order construction logic, differing only in the quantity
used. Update `NewMarketOrderForLiquidation` to delegate to
`NewMarketOrderForPartialLiquidation` using `position.Quantity` for the close
quantity, and keep all shared fields and order type selection centralized in the
partial-liquidation constructor.
chain/exchange/types/v2/fee_discounts.go (2)

128-147: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

Lazy mutex init in SetReadOnlyFeeDiscountRate is itself racy.

If c.readOnlyFeeDiscountMux == nil, two goroutines can each allocate a distinct mutex before either takes the lock, defeating the synchronization. Since NewFeeDiscountConfig always initializes the mux, this branch only matters for configs built outside the constructor. Prefer requiring construction via NewFeeDiscountConfig (or sync.Once) rather than lazy init under contention.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@chain/exchange/types/v2/fee_discounts.go` around lines 128 - 147, The lazy
initialization in SetReadOnlyFeeDiscountRate is racy because multiple goroutines
can create different readOnlyFeeDiscountMux instances before locking. Update
FeeDiscountConfig so the mutex is guaranteed to be initialized through
NewFeeDiscountConfig (and avoid nil-branch lazy init here), or protect one-time
initialization with sync.Once; keep the SetReadOnlyFeeDiscountRate and
readOnlyFeeDiscountRates access synchronized on the single shared mutex.

241-268: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Optional: reduce duplication with NewFeeDiscountStakingInfo.

This constructor repeats the full map/mutex initialization block of NewFeeDiscountStakingInfo, differing only in field sources. A shared private helper for the empty-maps+muxes skeleton would keep the two in sync as fields evolve.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@chain/exchange/types/v2/fee_discounts.go` around lines 241 - 268,
NewFeeDiscountStakingInfoForReadOnlyLookup duplicates the empty map and mutex
setup from NewFeeDiscountStakingInfo, which risks the two constructors drifting
apart. Extract the shared initialization for FeeDiscountStakingInfo into a
private helper and have both constructors call it, then apply only their
differing field assignments (for example Schedule, timestamps, and cache
references) in each constructor.
proto/injective/peggy/v1/rate_limit.proto (1)

36-37: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Reserve field number 8 on persisted RateLimit.

token_oracle_type (field 8) was removed but not reserved. RateLimit is persisted chain state, so re-assigning tag 8 to a new field later would misdecode already-stored records. Add reserved 8; (and the field name) unless the upstream injective-core tree deliberately leaves it open.

♻️ Suggested reservation
   // transfers that occurred within the sliding window
   repeated BridgeTransfer transfers = 7 [ deprecated = true ];
+
+  reserved 8;
+  reserved "token_oracle_type";
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@proto/injective/peggy/v1/rate_limit.proto` around lines 36 - 37, The
persisted RateLimit proto still has an unused tag 8 that was previously assigned
to token_oracle_type, so it should be reserved to prevent future reuse from
misdecoding stored chain state. Update the RateLimit message in rate_limit.proto
to add a reservation for field number 8 and the removed field name
token_oracle_type, keeping the existing BridgeTransfer transfers definition
unchanged. Use the RateLimit message definition as the anchor for this change.
proto/injective/peggy/v1/msgs.proto (1)

404-405: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Reserve the removed field numbers and names. token_oracle_type and new_token_oracle_type were removed without reserved declarations, so their tags can be reused later with an incompatible type and break wire compatibility.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@proto/injective/peggy/v1/msgs.proto` around lines 404 - 405, The Peggy v1
message definition is missing protobuf reservations for fields that were
removed, which can allow their tags and names to be reused incompatibly. Update
the affected message in msgs.proto to add reserved declarations for the removed
symbols token_oracle_type and new_token_oracle_type, including both their field
numbers and names, so future edits to this message remain wire-compatible.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@chain/exchange/types/v2/genesis.go`:
- Around line 129-155: The genesis validation helpers for spot orderbooks and
settlement data are using raw MarketId strings, unlike the risk mode path that
normalizes hex IDs. Update spotMarketIDs, derivativeMarketByID, binaryMarketIDs,
validatedExpiryInfoMarketIDs, and validateScheduledSettlementMarkerID to store
and compare keys using the same common.HexToHash(id).Hex() normalization, and
make the spotOrderbook/settlement lookups in validateSpotOrderbookMarkets use
the normalized form so mixed-case hex IDs validate and duplicate detection stays
consistent.

In `@proto/injective/exchange/v2/query.proto`:
- Around line 1443-1486: Update the stale comment on the
`MsgLiquidateCrossMarginPool` request in `query.proto` so it matches the
graduated selective liquidation behavior described in `tx.proto` instead of
saying the message closes all positions atomically. Keep the `reserved 16`
rationale aligned with the current design: explain that this field is no longer
needed because liquidation now closes only enough positions to restore health,
and reference the nearby snapshot fields (`ratcheted_maintenance_margin_total`,
`cross_margin_util_ratio`, `position_order_lock_requirement`,
`non_position_order_lock_requirement`) as the supporting context.

---

Nitpick comments:
In `@chain/exchange/types/v2/derivative_orders.go`:
- Around line 47-75: `NewMarketOrderForLiquidation` and
`NewMarketOrderForPartialLiquidation` duplicate the same order construction
logic, differing only in the quantity used. Update
`NewMarketOrderForLiquidation` to delegate to
`NewMarketOrderForPartialLiquidation` using `position.Quantity` for the close
quantity, and keep all shared fields and order type selection centralized in the
partial-liquidation constructor.

In `@chain/exchange/types/v2/fee_discounts.go`:
- Around line 128-147: The lazy initialization in SetReadOnlyFeeDiscountRate is
racy because multiple goroutines can create different readOnlyFeeDiscountMux
instances before locking. Update FeeDiscountConfig so the mutex is guaranteed to
be initialized through NewFeeDiscountConfig (and avoid nil-branch lazy init
here), or protect one-time initialization with sync.Once; keep the
SetReadOnlyFeeDiscountRate and readOnlyFeeDiscountRates access synchronized on
the single shared mutex.
- Around line 241-268: NewFeeDiscountStakingInfoForReadOnlyLookup duplicates the
empty map and mutex setup from NewFeeDiscountStakingInfo, which risks the two
constructors drifting apart. Extract the shared initialization for
FeeDiscountStakingInfo into a private helper and have both constructors call it,
then apply only their differing field assignments (for example Schedule,
timestamps, and cache references) in each constructor.

In `@proto/injective/peggy/v1/msgs.proto`:
- Around line 404-405: The Peggy v1 message definition is missing protobuf
reservations for fields that were removed, which can allow their tags and names
to be reused incompatibly. Update the affected message in msgs.proto to add
reserved declarations for the removed symbols token_oracle_type and
new_token_oracle_type, including both their field numbers and names, so future
edits to this message remain wire-compatible.

In `@proto/injective/peggy/v1/rate_limit.proto`:
- Around line 36-37: The persisted RateLimit proto still has an unused tag 8
that was previously assigned to token_oracle_type, so it should be reserved to
prevent future reuse from misdecoding stored chain state. Update the RateLimit
message in rate_limit.proto to add a reservation for field number 8 and the
removed field name token_oracle_type, keeping the existing BridgeTransfer
transfers definition unchanged. Use the RateLimit message definition as the
anchor for this change.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: ebf89bb9-03ef-405b-963e-e7adbba69803

📥 Commits

Reviewing files that changed from the base of the PR and between 9449739 and 2355015.

⛔ Files ignored due to path filters (10)
  • chain/exchange/types/v2/events.pb.go is excluded by !**/*.pb.go
  • chain/exchange/types/v2/exchange.pb.go is excluded by !**/*.pb.go
  • chain/exchange/types/v2/genesis.pb.go is excluded by !**/*.pb.go
  • chain/exchange/types/v2/query.pb.go is excluded by !**/*.pb.go
  • chain/exchange/types/v2/tx.pb.go is excluded by !**/*.pb.go
  • chain/oracle/types/oracle.pb.go is excluded by !**/*.pb.go
  • chain/peggy/types/msgs.pb.go is excluded by !**/*.pb.go
  • chain/peggy/types/rate_limit.pb.go is excluded by !**/*.pb.go
  • chain/stream/types/query.pb.go is excluded by !**/*.pb.go
  • chain/stream/types/v2/query.pb.go is excluded by !**/*.pb.go
📒 Files selected for processing (24)
  • chain/exchange/types/authz_exchange_generic.go
  • chain/exchange/types/errors.go
  • chain/exchange/types/key.go
  • chain/exchange/types/proposal.go
  • chain/exchange/types/v2/authz_exchange_generic.go
  • chain/exchange/types/v2/codec.go
  • chain/exchange/types/v2/derivative.go
  • chain/exchange/types/v2/derivative_orders.go
  • chain/exchange/types/v2/fee_discounts.go
  • chain/exchange/types/v2/genesis.go
  • chain/exchange/types/v2/msgs.go
  • chain/exchange/types/v2/params.go
  • chain/exchange/types/v2/proposal.go
  • injective_data/chain_messages_list.json
  • proto/injective/exchange/v2/events.proto
  • proto/injective/exchange/v2/exchange.proto
  • proto/injective/exchange/v2/genesis.proto
  • proto/injective/exchange/v2/query.proto
  • proto/injective/exchange/v2/tx.proto
  • proto/injective/oracle/v1beta1/oracle.proto
  • proto/injective/peggy/v1/msgs.proto
  • proto/injective/peggy/v1/rate_limit.proto
  • proto/injective/stream/v1beta1/query.proto
  • proto/injective/stream/v2/query.proto

Comment on lines +129 to +155
func (gs GenesisState) validateSpotOrderbookMarkets() error {
spotMarkets := gs.spotMarketIDs()
for i, orderbook := range gs.SpotOrderbook {
marketID := orderbook.MarketId
if !types.IsHexHash(marketID) {
return fmt.Errorf("spot_orderbook[%d]: invalid market_id %q", i, marketID)
}
if _, ok := spotMarkets[marketID]; !ok {
return fmt.Errorf("spot_orderbook[%d]: unknown market_id %s", i, marketID)
}
}

return nil
}

func (gs GenesisState) spotMarketIDs() map[string]struct{} {
markets := make(map[string]struct{}, len(gs.SpotMarkets))
for _, market := range gs.SpotMarkets {
if market == nil {
continue
}
markets[market.MarketId] = struct{}{}
}

return markets
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Normalize hex market IDs in spot orderbook and settlement validation maps for consistency with risk mode validation.

The new risk mode validation path (derivativeMarketIDs at line 123, validateMarketRiskModeRecord at line 80) normalizes market IDs via common.HexToHash(id).Hex(), and the duplicate-detection comment at lines 59-62 explicitly states this is required because IsHexHash accepts mixed-case hex. However, the also-new spot orderbook and settlement validation helpers build their lookup maps with raw market.MarketId strings:

  • spotMarketIDs() (line 150): markets[market.MarketId] = struct{}{}
  • derivativeMarketByID() (line 183): markets[market.MarketId] = market
  • binaryMarketIDs() (line 195): markets[market.MarketId] = struct{}{}
  • validatedExpiryInfoMarketIDs() (line 207): expiryInfos[info.MarketId] = struct{}{}
  • validateScheduledSettlementMarkerID (line 240): seen[marketID] = struct{}{}

This causes two classes of problems with mixed-case hex genesis input:

  1. False rejections — a spot orderbook or settlement marker referencing a market with different casing than its definition fails the known market_id lookup.
  2. False acceptances — duplicate settlement markers with mixed-case market IDs bypass duplicate detection, potentially allowing inconsistent genesis state.
🔧 Proposed fix: normalize all genesis market-ID map keys and lookups

Apply the same common.HexToHash(id).Hex() normalization used by derivativeMarketIDs():

 func (gs GenesisState) spotMarketIDs() map[string]struct{} {
 	markets := make(map[string]struct{}, len(gs.SpotMarkets))
 	for _, market := range gs.SpotMarkets {
 		if market == nil {
 			continue
 		}
-		markets[market.MarketId] = struct{}{}
+		markets[common.HexToHash(market.MarketId).Hex()] = struct{}{}
 	}
 	return markets
 }
 func (gs GenesisState) derivativeMarketByID() map[string]*DerivativeMarket {
 	markets := make(map[string]*DerivativeMarket, len(gs.DerivativeMarkets))
 	for _, market := range gs.DerivativeMarkets {
 		if market == nil {
 			continue
 		}
-		markets[market.MarketId] = market
+		markets[common.HexToHash(market.MarketId).Hex()] = market
 	}
 	return markets
 }
 func (gs GenesisState) binaryMarketIDs() map[string]struct{} {
 	markets := make(map[string]struct{}, len(gs.BinaryOptionsMarkets))
 	for _, market := range gs.BinaryOptionsMarkets {
 		if market == nil {
 			continue
 		}
-		markets[market.MarketId] = struct{}{}
+		markets[common.HexToHash(market.MarketId).Hex()] = struct{}{}
 	}
 	return markets
 }
 func (gs GenesisState) validatedExpiryInfoMarketIDs() (map[string]struct{}, error) {
 	expiryInfos := make(map[string]struct{}, len(gs.ExpiryFuturesMarketInfoState))
 	for i, info := range gs.ExpiryFuturesMarketInfoState {
 		if err := validateExpiryInfoState(i, info); err != nil {
 			return nil, err
 		}
-		expiryInfos[info.MarketId] = struct{}{}
+		expiryInfos[common.HexToHash(info.MarketId).Hex()] = struct{}{}
 	}
 	return expiryInfos, nil
 }

Then update all lookups and duplicate detection to normalize the query key:

 func (gs GenesisState) validateSpotOrderbookMarkets() error {
 	spotMarkets := gs.spotMarketIDs()
 	for i, orderbook := range gs.SpotOrderbook {
 		marketID := orderbook.MarketId
 		if !types.IsHexHash(marketID) {
 			return fmt.Errorf("spot_orderbook[%d]: invalid market_id %q", i, marketID)
 		}
-		if _, ok := spotMarkets[marketID]; !ok {
+		if _, ok := spotMarkets[common.HexToHash(marketID).Hex()]; !ok {
 			return fmt.Errorf("spot_orderbook[%d]: unknown market_id %s", i, marketID)
 		}
 	}
 	return nil
 }
 func validateScheduledSettlementMarkerID(
 	i int,
 	marker DerivativeMarketSettlementInfo,
 	seen map[string]struct{},
 ) error {
 	marketID := marker.MarketId
 	if !types.IsHexHash(marketID) {
 		return fmt.Errorf("derivative_market_settlement_scheduled[%d]: invalid market_id %q", i, marketID)
 	}
-	if _, ok := seen[marketID]; ok {
+	normalizedID := common.HexToHash(marketID).Hex()
+	if _, ok := seen[normalizedID]; ok {
 		return fmt.Errorf("derivative_market_settlement_scheduled[%d]: duplicate market_id %s", i, marketID)
 	}
-	seen[marketID] = struct{}{}
+	seen[normalizedID] = struct{}{}
 	return nil
 }
 func validateScheduledSettlementMarkerMarket(
 	i int,
 	marker DerivativeMarketSettlementInfo,
 	derivativeMarkets map[string]*DerivativeMarket,
 	binaryMarkets map[string]struct{},
 	expiryInfos map[string]struct{},
 ) error {
 	marketID := marker.MarketId
-	if derivativeMarket, ok := derivativeMarkets[marketID]; ok {
+	normalizedID := common.HexToHash(marketID).Hex()
+	if derivativeMarket, ok := derivativeMarkets[normalizedID]; ok {
 		return validateDerivativeSettlementMarker(i, marker, derivativeMarket, expiryInfos)
 	}
-	if _, ok := binaryMarkets[marketID]; ok {
+	if _, ok := binaryMarkets[normalizedID]; ok {
 		return validateBinarySettlementMarker(i, marker)
 	}
 	return fmt.Errorf("derivative_market_settlement_scheduled[%d]: unknown market_id %s", i, marketID)
 }
 func validateDerivativeSettlementMarker(
 	i int,
 	marker DerivativeMarketSettlementInfo,
 	market *DerivativeMarket,
 	expiryInfos map[string]struct{},
 ) error {
 	marketID := marker.MarketId
 	if market.IsTimeExpiry() {
-		if _, hasInfo := expiryInfos[marketID]; !hasInfo {
+		if _, hasInfo := expiryInfos[common.HexToHash(marketID).Hex()]; !hasInfo {
 			return fmt.Errorf("derivative_market_settlement_scheduled[%d]: expiry market %s missing expiry info",
 				i, marketID)
 		}
 	}
 func validateExpiryInfoState(i int, info ExpiryFuturesMarketInfoState) error {
 	if !types.IsHexHash(info.MarketId) {
 		return fmt.Errorf("expiry_futures_market_info_state[%d]: invalid market_id %q", i, info.MarketId)
 	}
 	if info.MarketInfo == nil {
 		return fmt.Errorf("expiry_futures_market_info_state[%d]: missing market_info", i)
 	}
-	if info.MarketInfo.MarketId != "" && info.MarketInfo.MarketId != info.MarketId {
+	if info.MarketInfo.MarketId != "" &&
+		common.HexToHash(info.MarketInfo.MarketId).Hex() != common.HexToHash(info.MarketId).Hex() {
 		return fmt.Errorf("expiry_futures_market_info_state[%d]: market_id mismatch %q != %q",
 			i, info.MarketInfo.MarketId, info.MarketId)
 	}

Also applies to: 177-211, 228-243

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@chain/exchange/types/v2/genesis.go` around lines 129 - 155, The genesis
validation helpers for spot orderbooks and settlement data are using raw
MarketId strings, unlike the risk mode path that normalizes hex IDs. Update
spotMarketIDs, derivativeMarketByID, binaryMarketIDs,
validatedExpiryInfoMarketIDs, and validateScheduledSettlementMarkerID to store
and compare keys using the same common.HexToHash(id).Hex() normalization, and
make the spotOrderbook/settlement lookups in validateSpotOrderbookMarkets use
the normalized form so mixed-case hex IDs validate and duplicate detection stays
consistent.

Comment on lines +1443 to +1486

// Ratcheted maintenance margin total: max(maintenance at current mark,
// maintenance at entry notional) summed across positions. This is the
// maintenance floor used in the cash-floor and withdrawable formulas, and is
// distinct from maintenance_margin_total. It is NOT derivable from the other
// response fields, so off-chain monitors need it to reconstruct the chain's
// admission / withdrawal predicates.
string ratcheted_maintenance_margin_total = 17 [
(gogoproto.customtype) = "cosmossdk.io/math.LegacyDec",
(gogoproto.nullable) = false
];

// Cross-margin utilisation ratio applied to the cash floor (0 is the
// emergency halt on new risk-increasing actions). A module param, not
// recoverable from any other response field.
string cross_margin_util_ratio = 18 [
(gogoproto.customtype) = "cosmossdk.io/math.LegacyDec",
(gogoproto.nullable) = false
];

// Position-side Order Lock Requirement bucket embedded in
// order_lock_requirement. This is the sum of max(position.Margin, 0), not the
// signed position_margin_total. It is exposed because negative-funded or
// mixed-sign position margins make the OLR split impossible to reconstruct
// from position_margin_total.
string position_order_lock_requirement = 19 [
(gogoproto.customtype) = "cosmossdk.io/math.LegacyDec",
(gogoproto.nullable) = false
];

// Non-position Order Lock Requirement bucket used by the cash-floor formula.
// This is not generally max(order_lock_requirement - position_margin_total,
// 0) because position_margin_total is signed while the position-side OLR
// bucket floors each position margin at zero. Together with
// ratcheted_maintenance_margin_total, cross_margin_util_ratio, and the
// existing equity fields, monitors can derive fees_buffer
// (= quote_balance + position_margin_total + unrealized_pnl_effective -
// equity_admission), cash_floor_available, admission_available
// (= equity_admission - order_lock_requirement), available_for_new
// (= min(cash_floor_available, admission_available)), and withdrawable.
string non_position_order_lock_requirement = 20 [
(gogoproto.customtype) = "cosmossdk.io/math.LegacyDec",
(gogoproto.nullable) = false
];

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Update stale MsgLiquidateCrossMarginPool comment at line 1441 to match graduated selective liquidation.

The adjacent comment at line 1441 states "MsgLiquidateCrossMarginPool closes all positions atomically," but tx.proto lines 1745–1747 now describe it as performing "graduated selective liquidation" that closes "only enough positions to restore health according to the configured target." The new snapshot fields 17–20 (ratcheted_maintenance_margin_total, cross_margin_util_ratio, position_order_lock_requirement, non_position_order_lock_requirement) directly support this graduated behavior, making the stale rationale for the reserved 16 field confusing.

The field 16 removal may still be correct (graduated liquidation doesn't need a canonical target market ID either), but the stated reason should be updated to reflect the current behavior.

📝 Proposed fix for stale comment
   // Field 16 removed: canonical_liquidation_target_market_id is no longer
-  // relevant — MsgLiquidateCrossMarginPool closes all positions atomically.
+  // relevant — MsgLiquidateCrossMarginPool performs graduated selective
+  // liquidation, closing only enough positions to restore health.
   reserved 16;
🧰 Tools
🪛 Buf (1.71.0)

[error] 1451-1451: cannot find gogoproto.customtype in this scope

(COMPILE)


[error] 1452-1452: cannot find gogoproto.nullable in this scope

(COMPILE)


[error] 1459-1459: cannot find gogoproto.customtype in this scope

(COMPILE)


[error] 1460-1460: cannot find gogoproto.nullable in this scope

(COMPILE)


[error] 1469-1469: cannot find gogoproto.customtype in this scope

(COMPILE)


[error] 1470-1470: cannot find gogoproto.nullable in this scope

(COMPILE)


[error] 1484-1484: cannot find gogoproto.customtype in this scope

(COMPILE)


[error] 1485-1485: cannot find gogoproto.nullable in this scope

(COMPILE)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@proto/injective/exchange/v2/query.proto` around lines 1443 - 1486, Update the
stale comment on the `MsgLiquidateCrossMarginPool` request in `query.proto` so
it matches the graduated selective liquidation behavior described in `tx.proto`
instead of saying the message closes all positions atomically. Keep the
`reserved 16` rationale aligned with the current design: explain that this field
is no longer needed because liquidation now closes only enough positions to
restore health, and reference the nearby snapshot fields
(`ratcheted_maintenance_margin_total`, `cross_margin_util_ratio`,
`position_order_lock_requirement`, `non_position_order_lock_requirement`) as the
supporting context.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant