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.3.0.tar.gz (36.3 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.3.0-py3-none-any.whl (44.2 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: forkast_py_client-0.3.0.tar.gz
  • Upload date:
  • Size: 36.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.4.1 CPython/3.12.6 Windows/11

File hashes

Hashes for forkast_py_client-0.3.0.tar.gz
Algorithm Hash digest
SHA256 3349ab1d7116d0796399bf88d9592c75feb1cbbe21bbea9587f89fc3feda4ca3
MD5 2f831f0f3eae4a5da23d40502029c1e7
BLAKE2b-256 08706d7a54ba592852c0890d0c199a383d1f934d81ad701d6f65e4d84f7fa294

See more details on using hashes here.

File details

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

File metadata

  • Download URL: forkast_py_client-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 44.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.4.1 CPython/3.12.6 Windows/11

File hashes

Hashes for forkast_py_client-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 58549e006ee2c25c700803141ee219e013da8bbd0266659d76dee6e2f603a6b9
MD5 e914c04afb0c92544d1bd1bbafc62ca5
BLAKE2b-256 3eff2aa9d135b1dd65e06d247ebff29221f5ba3749715d78cb095a590b483d9c

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