Skip to content
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
71b34aa
[CHORE] Updated project version to v1.12.0-rc1
aarmoa Jul 28, 2025
8c6b3f5
Merge pull request #392 from InjectiveLabs/chore/sync_with_master_aft…
aarmoa Jul 28, 2025
95f9b21
feat: added a new example script to search for liquidable positions
aarmoa Aug 1, 2025
65f0426
fix: fix liquidation price calculation in the liquidable positions ex…
aarmoa Aug 1, 2025
a7d26e6
fix: fixed the position liquidation price calculation in the liquidab…
aarmoa Aug 1, 2025
987e82b
fix: fixed the position liquidation price calculation in the liquidab…
aarmoa Aug 1, 2025
1c7fb55
fix: solved pre-commit issues
aarmoa Aug 1, 2025
26b9e06
Merge pull request #395 from InjectiveLabs/feat/add_liquidable_positi…
aarmoa Aug 1, 2025
bdd321b
Update README.md
aarmoa Jul 29, 2025
251ae51
(fix) Fixed the v1 AsyncClient orderbooks queries to include the dept…
aarmoa Aug 20, 2025
10509e6
(fix) Fixed pre-commit errors
aarmoa Aug 20, 2025
c22a799
(fix) Fixed pre-commit errors
aarmoa Aug 20, 2025
403ccff
(feat) Updated proto definitions to match Injective core v1.16.4 and …
aarmoa Sep 23, 2025
76e43be
(fix) Fixed pre-commit issues
aarmoa Sep 23, 2025
6a54b6a
(fix) Updated the official tokens JSON files URLs
aarmoa Sep 23, 2025
0c7e085
(fix) Fixed pre-commit issues
aarmoa Sep 23, 2025
15adf6d
Merge pull request #399 from InjectiveLabs/feat/sync_dev_with_master_…
aarmoa Sep 24, 2025
05d6a6e
Merge branch 'master' of https://github.com/InjectiveLabs/sdk-python …
aarmoa Oct 23, 2025
226c092
Merge pull request #401 from InjectiveLabs/chore/sync_dev_with_master…
aarmoa Oct 23, 2025
e42bd38
(feat) Updated proto definitions to injective-core v1.17.0-beta.3 and…
aarmoa Oct 23, 2025
4d134c8
(fix) Fixed pre-commit errors
aarmoa Oct 23, 2025
74bd9d2
(feat) Updated proto definitions to injective-core v1.17.0 and indexe…
aarmoa Nov 10, 2025
8645426
Merge pull request #400 from InjectiveLabs/cp-652/update_protos_for_v…
aarmoa Nov 10, 2025
629d62e
(fix) Updated CHANGELOG.md to include v1.12.0 details
aarmoa Nov 10, 2025
162ad40
(fix) Fixed pre-commit issues
aarmoa Nov 10, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

All notable changes to this project will be documented in this file.

## [1.12.0] - 2025-11-10
### Changed
- Updated all compiled protos for compatibility with Injective core v1.17.0 and Indexer v1.17.16
- Included the OpenNotionalCap in derivative markets
- Added support for market orders creation with the MsgBatchUpdateOrders message
- Support for order failure events and conditional orders trigger failures in the chainstrem updates

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

Fix typo and trailing whitespace.

Line 10 contains a typo: "chainstrem" should be "chainstream". Additionally, the pre-commit hook detected trailing whitespace that must be removed.

Apply this diff to fix both issues:

 ## [1.12.0] - 2025-11-10 
 ### Changed
 - Updated all compiled protos for compatibility with Injective core v1.17.0 and Indexer v1.17.16
 - Included the OpenNotionalCap in derivative markets
 - Added support for market orders creation with the MsgBatchUpdateOrders message
-- Support for order failure events and conditional orders trigger failures in the chainstrem updates
+- Support for order failure events and conditional orders trigger failures in the chainstream updates
-
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
## [1.12.0] - 2025-11-10
### Changed
- Updated all compiled protos for compatibility with Injective core v1.17.0 and Indexer v1.17.16
- Included the OpenNotionalCap in derivative markets
- Added support for market orders creation with the MsgBatchUpdateOrders message
- Support for order failure events and conditional orders trigger failures in the chainstrem updates
## [1.12.0] - 2025-11-10
### Changed
- Updated all compiled protos for compatibility with Injective core v1.17.0 and Indexer v1.17.16
- Included the OpenNotionalCap in derivative markets
- Added support for market orders creation with the MsgBatchUpdateOrders message
- Support for order failure events and conditional orders trigger failures in the chainstream updates
🧰 Tools
🪛 LanguageTool

[grammar] ~10-~10: Ensure spelling is correct
Context: ...ditional orders trigger failures in the chainstrem updates ## [1.11.2] - 2025-09-24 ### A...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)


[grammar] ~10-~10: Ensure spelling is correct
Context: ...igger failures in the chainstrem updates ## [1.11.2] - 2025-09-24 ### Added - Added ...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🤖 Prompt for AI Agents
In CHANGELOG.md around lines 5 to 11, fix the typo "chainstrem" to "chainstream"
on line 10 and remove any trailing whitespace on that line (and any other lines
flagged by the pre-commit hook); update the text to read "Support for order
failure events and conditional orders trigger failures in the chainstream
updates" and ensure no trailing spaces remain at end-of-line.

## [1.11.2] - 2025-09-24
### Added
- Added support in v2 Composer to create the new exchange module MsgCancelPostOnlyMode message
Expand Down
7 changes: 1 addition & 6 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,14 @@ fix-generated-proto-imports:
@find ./pyinjective/proto -type f -name "*.py" -exec sed -i "" -e "s/from google.api/from pyinjective.proto.google.api/g" {} \;

define clean_repos
rm -Rf cosmos-sdk
rm -Rf ibc-go
rm -Rf cometbft
rm -Rf wasmd
rm -Rf injective-core
rm -Rf injective-indexer
endef

clean-all:
$(call clean_repos)

clone-injective-indexer:
git clone https://github.com/InjectiveLabs/injective-indexer.git -b v1.16.91 --depth 1 --single-branch
git clone https://github.com/InjectiveLabs/injective-indexer.git -b v1.17.16 --depth 1 --single-branch

clone-all: clone-injective-indexer

Expand Down
13 changes: 8 additions & 5 deletions buf.gen.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,21 @@ inputs:
- module: buf.build/googleapis/googleapis
- module: buf.build/cosmos/ics23
- git_repo: https://github.com/InjectiveLabs/ibc-go
tag: v8.7.0-evm-comet1-inj
tag: v8.7.0-inj.3
- git_repo: https://github.com/InjectiveLabs/wasmd
tag: v0.53.3-evm-comet1-inj
tag: v0.53.3-inj.2
- git_repo: https://github.com/InjectiveLabs/cometbft
tag: v1.0.1-inj.3
tag: v1.0.1-inj.4
- git_repo: https://github.com/InjectiveLabs/cosmos-sdk
tag: v0.50.13-evm-comet1-inj.6
tag: v0.50.14-inj
# - git_repo: https://github.com/InjectiveLabs/wasmd
# branch: v0.51.x-inj
# subdir: proto
- git_repo: https://github.com/InjectiveLabs/hyperlane-cosmos
tag: v1.0.1-inj
subdir: proto
- git_repo: https://github.com/InjectiveLabs/injective-core
tag: v1.16.4
tag: v1.17.0
subdir: proto
# - git_repo: https://github.com/InjectiveLabs/injective-core
# branch: master
Expand Down
76 changes: 76 additions & 0 deletions examples/chain_client/10_SearchLiquidablePositions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import asyncio
from decimal import Decimal

from pyinjective.async_client_v2 import AsyncClient
from pyinjective.core.network import Network


def adjusted_margin(
quantity: Decimal, margin: Decimal, is_long: bool, cumulative_funding_entry: Decimal, cumulative_funding: Decimal
) -> Decimal:
unrealized_funding_payment = (cumulative_funding - cumulative_funding_entry) * quantity * (-1 if is_long else 1)
return margin + unrealized_funding_payment


async def main() -> None:
# select network: local, testnet, mainnet
network = Network.mainnet()

# initialize grpc client
client = AsyncClient(network)

positions_per_market = dict()

positions_dict = await client.fetch_chain_positions()
liquidable_positions = []

for position in positions_dict["state"]:
if position["marketId"] not in positions_per_market:
positions_per_market[position["marketId"]] = []
positions_per_market[position["marketId"]].append(position)

derivative_markets = await client.fetch_chain_derivative_markets(
status="Active",
market_ids=list(positions_per_market.keys()),
)

for market in derivative_markets["markets"]:
client_market = (await client.all_derivative_markets())[market["market"]["marketId"]]
market_mark_price = client_market._from_extended_chain_format(Decimal(market["markPrice"]))
for position in positions_per_market[client_market.id]:
is_long = position["position"]["isLong"]
quantity = client_market._from_extended_chain_format(Decimal(position["position"]["quantity"]))
entry_price = client_market._from_extended_chain_format(Decimal(position["position"]["entryPrice"]))
margin = client_market._from_extended_chain_format(Decimal(position["position"]["margin"]))
cumulative_funding_entry = client_market._from_extended_chain_format(
Decimal(position["position"]["cumulativeFundingEntry"])
)
market_cumulative_funding = client_market._from_extended_chain_format(
Decimal(market["perpetualInfo"]["fundingInfo"]["cumulativeFunding"])
)
Comment on lines +48 to +50
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

Guard against missing funding info on non‑perpetual markets

market["perpetualInfo"] is absent for expiry futures and other non‑perpetual derivative markets. This line will throw a KeyError, crashing the example as soon as such a market is encountered—even though the script targets all active derivative markets. Please gate the funding-derived calculations so we either skip non‑perpetual markets or fall back to a safe default.

Apply something along these lines to avoid the crash:

-            market_cumulative_funding = client_market._from_extended_chain_format(
-                Decimal(market["perpetualInfo"]["fundingInfo"]["cumulativeFunding"])
-            )
+            perpetual_info = market.get("perpetualInfo")
+            if not perpetual_info or "fundingInfo" not in perpetual_info:
+                continue  # no funding data available for this market type
+
+            market_cumulative_funding = client_market._from_extended_chain_format(
+                Decimal(perpetual_info["fundingInfo"]["cumulativeFunding"])
+            )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
market_cumulative_funding = client_market._from_extended_chain_format(
Decimal(market["perpetualInfo"]["fundingInfo"]["cumulativeFunding"])
)
perpetual_info = market.get("perpetualInfo")
if not perpetual_info or "fundingInfo" not in perpetual_info:
continue # no funding data available for this market type
market_cumulative_funding = client_market._from_extended_chain_format(
Decimal(perpetual_info["fundingInfo"]["cumulativeFunding"])
)
🤖 Prompt for AI Agents
In examples/chain_client/10_SearchLiquidablePositions.py around lines 48 to 50,
the code assumes market["perpetualInfo"] exists and will KeyError on
non‑perpetual markets; guard that access by checking for "perpetualInfo" and its
"fundingInfo"/"cumulativeFunding" keys (or use market.get(...)) before
converting to Decimal, and either skip non‑perpetual markets (continue) or fall
back to a safe default like Decimal(0) so the example no longer crashes when
encountering expiry futures.


adj_margin = adjusted_margin(quantity, margin, is_long, cumulative_funding_entry, market_cumulative_funding)
adjusted_unit_margin = (adj_margin / quantity) * (-1 if is_long else 1)
maintenance_margin_ratio = client_market.maintenance_margin_ratio * (-1 if is_long else 1)

liquidation_price = (entry_price + adjusted_unit_margin) / (Decimal(1) + maintenance_margin_ratio)

should_be_liquidated = (is_long and market_mark_price <= liquidation_price) or (
not is_long and market_mark_price >= liquidation_price
)

if should_be_liquidated:
position_side = "Long" if is_long else "Short"
print(
f"{position_side} position for market {client_market.id} and subaccount "
f"{position['subaccountId']} should be liquidated (liquidation price: "
f"{liquidation_price.normalize()} / mark price: {market_mark_price.normalize()})"
)
liquidable_positions.append(position)

# print(f"\n\n\n")
# print(json.dumps(liquidable_positions, indent=4))


if __name__ == "__main__":
asyncio.get_event_loop().run_until_complete(main())
8 changes: 8 additions & 0 deletions examples/chain_client/7_ChainStream.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@ async def main() -> None:
subaccount_ids=[subaccount_id], market_ids=[inj_usdt_perp_market]
)
oracle_price_filter = composer.chain_stream_oracle_price_filter(symbols=["INJ", "USDT"])
order_failures_filter = composer.chain_stream_order_failures_filter(
accounts=["inj1hkhdaj2a2clmq5jq6mspsggqs32vynpk228q3r"]
)
conditional_order_trigger_failures_filter = composer.chain_stream_conditional_order_trigger_failures_filter(
subaccount_ids=[subaccount_id], market_ids=[inj_usdt_perp_market]
)

task = asyncio.get_event_loop().create_task(
client.listen_chain_stream_updates(
Expand All @@ -66,6 +72,8 @@ async def main() -> None:
derivative_orderbooks_filter=derivative_orderbooks_filter,
positions_filter=positions_filter,
oracle_price_filter=oracle_price_filter,
order_failures_filter=order_failures_filter,
conditional_order_trigger_failures_filter=conditional_order_trigger_failures_filter,
)
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ async def main() -> None:
min_price_tick_size=Decimal("0.01"),
min_quantity_tick_size=Decimal("0.01"),
min_notional=Decimal("1"),
open_notional_cap=composer.uncapped_open_notional_cap(),
)

# broadcast the transaction
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ async def main() -> None:
new_initial_margin_ratio=Decimal("0.40"),
new_maintenance_margin_ratio=Decimal("0.085"),
new_reduce_margin_ratio=Decimal("3.5"),
new_open_notional_cap=composer.uncapped_open_notional_cap(),
)

# broadcast the transaction
Expand Down
68 changes: 68 additions & 0 deletions examples/chain_client/exchange/30_MsgOffsetPosition.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import asyncio
import json
import os

import dotenv

from pyinjective.async_client_v2 import AsyncClient
from pyinjective.core.broadcaster import MsgBroadcasterWithPk
from pyinjective.core.network import Network
from pyinjective.wallet import PrivateKey


async def main() -> None:
dotenv.load_dotenv()
configured_private_key = os.getenv("INJECTIVE_PRIVATE_KEY")

# select network: local, testnet, mainnet
network = Network.testnet()

# initialize grpc client
client = AsyncClient(network)
await client.initialize_tokens_from_chain_denoms()
composer = await client.composer()

gas_price = await client.current_chain_gas_price()
# adjust gas price to make it valid even if it changes between the time it is requested and the TX is broadcasted
gas_price = int(gas_price * 1.1)

message_broadcaster = MsgBroadcasterWithPk.new_using_simulation(
network=network,
private_key=configured_private_key,
gas_price=gas_price,
client=client,
composer=composer,
)

# load account
priv_key = PrivateKey.from_hex(configured_private_key)
pub_key = priv_key.to_public_key()
address = pub_key.to_address()
await client.fetch_account(address.to_acc_bech32())

offsetting_subaccount_ids = {
"0xbdaedec95d563fb05240d6e01821008454c24c36000000000000000000000000",
"0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000000",
}

# prepare tx msg
message = composer.msg_offset_position(
sender=address.to_acc_bech32(),
subaccount_id=address.get_subaccount_id(index=0),
market_id="0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6",
offsetting_subaccount_ids=offsetting_subaccount_ids,
)

# broadcast the transaction
result = await message_broadcaster.broadcast([message])
print("---Transaction Response---")
print(json.dumps(result, indent=2))

gas_price = await client.current_chain_gas_price()
# adjust gas price to make it valid even if it changes between the time it is requested and the TX is broadcasted
gas_price = int(gas_price * 1.1)
message_broadcaster.update_gas_price(gas_price=gas_price)


if __name__ == "__main__":
asyncio.get_event_loop().run_until_complete(main())
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ async def main() -> None:
min_price_tick_size=Decimal("0.001"),
min_quantity_tick_size=Decimal("0.01"),
min_notional=Decimal("1"),
open_notional_cap=composer.uncapped_open_notional_cap(),
)

# broadcast the transaction
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ async def main() -> None:
min_price_tick_size=Decimal("0.001"),
min_quantity_tick_size=Decimal("0.01"),
min_notional=Decimal("1"),
open_notional_cap=composer.uncapped_open_notional_cap(),
)

# broadcast the transaction
Expand Down
29 changes: 29 additions & 0 deletions examples/chain_client/exchange/9_MsgBatchUpdateOrders.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,21 @@ async def main() -> None:
),
]

derivative_market_orders_to_create = [
composer.derivative_order(
market_id=derivative_market_id_create,
subaccount_id=subaccount_id,
fee_recipient=fee_recipient,
price=Decimal(25100),
quantity=Decimal(0.1),
margin=composer.calculate_margin(
quantity=Decimal(0.1), price=Decimal(25100), leverage=Decimal(1), is_reduce_only=False
),
order_type="BUY",
cid=str(uuid.uuid4()),
),
]

spot_orders_to_create = [
composer.spot_order(
market_id=spot_market_id_create,
Expand All @@ -125,13 +140,27 @@ async def main() -> None:
),
]

spot_market_orders_to_create = [
composer.spot_order(
market_id=spot_market_id_create,
subaccount_id=subaccount_id,
fee_recipient=fee_recipient,
price=Decimal("3.5"),
quantity=Decimal("1"),
order_type="BUY",
cid=str(uuid.uuid4()),
),
]

# prepare tx msg
msg = composer.msg_batch_update_orders(
sender=address.to_acc_bech32(),
derivative_orders_to_create=derivative_orders_to_create,
spot_orders_to_create=spot_orders_to_create,
derivative_orders_to_cancel=derivative_orders_to_cancel,
spot_orders_to_cancel=spot_orders_to_cancel,
spot_market_orders_to_create=spot_market_orders_to_create,
derivative_market_orders_to_create=derivative_market_orders_to_create,
)

# broadcast the transaction
Expand Down
24 changes: 24 additions & 0 deletions examples/chain_client/exchange/query/66_OpenInterest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import asyncio
import json

from pyinjective.async_client_v2 import AsyncClient
from pyinjective.core.network import Network


async def main() -> None:
"""
Demonstrate fetching denom min notionals using AsyncClient.
"""
Comment on lines +9 to +11
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

Fix the docstring to match the actual functionality.

The docstring states "Demonstrate fetching denom min notionals" but the code actually fetches open interest using client.fetch_open_interest().

Apply this diff to correct the docstring:

-    """
-    Demonstrate fetching denom min notionals using AsyncClient.
-    """
+    """
+    Demonstrate fetching open interest using AsyncClient.
+    """
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"""
Demonstrate fetching denom min notionals using AsyncClient.
"""
"""
Demonstrate fetching open interest using AsyncClient.
"""
🤖 Prompt for AI Agents
In examples/chain_client/exchange/query/66_OpenInterest.py around lines 9 to 11,
the docstring incorrectly says "Demonstrate fetching denom min notionals" but
the code calls client.fetch_open_interest(); update the docstring to accurately
describe that this example demonstrates fetching open interest using AsyncClient
(e.g., "Demonstrate fetching open interest using AsyncClient.") so the comment
matches the implemented functionality.

# Select network: choose between Network.mainnet(), Network.testnet(), or Network.devnet()
network = Network.testnet()

# Initialize the Async Client
client = AsyncClient(network)

market_id = "0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6"
open_interest = await client.fetch_open_interest(market_id=market_id)
print(json.dumps(open_interest, indent=2))


if __name__ == "__main__":
asyncio.get_event_loop().run_until_complete(main())
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ async def main() -> None:
# initialize grpc client
client = AsyncClient(network)

deposits = await client.fetch_denom_decimal(denom="peggy0x87aB3B4C8661e07D6372361211B96ed4Dc36B1B5")
deposits = await client.fetch_auction_exchange_transfer_denom_decimal(
denom="peggy0x87aB3B4C8661e07D6372361211B96ed4Dc36B1B5"
)
print(deposits)


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ async def main() -> None:
# initialize grpc client
client = AsyncClient(network)

deposits = await client.fetch_denom_decimals(denoms=["inj", "peggy0x87aB3B4C8661e07D6372361211B96ed4Dc36B1B5"])
deposits = await client.fetch_auction_exchange_transfer_denom_decimals(
denoms=["inj", "peggy0x87aB3B4C8661e07D6372361211B96ed4Dc36B1B5"]
)
print(deposits)


Expand Down
Loading
Loading