Skip to main content

Python SDK for Ordora DEX — place orders, monitor status, and stream real-time market data

Project description

Ordora DEX — Python SDK

Official Python SDK for interacting with Ordora DEX from Python 3.10+ applications. Supports placing orders, cancelling orders, real-time status monitoring, and reading market data.


Table of Contents


Installation

Prerequisites: Python 3.10+

cd sdk/python
pip install -r requirements.txt

Core dependencies:

Package Purpose
web3 v6 Blockchain interaction (sign, send tx)
gql[aiohttp,websockets] Hasura GraphQL queries and WebSocket subscriptions
aiohttp Async HTTP transport
python-dotenv Load .env files

Quick Start

import asyncio
import os
from ordora_sdk import OrdoraSDK, PlaceAskNativeParams

# Initialize with a private key (default chain: bsc-testnet)
sdk = OrdoraSDK(private_key=os.environ["PRIVATE_KEY"])

print("Wallet:", sdk.address)

# Check BNB balance (synchronous)
balance = sdk.get_bnb_balance()
print(f"BNB: {balance / 1e18:.6f}")

# Fetch active markets (async)
markets = asyncio.run(sdk.get_markets())
for m in markets:
    print(m.pair_symbol)

# Sell 0.01 BNB at 310 USDT (synchronous)
result = sdk.place_ask_native(PlaceAskNativeParams(
    quote_token = "0x337610d27c682E347C9cD60BD4b3b107C9d34dDd",  # USDT testnet
    price       = 310.00,
    amount      = 0.01,
))
print("Order ID:", result.order_id)
print("TX Hash: ", result.tx_hash)

Important: Store private keys in environment variables — never hardcode them.


Configuration

Constructor

sdk = OrdoraSDK(
    private_key   = "0x...",
    chain         = "bsc-testnet",     # or "bsc-mainnet" or a ChainConfig object
    hasura_url    = None,              # override Hasura HTTP URL
    hasura_ws_url = None,              # override Hasura WebSocket URL
)

Supported Chains

Chain chain value Chain ID Contract
BSC Testnet "bsc-testnet" 97 0x9466a9259075859E14897AE556ca0B449BdF6e6D
BSC Mainnet "bsc-mainnet" 56 (not yet deployed)

Custom Chain

from ordora_sdk import OrdoraSDK, ChainConfig

sdk = OrdoraSDK(
    private_key = "0x...",
    chain = ChainConfig(
        chain_id         = 97,
        rpc_url          = "https://rpc-custom.example.com",
        contract_address = "0x...",
        wbnb_address     = "0x...",
        explorer_url     = "https://testnet.bscscan.com",
    ),
)

Environment Variables

Create a .env file in your project root:

PRIVATE_KEY=0x...trader_wallet_private_key...

Load with python-dotenv:

from dotenv import load_dotenv
import os

load_dotenv()
sdk = OrdoraSDK(private_key=os.environ["PRIVATE_KEY"])

Sync vs Async

Method type How to call
On-chain reads & writes Synchronous (blocking) — call directly
Hasura queries async coroutine — use asyncio.run() or await
Hasura subscriptions async coroutine — run as asyncio.create_task()

API Reference

Balance & Approval

sdk.get_bnb_balance() -> int

Get the wallet's native BNB balance in wei.

balance = sdk.get_bnb_balance()
print(f"{balance / 1e18:.6f} BNB")

sdk.get_token_balance(token_address: str) -> int

Get the ERC20 token balance in wei.

USDT = "0x337610d27c682E347C9cD60BD4b3b107C9d34dDd"
balance = sdk.get_token_balance(USDT)

sdk.approve_token(token_address: str) -> ApprovalResult

Approve an ERC20 token for the DEX contract (unlimited).

The SDK does this automatically when placing an order. Call this only to pre-approve.

result = sdk.approve_token(USDT)
if result.approved:
    print("Approved, TX:", result.tx_hash)
else:
    print("Already approved")

Place Order

All place order methods:

  • Automatically encode price/amount from human-readable → raw wei
  • Automatically approve ERC20 tokens if allowance is insufficient
  • Automatically generate a unique order_id and nonce
  • Return PlaceOrderResult(order_id, tx_hash)

sdk.place_ask_token(params) — Sell an ERC20 Token

from ordora_sdk import PlaceAskTokenParams

result = sdk.place_ask_token(PlaceAskTokenParams(
    base_token  = "0xTokenToSell...",
    quote_token = "0xUSDT...",
    price       = 312.50,   # 1 token = 312.50 USDT
    amount      = 1.5,      # sell 1.5 tokens
))
print("Order ID:", result.order_id)

sdk.place_ask_native(params) — Sell Native BNB

BNB is sent as msg.value — no approval needed.

from ordora_sdk import PlaceAskNativeParams

result = sdk.place_ask_native(PlaceAskNativeParams(
    quote_token = "0xUSDT...",
    price       = 312.50,   # 1 BNB = 312.50 USDT
    amount      = 0.5,      # sell 0.5 BNB
))

sdk.place_bid(params) — Buy a Token

Auto-approves the quote token if ERC20, or sends BNB if native.

from ordora_sdk import PlaceBidParams

result = sdk.place_bid(PlaceBidParams(
    base_token  = "0xTokenToBuy...",
    quote_token = "0xUSDT...",
    price       = 310.00,   # buy at 310.00 USDT per token
    amount      = 2.0,      # buy 2 tokens
))

Escrow required for BID: The SDK calculates this automatically: escrow = price × amount


Cancel Order

sdk.cancel_order(order_id: str) -> str

Cancel your own order. Returns the transaction hash.

tx_hash = sdk.cancel_order("0x1a2b3c...")
print("Cancelled, TX:", tx_hash)

Read Data (Hasura)

All Hasura methods are async. Data is already human-readable — no conversion needed.

Orders

import asyncio

# Open orders for this wallet (OPEN and PARTIAL)
open_orders = asyncio.run(sdk.get_open_orders())

# Full order history with pagination
result = asyncio.run(sdk.get_order_history(limit=20, offset=0))
print(f"Total: {result.total_count}, Has next: {result.has_next_page}")
for o in result.data:
    print(o.order_id, o.status)

# Single order by ID
order = asyncio.run(sdk.get_order_by_id("0x1a2b..."))
if order:
    print(order.status, order.base_filled)

Trades

# All trades for this wallet (as seller or buyer)
result = asyncio.run(sdk.get_my_trades(limit=50))

# Trade history for a pair
trades = asyncio.run(sdk.get_trades_by_pair("BNB_USDT", limit=30))

# All fills for a specific order
fills = asyncio.run(sdk.get_trades_by_order("0x1a2b..."))
for t in fills:
    print(f"Filled @ {t.price}, qty: {t.base_amount}")

Market & Ticker

markets = asyncio.run(sdk.get_markets())

tickers = asyncio.run(sdk.get_all_tickers())

ticker = asyncio.run(sdk.get_ticker("BNB_USDT"))
if ticker:
    print("Last price:", ticker.last_price)
    print("24h change:", ticker.price_change_pct_24h, "%")
    print("Volume:    ", ticker.base_volume_24h, "BNB")

Assets

tokens = asyncio.run(sdk.get_tokens())

locked = asyncio.run(sdk.get_locked_assets())
for a in locked:
    print(f"{a.locked_human} {a.token_symbol} ({a.order_count} active orders)")

Real-time Subscriptions

Subscriptions are async coroutines that stream data via WebSocket until cancelled. Run them as asyncio.create_task() alongside other work.

Subscribe Open Orders

import asyncio
from ordora_sdk import OrdoraSDK

async def main():
    sdk = OrdoraSDK(private_key="0x...")

    async def on_orders(orders):
        print(f"{len(orders)} open order(s)")
        for o in orders:
            print(f"  {o.pair_symbol} {o.side} @ {o.price}")

    task = asyncio.create_task(sdk.subscribe_open_orders(on_orders))
    await asyncio.sleep(60)   # run for 60 seconds
    task.cancel()

asyncio.run(main())

Subscribe to a Single Order's Status

async def on_status(order):
    if order is None:
        return
    print("Status:", order.status, "| Fill:", order.base_filled)
    if order.status == "FILLED":
        print("Order fully filled!")
        task.cancel()

task = asyncio.create_task(
    sdk.subscribe_order_status("0x1a2b...", on_status)
)

Subscribe to Incoming Trades

async def on_trades(trades):
    if not trades:
        return
    t = trades[0]
    print(f"Trade: {t.trade_type} {t.base_amount} @ {t.price}")

task = asyncio.create_task(sdk.subscribe_my_trades(on_trades))

Subscribe to Live Market Trades

async def on_market(trades):
    for t in trades:
        arrow = "▲" if t.trade_type == "buy" else "▼"
        print(f"{arrow} {t.price} | {t.base_amount} BNB")

task = asyncio.create_task(
    sdk.subscribe_live_trades("BNB_USDT", on_market, limit=20)
)

Subscribe to Locked Escrow

async def on_locked(assets):
    for a in assets:
        print(f"Locked: {a.locked_human} {a.token_symbol}")

task = asyncio.create_task(sdk.subscribe_locked_assets(on_locked))

On-chain Events

On-chain event listeners arrive faster than Hasura (~1-3 second indexing delay). They run in a background daemon thread and return a stop function.

sdk.on_order_placed(callback)

def on_placed(order_id: str, tx_hash: str):
    print("Order placed:", order_id)

stop = sdk.on_order_placed(on_placed)
# ... later ...
stop()  # stop listening

sdk.on_order_matched(callback)

def on_matched(ask_order_id: str, bid_order_id: str, tx_hash: str):
    print("Match occurred!")
    print("ASK:", ask_order_id)
    print("BID:", bid_order_id)

stop = sdk.on_order_matched(on_matched)

sdk.on_order_cancelled(callback)

def on_cancelled(order_id: str, tx_hash: str):
    print("Order cancelled:", order_id)

stop = sdk.on_order_cancelled(on_cancelled)

Utilities

sdk.get_remaining(order_id) -> str

Get the unfilled remaining amount of an order (human-readable).

remaining = sdk.get_remaining("0x1a2b...")
print("Remaining:", remaining, "BNB")

sdk.is_paused() -> bool

Check whether the DEX contract is paused.

if sdk.is_paused():
    print("DEX is under maintenance")

await sdk.place_ask_token_and_wait(params, timeout_seconds=60.0)

Place an ERC20 sell order and wait until FILLED.

result, order = await sdk.place_ask_token_and_wait(
    PlaceAskTokenParams(
        base_token  = "0x...",
        quote_token = "0x...",
        price       = 312.50,
        amount      = 1.0,
    ),
    timeout_seconds = 120.0,
)
print("Order filled! Status:", order.status)

await sdk.place_bid_and_wait(params, timeout_seconds=60.0)

result, order = await sdk.place_bid_and_wait(
    PlaceBidParams(base_token="0x...", quote_token="0x...", price=308.00, amount=1.0)
)

Types

PlaceAskTokenParams

@dataclass
class PlaceAskTokenParams:
    base_token:  str            # contract address of the token to sell
    quote_token: str            # contract address of the token to receive
    price:       float | str    # human-readable price
    amount:      float | str    # human-readable amount
    expires_at:  int | None     # unix timestamp, default = now + DEFAULT_EXPIRY

PlaceAskNativeParams

@dataclass
class PlaceAskNativeParams:
    quote_token: str
    price:       float | str
    amount:      float | str    # amount of BNB to sell
    expires_at:  int | None

PlaceBidParams

@dataclass
class PlaceBidParams:
    base_token:  str            # token to buy
    quote_token: str            # token used for payment
    price:       float | str
    amount:      float | str    # amount of base token to buy
    expires_at:  int | None

Order

@dataclass
class Order:
    order_id:      str          # format "0x..."
    trader:        str          # lowercase wallet address
    side:          str          # "ASK" | "BID"
    status:        str          # "OPEN" | "PARTIAL" | "FILLED" | "CANCELLED"
    pair_symbol:   str          # e.g. "BNB_USDT"
    price:         str          # human-readable
    amount:        str          # human-readable
    base_filled:   str          # amount filled so far
    quote_filled:  str
    placed_at:     str | None   # ISO timestamp
    updated_at:    str
    tx_hash:       str
    cancelled_tx:  str | None

PaginatedResult

@dataclass
class PaginatedResult:
    data:          list         # list of Order or Trade objects
    total_count:   int
    has_next_page: bool

Full Examples

Simple Market Making Bot

See examples/simple_bot.py for a complete bot that:

  • Reads market price from the Hasura ticker
  • Places an ASK above market price and a BID below it
  • Automatically re-places orders when they are filled
  • Cancels stale orders when price drifts >1%
  • Graceful Ctrl+C shutdown that cancels all active orders
PRIVATE_KEY=0x... python examples/simple_bot.py

Real-time Monitor

See examples/monitor_orders.py for:

  • Open order monitoring via WebSocket
  • Incoming trade feed
  • Locked escrow updates
  • Live market trade feed
  • On-chain event listeners
PRIVATE_KEY=0x... python examples/monitor_orders.py

Running Examples

1. Set up the environment

# In the sdk/python folder
echo "PRIVATE_KEY=0x..." > .env

Or export directly:

export PRIVATE_KEY=0xyour_private_key_here

2. Install dependencies

pip install -r requirements.txt

3. Run an example

# Place an order and monitor status
python examples/place_order.py

# Monitor all activity in real-time
python examples/monitor_orders.py

# Run the market making bot
python examples/simple_bot.py

4. Use in your own project

# Install (after pip install -e . or building the package)
from ordora_sdk import OrdoraSDK, PlaceAskNativeParams

Or for development, add the path manually:

import sys
sys.path.insert(0, "sdk/python/src")
from ordora_sdk import OrdoraSDK

Security

Never do this:

# ❌ WRONG — private key hardcoded in source code
sdk = OrdoraSDK(private_key="0xyourprivatekey...")

# ❌ WRONG — committing a .env file to git

Do this instead:

# ✅ CORRECT — read from environment variable
import os
sdk = OrdoraSDK(private_key=os.environ["PRIVATE_KEY"])

# ✅ CORRECT — add .env to .gitignore

Additional security notes:

  • The SDK never sends your private key to any server
  • Token approvals are made to the DEX contract only (no third parties)
  • All transactions are signed locally before being sent to the RPC

Error Reference

Errors from the DEX Contract

Error Cause Fix
MarketNotFound Token pair does not exist Check base_token and quote_token against get_markets()
MarketNotActive Market has been deactivated Wait for the market to be re-enabled
AmountBelowMinimum Amount is below the market minimum Check min_base_amount from get_markets()
OrderValueBelowMinimum Order value (price × amount) is too small Increase price or amount
InsufficientEscrow Insufficient balance to cover escrow Add funds or reduce the amount
NotYourOrder Attempting to cancel another wallet's order Only your own orders can be cancelled
ExpiryInPast expires_at is in the past Use a future Unix timestamp
EnforcedPause Contract is paused Check sdk.is_paused() before placing orders

SDK / Network Errors

try:
    result = sdk.place_ask_native(PlaceAskNativeParams(...))
except ValueError as e:
    print("Validation error:", e)      # e.g. "Market is not active"
except Exception as e:
    if "insufficient funds" in str(e):
        print("Insufficient BNB for gas + escrow")
    else:
        print("Unknown error:", e)

Subscription Errors

async def on_error(exc: Exception):
    print("WebSocket error:", exc)

task = asyncio.create_task(
    sdk.subscribe_open_orders(on_data, on_error=on_error)
)

File Structure

sdk/python/
├── README.md
├── pyproject.toml
├── requirements.txt
├── src/
│   └── ordora_sdk/
│       ├── __init__.py    — public exports
│       ├── sdk.py         — OrdoraSDK main class
│       ├── contract.py    — on-chain interaction via web3.py
│       ├── hasura.py      — Hasura GraphQL queries & subscriptions
│       ├── types.py       — all dataclasses & type definitions
│       ├── abi.py         — DEX contract ABI
│       └── constants.py   — chain configs, addresses, ERC20 ABI
└── examples/
    ├── place_order.py     — example: place ASK + BID + monitor status
    ├── monitor_orders.py  — monitor all activity in real-time
    └── simple_bot.py      — complete market making bot

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ordora_sdk-0.1.0.tar.gz (27.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ordora_sdk-0.1.0-py3-none-any.whl (22.8 kB view details)

Uploaded Python 3

File details

Details for the file ordora_sdk-0.1.0.tar.gz.

File metadata

  • Download URL: ordora_sdk-0.1.0.tar.gz
  • Upload date:
  • Size: 27.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.11

File hashes

Hashes for ordora_sdk-0.1.0.tar.gz
Algorithm Hash digest
SHA256 4127388e47b08a86c493a6f645a55ff3d6d5c37e2924f372f7b09e0b2635d876
MD5 d8b9314690e083f80d12e91e4adf89dd
BLAKE2b-256 ab40da1388eabf4b7dc08c82c8cf9ab086e97d76ee0fd54419b021048158b3e8

See more details on using hashes here.

File details

Details for the file ordora_sdk-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: ordora_sdk-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 22.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.11

File hashes

Hashes for ordora_sdk-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c30fe58b4e8a8f0014a07b46bf81a5e6bd06e5a03e174ad60f41650c455e78d4
MD5 b298559cb67c72777fb81a37f8688992
BLAKE2b-256 958b49d464b8e257da8208df41a1843b8e4bc5a76355d1673be36d2a341c7d97

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page