Skip to main content

Python client for the Forkast Orderbooks

Project description

Forkast Python SDK

A Python SDK for interacting with the Forkast trading platform


✨ Features

  • 🔐 Gnosis Safe (Proxy Wallet) approval via MultiSend
  • 📝 Place / cancel orders (EIP-712 signed)
  • 📊 Fetch balances, markets, orders
  • 🌐 Network-aware (Testnet / Mainnet)
  • 🧪 Integration tests

🚧 Requirements

  • Python >= 3.12
  • pip
  • An async-compatible environment (asyncio)

📦 Installation

pip install forkast-py-client

🌀 Running

⚠️ Examples require Python ≥ 3.12 and a virtual environment. You can see the full quickstart example below at examples/example.py. Run examples with:

python3 examples/example.py

⚠️ Async Usage

This SDK is fully asynchronous and built on top of aiohttp.

  • All network calls must be awaited
  • The SDK must be used inside an async event loop
  • Always use async with ForkastSDK(...) or call await sdk.open()

Example:

async with ForkastSDK(...) as sdk:
    ...

🚀 Quick Start

Minimal Example

import asyncio
from forkast_py_client import ForkastSDK, Network

async def main():
    async with ForkastSDK(Network.TESTNET) as sdk:
        market_service = sdk.get_market_service()
        event_id = 35 # Replace with a valid event id
        event_data = await market_service.get_event_data(event_id)
        print("Event data: ", event_data)

asyncio.run(main())

1️⃣ Initialize SDK

import asyncio
from eth_account import Account
from forkast_py_client import ForkastSDK, MarketOutcome, Network, load_abi, create_provider_without_signer

async def main():
    # Initialize SDK
    async with ForkastSDK(
        network=Network.TESTNET, # or Network.MAINNET
        api_key="YOUR_API_KEY"
    ) as sdk:

2️⃣ Login using private key

account_service = sdk.get_account_service()

# Login using private key
private_key = "YOUR_PRIVATE_KEY"
login = await account_service.login_with_private_key(private_key)
access_token = login.access_token
print("access token: ", access_token)

3️⃣ Fetch user profile

user = await account_service.get_user(access_token)
print("User: ", user)

4️⃣ Fetch balances

balances_service = sdk.get_balances_service()

balances = await balances_service.get_balances(access_token)
print("Balances: ", balances)

5️⃣ Fetch event data

market_service = sdk.get_market_service()
event_id = 35 # Replace with a valid event id
event_data = await market_service.get_event_data(event_id)
print("Event data: ", event_data)

6️⃣ Choose a market and get its details

market_id = event_data.markets[0].id
outcome_id = event_data.markets[0].outcomes[0].id
outcome_token_id = event_data.markets[0].outcomes[0].token_id
outcome_type = event_data.markets[0].outcomes[0].outcome_type
order_book = await market_service.get_order_book(market_id, outcome_id, outcome_type)
print("Market ID: ", market_id)
print("Outcome ID: ", outcome_id)
print("Outcome Token ID: ", outcome_token_id)
print("Outcome Type: ", outcome_type)
print("Orderbook: ", order_book)

7️⃣ Get token prices

token_prices = await market_service.get_token_prices(market_id, 0) # 0 = BUY, 1 = SELL
print("Token prices: ", token_prices)

8️⃣ Approve if needed

Required once before placing orders.

signer = Account.from_key(private_key)
provider = await create_provider_without_signer(Network.TESTNET)
proxy_wallet = "YOUR_PROXY_WALLET_ADDRESS"
gnosis_safe_abi = load_abi("GnosisSafe.abi.json")
multi_send = "MULTI_SEND_CONTRACT_ADDRESS"
chain_id = 421614 # Arbitrum Sepolia (TESTNET), replace with correct chain id
result = await account_service.approve_max_platform_credits_for_proxy_wallet(
    signer=signer,
    wallet_proxy_address=proxy_wallet,
    buy_amount=10000000,
)
print("approve_max_platform_credits_for_proxy_wallet_success response: ", result)

Send transaction if approval is required

if result is not None:
    gnosis_safe = provider.eth.contract(
        address=proxy_wallet,
        abi=gnosis_safe_abi,
    )
    tx = await gnosis_safe.functions.execTransaction(
        provider.to_checksum_address(multi_send),
        0,  # value
        bytes.fromhex(result.data_sign["data"][2:]),
        1,  # DELEGATE_CALL
        0,
        0,
        0,
        "0x0000000000000000000000000000000000000000",
        "0x0000000000000000000000000000000000000000",
        bytes.fromhex(result.signature[2:]),
    ).build_transaction(
        {
            "from": signer.address,
            "nonce": await provider.eth.get_transaction_count(signer.address),
            "gas": 500_000,
            "chainId": chain_id,
        }
    )
    print("Built tx: ", tx)

    signed_tx = signer.sign_transaction(tx) # The signer address will be the one sending tx, so we need to ensure signer has enough gas
    tx_hash = await provider.eth.send_raw_transaction(signed_tx.raw_transaction)
    await provider.eth.wait_for_transaction_receipt(tx_hash)
    print("Tx Hash: ", tx_hash.hex())

⚠️ The signer address must have enough native gas.

9️⃣ Place an order

order_service = sdk.get_order_service()
market_outcome = MarketOutcome(
    id=outcome_id,  # outcome id
    token_id=outcome_token_id,  # outcome token id
    outcome_type=outcome_type,  # 0 = No, 1 = Yes
)
resp = await order_service.place_single_order(
    event_id=event_id,
    market_id=market_id,
    token=market_outcome,
    account={
        "wallet": signer.address,
        "private_key": private_key,
        "proxy_wallet": proxy_wallet,
    },
    price=0.7, # the price you want to place your order
    amount=96, # the amount you want to buy/sell
    side=0,  # the side of your order: 0 = BUY, 1 = SELL
    access_token=access_token,
)
print("place_single_order response:", resp)
order_id_placed = None
if resp.success:
    order_id_placed = resp.order_result["data"]["id"]
    print("Order ID placed: ", order_id_placed)

🔟 Get all orders

resp = await order_service.get_all_orders(
    address=proxy_wallet,
    outcome_id=outcome_id,
    access_token=access_token,
    limit=10,
    page=1,
)
print("get_all_orders response:", resp)

🟡 Cancel order

if order_id_placed:
    resp = await order_service.cancel_order(
        order_id=order_id_placed,
        access_token=access_token,
    )
    print("cancel_order response:", resp)

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

forkast_py_client-0.2.0.tar.gz (27.7 kB view details)

Uploaded Source

Built Distribution

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

forkast_py_client-0.2.0-py3-none-any.whl (35.8 kB view details)

Uploaded Python 3

File details

Details for the file forkast_py_client-0.2.0.tar.gz.

File metadata

  • Download URL: forkast_py_client-0.2.0.tar.gz
  • Upload date:
  • Size: 27.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.2.1 CPython/3.12.7 Windows/11

File hashes

Hashes for forkast_py_client-0.2.0.tar.gz
Algorithm Hash digest
SHA256 2b05093eaac9c95cd4234d5edd11c46b1c474335cf593446fe4e71d4eafac869
MD5 0435075084b50a7093f2c59bb0635f5f
BLAKE2b-256 1d8813fb611e30c26f27c40222fff486b66954e7dfdf81047f2b39104c7aba9e

See more details on using hashes here.

File details

Details for the file forkast_py_client-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: forkast_py_client-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 35.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.2.1 CPython/3.12.7 Windows/11

File hashes

Hashes for forkast_py_client-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 f5b2abafa307fc5bc998ec2febae2f9cb64afbde4d9cc8f35b534fc82c8d94bb
MD5 ef9485a754ce72244fdde9e42bc57337
BLAKE2b-256 652230893472e3e9223a9fff3a5adadf954a95f2cf90cdaae868680e342b4052

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