Skip to content

[IC-1055] create-python-sdk-final-release-for-v120-testnet#415

Merged
aarmoa merged 3 commits into
devfrom
ic-1055/create-python-sdk-final-release-for-v120-testnet
May 18, 2026
Merged

[IC-1055] create-python-sdk-final-release-for-v120-testnet#415
aarmoa merged 3 commits into
devfrom
ic-1055/create-python-sdk-final-release-for-v120-testnet

Conversation

@aarmoa
Copy link
Copy Markdown
Collaborator

@aarmoa aarmoa commented May 18, 2026

  • Updated proto definitions pointing to injective-core v1.20.0-alpha.3 and injective-indexer v1.19.41.
  • Generated the MAINTAINERS.md file with documentation of the actions to execute when updating the SDK for a new chain version.

Summary by CodeRabbit

  • New Features
    • v2 Composer: batch liquidation support with helper and example; Composer exposes OrderType, OracleType, CrossMarginEligibility enums; accepts string/int for order/oracle types; market launch/update messages support cross-margin flags.
  • Documentation
    • Added Maintainers Guide and README callout for proto regen, OFAC refresh, and release workflow.
  • Chores
    • Regenerated protobufs for compatibility with updated core/indexer; Makefile adds OFAC generation target; package version bumped.
  • Tests
    • Added/updated tests for enums, batch liquidation, market fields, and stream updates.

Review Change Stack

…0-alpha.3 and injective-indexer v1.19.41. Generated the MAINTAINERS.md file with documentation of the actions to execute when updating the SDK for a new chain version.
@linear
Copy link
Copy Markdown

linear Bot commented May 18, 2026

IC-1055

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 18, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a8ba7262-9dfa-486b-a606-a3af17c23d9b

📥 Commits

Reviewing files that changed from the base of the PR and between fd12616 and 54470d4.

📒 Files selected for processing (2)
  • CHANGELOG.md
  • MAINTAINERS.md
✅ Files skipped from review due to trivial changes (1)
  • CHANGELOG.md

📝 Walkthrough

Walkthrough

Adds MsgBatchLiquidatePositions and Composer enum helpers with flexible resolvers, extends market builders with cross-margin flags, regenerates many protobuf/grpc bindings for updated core/indexer, adds maintainer docs, examples, and tests; bumps version to 1.15.0-rc1.

Changes

Batch Liquidation and Cross-Margin Eligibility Release

Layer / File(s) Summary
Build config, Makefile, version, docs
buf.gen.yaml, Makefile, pyproject.toml, MAINTAINERS.md, README.md, CHANGELOG.md
Bump buf input to Injective core v1.20.0-alpha.3, update Makefile to clone indexer v1.19.41 and add gen-ofac, bump version to 1.15.0-rc1, add MAINTAINERS guide and README note, and add CHANGELOG v1.15.0 entry.
Regenerated protobuf & gRPC bindings
pyinjective/proto/**
Regenerated many protobuf/grpc Python modules: updated DESCRIPTOR blobs, adjusted serialized offsets/options, added RPCs (BatchLiquidatePositions, SubaccountRiskProfile, CrossMarginPoolSnapshot, RelayPythProPrices, SedaFastPriceStates), removed deprecated RFQ RPCs, and updated client/server stubs and experimental wrappers.
Composer enum helpers & resolvers
pyinjective/composer_v2.py
Expose ORDER_TYPE, ORACLE_TYPE, CROSS_MARGIN_ELIGIBILITY IntEnums on Composer and add _resolve_order_type / _resolve_oracle_type to accept str or int inputs; update composer type maps for MsgBatchLiquidatePositions.
Composer builders: batch liquidation & cross-margin
pyinjective/composer_v2.py
Widen order_type/oracle_type params to Union[str,int], refactor _basic_derivative_order, add liquidate_position_data and msg_batch_liquidate_positions, and add/forward cross-margin flags in market/insurance/update builders.
Examples and test updates
examples/chain_client/exchange/*, tests/test_composer_v2.py, tests/client/chain/**
Add 31_MsgBatchLiquidatePositions.py example; update market launch examples to include cross_margin_eligible; add enum-mirroring and batch liquidation tests; update exchange v2 query and stream tests to assert new fields (crossMarginEligible, riskMode, isLiquidation).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • InjectiveLabs/sdk-python#402: Both PRs modify Composer v2 instant market launch/update builders; related changes to market-launch parameters.

Suggested reviewers

  • PavelInjective

Poem

🐰 With proto bytes and composer cheer,

I batch the liquidations near,
Enums revealed and examples run,
Cross-margin flags now set to sun—
Hooray, v1.15 hops into gear!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 31.46% 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 accurately describes the main objective: creating a Python SDK release for v1.20.0 testnet, which aligns with the proto updates and release documentation.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ic-1055/create-python-sdk-final-release-for-v120-testnet

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

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🤖 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 `@CHANGELOG.md`:
- Line 11: Update the CHANGELOG entry so the version string matches the intended
proto targets: replace the current line "Updated all compiled protos for
compatibility with Injective core v1.20.0 and Indexer v1.19.41ß" by correcting
the typo and exact versions to read "Updated all compiled protos for
compatibility with Injective core v1.20.0 and Indexer v1.19.41" (remove the
stray ß and ensure both version numbers match the PR description).

In `@examples/chain_client/exchange/31_MsgBatchLiquidatePositions.py`:
- Around line 17-18: The code reads INJECTIVE_PRIVATE_KEY into
private_key_in_hexa without validating it; add an explicit presence check
immediately after the getenv (for variable private_key_in_hexa) and fail fast
with a clear error (raise a RuntimeError or call sys.exit(1) with a message like
"INJECTIVE_PRIVATE_KEY environment variable is required") so the script stops
with an actionable message; apply the same presence check pattern for the other
similar getenv usages around the 30-33 and 38 lines in this example.

In `@MAINTAINERS.md`:
- Line 17: Update the MAINTAINERS.md table row that currently reads "Python
3.9+" to match the project's actual supported Python range: locate the table
cell containing "`Python 3.9+`" and replace it with the supported version
specifier found in the project's configuration (e.g., pyproject.toml /
runtime.txt / CI matrix). Ensure the runtime column entry remains the same and
only the Python version cell is updated so the table accurately reflects the
project's current interpreter requirement.

In `@tests/test_composer_v2.py`:
- Around line 54-56: The failing E501 is due to an overly long assert comparing
Composer.CROSS_MARGIN_ELIGIBILITY[name].value to
injective_proposal_v2_pb.CrossMarginEligibility.Value(name); to fix, split the
long line into two by assigning one or both sides to short local variables
(e.g., left = Composer.CROSS_MARGIN_ELIGIBILITY[name].value and right =
injective_proposal_v2_pb.CrossMarginEligibility.Value(name)) inside the same
test function test_order_type_enum_accepted_for_order_type_param, then assert
left == right so the line length stays under 120 chars and pre-commit lint
passes.
- Around line 30-37: The test test_permissions_action_enum_mirrors_proto relies
on iteration order when comparing [m.name for m in Composer.PERMISSIONS_ACTION]
to proto_non_zero_keys; instead, build normalized mappings (e.g., dicts) of
non-zero proto names to values and of Composer.PERMISSIONS_ACTION names to
values (filter out zero and handle aliases) and assert equality of these dicts
so the comparison is order-independent; reference Composer.PERMISSIONS_ACTION
and permissions_pb.Action when locating and replacing the list-based comparisons
with dict-based comparisons.
🪄 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: de7e1eaf-cece-46ed-95b8-3bccff6540e5

📥 Commits

Reviewing files that changed from the base of the PR and between c3d1dd1 and b14afd2.

📒 Files selected for processing (38)
  • CHANGELOG.md
  • MAINTAINERS.md
  • Makefile
  • README.md
  • buf.gen.yaml
  • examples/chain_client/exchange/25_MsgUpdateDerivativeMarket.py
  • examples/chain_client/exchange/31_MsgBatchLiquidatePositions.py
  • examples/chain_client/exchange/4_MsgInstantPerpetualMarketLaunch.py
  • examples/chain_client/exchange/5_MsgInstantExpiryFuturesMarketLaunch.py
  • pyinjective/composer_v2.py
  • pyinjective/proto/exchange/injective_rfq_gw_rpc_pb2.py
  • pyinjective/proto/exchange/injective_rfq_gw_rpc_pb2_grpc.py
  • pyinjective/proto/exchange/injective_rfq_rpc_pb2.py
  • pyinjective/proto/exchange/injective_rfq_rpc_pb2_grpc.py
  • pyinjective/proto/exchange/injective_tc_derivatives_rpc_pb2.py
  • pyinjective/proto/injective/exchange/v1beta1/events_pb2.py
  • pyinjective/proto/injective/exchange/v2/events_pb2.py
  • pyinjective/proto/injective/exchange/v2/exchange_pb2.py
  • pyinjective/proto/injective/exchange/v2/genesis_pb2.py
  • pyinjective/proto/injective/exchange/v2/market_pb2.py
  • pyinjective/proto/injective/exchange/v2/proposal_pb2.py
  • pyinjective/proto/injective/exchange/v2/query_pb2.py
  • pyinjective/proto/injective/exchange/v2/query_pb2_grpc.py
  • pyinjective/proto/injective/exchange/v2/tx_pb2.py
  • pyinjective/proto/injective/exchange/v2/tx_pb2_grpc.py
  • pyinjective/proto/injective/oracle/v1beta1/events_pb2.py
  • pyinjective/proto/injective/oracle/v1beta1/genesis_pb2.py
  • pyinjective/proto/injective/oracle/v1beta1/oracle_pb2.py
  • pyinjective/proto/injective/oracle/v1beta1/query_pb2.py
  • pyinjective/proto/injective/oracle/v1beta1/query_pb2_grpc.py
  • pyinjective/proto/injective/oracle/v1beta1/tx_pb2.py
  • pyinjective/proto/injective/oracle/v1beta1/tx_pb2_grpc.py
  • pyinjective/proto/injective/peggy/v1/params_pb2.py
  • pyinjective/proto/injective/stream/v2/query_pb2.py
  • pyproject.toml
  • tests/client/chain/grpc/test_chain_grpc_exchange_v2_api.py
  • tests/client/chain/stream_grpc/test_chain_grpc_chain_stream.py
  • tests/test_composer_v2.py
💤 Files with no reviewable changes (1)
  • pyinjective/proto/exchange/injective_rfq_rpc_pb2_grpc.py

Comment thread CHANGELOG.md Outdated
Comment on lines +17 to +18
private_key_in_hexa = os.getenv("INJECTIVE_PRIVATE_KEY")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add an explicit private key presence check before use.

If INJECTIVE_PRIVATE_KEY is unset, this example fails later with a less actionable error. Fail fast with a clear message.

Proposed fix
 async def main() -> None:
     dotenv.load_dotenv()
     private_key_in_hexa = os.getenv("INJECTIVE_PRIVATE_KEY")
+    if not private_key_in_hexa:
+        raise ValueError("INJECTIVE_PRIVATE_KEY is not set")

Also applies to: 30-33, 38-38

🤖 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 `@examples/chain_client/exchange/31_MsgBatchLiquidatePositions.py` around lines
17 - 18, The code reads INJECTIVE_PRIVATE_KEY into private_key_in_hexa without
validating it; add an explicit presence check immediately after the getenv (for
variable private_key_in_hexa) and fail fast with a clear error (raise a
RuntimeError or call sys.exit(1) with a message like "INJECTIVE_PRIVATE_KEY
environment variable is required") so the script stops with an actionable
message; apply the same presence check pattern for the other similar getenv
usages around the 30-33 and 38 lines in this example.

Comment thread MAINTAINERS.md Outdated
Comment thread tests/test_composer_v2.py
Comment on lines +30 to +37
def test_permissions_action_enum_mirrors_proto(self):
# IntFlag excludes zero-valued members from iteration (zero means "no flags set"),
# so we compare only the non-zero proto keys.
proto_non_zero_keys = [n for n in permissions_pb.Action.keys() if permissions_pb.Action.Value(n) != 0]
assert [m.name for m in Composer.PERMISSIONS_ACTION] == proto_non_zero_keys
for name in proto_non_zero_keys:
assert Composer.PERMISSIONS_ACTION[name].value == permissions_pb.Action.Value(name)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
python - <<'PY'
from pyinjective.composer_v2 import Composer
from pyinjective.proto.injective.permissions.v1beta1 import permissions_pb2 as permissions_pb

print("Composer iteration:", [m.name for m in Composer.PERMISSIONS_ACTION])
print("Proto keys:", list(permissions_pb.Action.keys()))
print("Proto non-zero:", [n for n in permissions_pb.Action.keys() if permissions_pb.Action.Value(n) != 0])

members = Composer.PERMISSIONS_ACTION.__members__
normalized = {
    n: m.value
    for n, m in members.items()
    if m.value != 0 and m.name == n
}
print("Composer normalized non-zero non-alias:", normalized)
PY

Repository: InjectiveLabs/sdk-python

Length of output: 585


🏁 Script executed:

# First, locate and examine the test file
find . -name "test_composer_v2.py" -type f

Repository: InjectiveLabs/sdk-python

Length of output: 96


🏁 Script executed:

# Find the Composer class definition
find . -name "composer_v2.py" -o -name "composer.py" | head -20

Repository: InjectiveLabs/sdk-python

Length of output: 97


🏁 Script executed:

# Search for PERMISSIONS_ACTION in the codebase
rg "PERMISSIONS_ACTION" --type py -A 5 -B 5

Repository: InjectiveLabs/sdk-python

Length of output: 13318


🏁 Script executed:

# Read the test file to see the full test context
cat -n tests/test_composer_v2.py | head -50

Repository: InjectiveLabs/sdk-python

Length of output: 2606


🏁 Script executed:

# Search for imports in the test file to understand what permissions_pb is
rg "permissions_pb|import" tests/test_composer_v2.py | head -20

Repository: InjectiveLabs/sdk-python

Length of output: 1265


🏁 Script executed:

# Look at the proto file to understand the Action enum
find . -name "*permissions*" -type f -path "*/proto/*" | grep -E "\.(proto|py)$"

Repository: InjectiveLabs/sdk-python

Length of output: 211


🏁 Script executed:

# Check if there are Python proto files
rg "class Action" --type py | head -20

Repository: InjectiveLabs/sdk-python

Length of output: 50


🏁 Script executed:

# Examine the proto generated file to understand Action enum
rg "Action = " pyinjective/proto/injective/permissions/v1beta1/permissions_pb2.py -A 50 | head -100

Repository: InjectiveLabs/sdk-python

Length of output: 50


🏁 Script executed:

# Check the structure of the proto Action enum
cat pyinjective/proto/injective/permissions/v1beta1/permissions_pb2.py | head -200 | tail -100

Repository: InjectiveLabs/sdk-python

Length of output: 5430


🏁 Script executed:

# Find the Action enum definition more specifically
grep -n "class Action\|Action = " pyinjective/proto/injective/permissions/v1beta1/permissions_pb2.py | head -20

Repository: InjectiveLabs/sdk-python

Length of output: 50


🏁 Script executed:

# Get the full proto file to see all enum members
cat pyinjective/proto/injective/permissions/v1beta1/permissions_pb2.py | tail -100

Repository: InjectiveLabs/sdk-python

Length of output: 5430


🏁 Script executed:

# Search for how Action enum members are defined
rg "UNSPECIFIED|MINT|RECEIVE|BURN" pyinjective/proto/injective/permissions/v1beta1/permissions_pb2.py

Repository: InjectiveLabs/sdk-python

Length of output: 3013


🏁 Script executed:

# Look at the composer_v2.py file more closely to see the IntFlag definition
cat -n pyinjective/composer_v2.py | grep -A 10 -B 5 "PERMISSIONS_ACTION"

Repository: InjectiveLabs/sdk-python

Length of output: 662


🏁 Script executed:

# Get full context of the Composer PERMISSIONS_ACTION definition and how it's used
wc -l pyinjective/composer_v2.py

Repository: InjectiveLabs/sdk-python

Length of output: 100


🏁 Script executed:

# Read more context around PERMISSIONS_ACTION definition
sed -n '300,330p' pyinjective/composer_v2.py

Repository: InjectiveLabs/sdk-python

Length of output: 1104


🏁 Script executed:

# Check imports in composer_v2.py to understand IntFlag usage
head -50 pyinjective/composer_v2.py

Repository: InjectiveLabs/sdk-python

Length of output: 2680


🏁 Script executed:

# Check if there could be alias issues with matching proto structure
python3 << 'PYTHON'
# Verify enum iteration semantics
from enum import IntFlag

# Create IntFlag similar to the test scenario
test_enum = IntFlag('TEST_ENUM', [
    ('ZERO', 0),
    ('ONE', 1),
    ('TWO', 2),
    ('FOUR', 4),
])

print("IntFlag iteration (should exclude ZERO):")
print([m.name for m in test_enum])
print("\nIntFlag members (includes ZERO):")
print([m.name for m in test_enum.__members__])
print("\nDirect __members__ dict:")
print({n: m.value for n, m in test_enum.__members__.items()})
PYTHON

Repository: InjectiveLabs/sdk-python

Length of output: 352


🏁 Script executed:

# Check the test file for any additional context about the failure
grep -n "def test_permissions" tests/test_composer_v2.py -A 15

Repository: InjectiveLabs/sdk-python

Length of output: 1082


🏁 Script executed:

# Verify the similar tests to understand the pattern
grep -n "def test_.*_enum_mirrors_proto" tests/test_composer_v2.py -A 5

Repository: InjectiveLabs/sdk-python

Length of output: 1599


🏁 Script executed:

# Test the actual enum behavior with dict comparison
python3 << 'PYTHON'
from enum import IntFlag

# Simulate the enum creation pattern from the code
members = [
    ('ZERO', 0),
    ('ONE', 1),
    ('TWO', 2),
    ('FOUR', 4),
]
test_enum = IntFlag('TEST_ENUM', members)

print("Iteration order:")
print([m.name for m in test_enum])

print("\nMembers dict keys:")
print(list(test_enum.__members__.keys()))

print("\nNormalized non-zero non-alias (proposed approach):")
normalized = {
    n: m.value
    for n, m in test_enum.__members__.items()
    if m.value != 0 and m.name == n
}
print(normalized)

print("\nComparison of iteration vs normalized:")
print("Iteration matches list order?", [m.name for m in test_enum] == ['ONE', 'TWO', 'FOUR'])
PYTHON

Repository: InjectiveLabs/sdk-python

Length of output: 318


Refactor the test to compare normalized enum members rather than iteration order.

The current test relies on iteration order matching between proto keys and IntFlag iteration, which is fragile and order-dependent. IntFlag iteration excludes zero-valued members correctly, but comparing iteration results directly is brittle. Use normalized dict comparison instead to make the test resilient to iteration order differences and to explicitly handle zero filtering and alias members.

Proposed fix
 def test_permissions_action_enum_mirrors_proto(self):
-    # IntFlag excludes zero-valued members from iteration (zero means "no flags set"),
-    # so we compare only the non-zero proto keys.
-    proto_non_zero_keys = [n for n in permissions_pb.Action.keys() if permissions_pb.Action.Value(n) != 0]
-    assert [m.name for m in Composer.PERMISSIONS_ACTION] == proto_non_zero_keys
-    for name in proto_non_zero_keys:
-        assert Composer.PERMISSIONS_ACTION[name].value == permissions_pb.Action.Value(name)
+    proto_non_zero = {
+        name: permissions_pb.Action.Value(name)
+        for name in permissions_pb.Action.keys()
+        if permissions_pb.Action.Value(name) != 0
+    }
+    composer_non_zero = {
+        name: member.value
+        for name, member in Composer.PERMISSIONS_ACTION.__members__.items()
+        if member.value != 0 and member.name == name
+    }
+    assert composer_non_zero == proto_non_zero
🧰 Tools
🪛 GitHub Actions: pre-commit / 0_pre-commit.txt

[error] pre-commit hook 'black' failed; files were reformatted (tests/test_composer_v2.py was modified). Run black to apply formatting changes.

🪛 GitHub Actions: pre-commit / pre-commit

[warning] pre-commit hook 'black' failed because it modified files (reformat required). Changes applied to tests/test_composer_v2.py.

🪛 GitHub Actions: run-tests / 7_run-tests (3.10, ubuntu-latest).txt

[error] 34-34: Test failed: TestComposer.test_permissions_action_enum_mirrors_proto. AssertionError: Composer.PERMISSIONS_ACTION enum ordering/contents do not match proto_non_zero_keys (unexpected 'UNSPECIFIED' at index 0; left contains extra item 'MODIFY_ROLE_MANAGERS').

🪛 GitHub Actions: run-tests / run-tests (3.10, ubuntu-latest)

[error] 34-34: Test failed: TestComposer.test_permissions_action_enum_mirrors_proto assertion mismatch. Expected Composer.PERMISSIONS_ACTION enum names (excluding proto non-zero keys) to match proto Non-zero Action keys; diff shows 'UNSPECIFIED' present at index 0 and 'MODIFY_ROLE_MANAGERS' count mismatch.

🤖 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 `@tests/test_composer_v2.py` around lines 30 - 37, The test
test_permissions_action_enum_mirrors_proto relies on iteration order when
comparing [m.name for m in Composer.PERMISSIONS_ACTION] to proto_non_zero_keys;
instead, build normalized mappings (e.g., dicts) of non-zero proto names to
values and of Composer.PERMISSIONS_ACTION names to values (filter out zero and
handle aliases) and assert equality of these dicts so the comparison is
order-independent; reference Composer.PERMISSIONS_ACTION and
permissions_pb.Action when locating and replacing the list-based comparisons
with dict-based comparisons.

Comment thread tests/test_composer_v2.py Outdated
@aarmoa aarmoa merged commit f06600a into dev May 18, 2026
14 checks passed
@aarmoa aarmoa deleted the ic-1055/create-python-sdk-final-release-for-v120-testnet branch May 18, 2026 22:38
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