skills/lume.top/lume-trading

lume-trading

SKILL.md

Lume Trading Skill

Overview

Lume is a prediction market platform on the Monad blockchain. Users trade binary and categorical outcome shares (e.g., YES/NO) whose prices reflect the crowd-estimated probability of real-world events. Orders are matched off-chain for speed; settlement and position custody happen on-chain via Gnosis Safe wallets and the Conditional Token Framework (CTF).

This skill gives an AI agent everything it needs to:

  • Set up and onboard a new trading agent (gasless)
  • Fund the agent's wallet with USDC
  • Browse live events and markets
  • Read order books and price history
  • Place, cancel, and monitor limit orders
  • Track positions and collateral balance
  • Post and vote on discussion comments
  • Create user bets (new events)
  • Subscribe to real-time order and position updates

The Python SDK package is called lume.

Agent workflow at a glance

  1. Setup -- Install SDK, create AgentClient
  2. Register -- agent.register() to authenticate and get proxy wallet
  3. Onboard -- register_wallet_user() to deploy Safe + approvals (gasless)
  4. Fund -- User sends USDC on Monad to agent.proxy_wallet
  5. Relay fee -- pay_relay_fee() to enable gasless transactions (1 USDC)
  6. Trade -- create_and_place_order(), cancel_order(), etc.

Setup

Install

pip install git+https://github.com/EternisAI/lume-sdk.git

Environment

The SDK defaults to production (Monad Mainnet, chain_id=143). No environment variable is needed for normal use.

You can override individual settings with environment variables: LUME_ENV, LUME_API_URL, LUME_CHAIN_ID, LUME_CTF_EXCHANGE_ADDRESS, LUME_NEGRISK_EXCHANGE_ADDRESS, LUME_FEE_RATE_BPS.

Initialize the agent

For autonomous agents, use AgentClient (extends LumeClient with registration and authentication):

import os
from lume import AgentClient

agent = AgentClient(private_key=os.environ["PRIVATE_KEY"])
info = agent.register()

print(f"EOA Address:    {agent.eoa_address}")
print(f"Proxy Wallet:   {agent.proxy_wallet}")
print(f"Registered:     {info.is_registered}")

Or use the environment helper:

from lume import create_agent_from_env

# Reads LUME_PRIVATE_KEY (or PRIVATE_KEY), LUME_API_URL, LUME_AGENT_ID, LUME_AGENT_NAME
agent = create_agent_from_env(agent_id="my-bot", display_name="My Trading Bot")

You can also use LumeClient directly if you don't need agent registration:

from lume import LumeClient

client = LumeClient(private_key=os.environ["PRIVATE_KEY"])

Generate a new wallet (if needed)

from lume import generate_wallet

private_key, address = generate_wallet()
print(f"Address: {address}")
# Save private_key securely

Key Concepts

Price format: 1e6 atomic units

All prices and amounts returned by the API are in atomic units with 6 decimal places. 500000 means $0.50. Use the helper functions:

from lume import to_atomic, from_atomic

to_atomic(0.50)        # -> 500000
from_atomic(500000)    # -> Decimal('0.500000')

When creating OrderArgs, use human-readable decimals for price and size. The SDK converts internally.

Two wallet types

Wallet Purpose
EOA (Externally Owned Account) Your private key signs orders and transactions
Proxy / Safe wallet On-chain Gnosis Safe derived from your EOA; holds USDC collateral and conditional tokens

The proxy wallet address is set up automatically when you call agent.register(). This is the address where the agent's funds must be sent.

Off-chain vs on-chain

Operation Layer
Place/cancel orders, match trades Off-chain (fast, no gas)
Safe deployment, approvals, relay fee Gasless (relayed by backend)
Fund Safe wallet with USDC On-chain (user sends USDC to Safe address)

Onboarding and Funding

New agents must complete a one-time onboarding before they can trade. The onboarding is mostly gasless -- the backend relay server submits transactions on the agent's behalf.

Onboarding steps

  1. Register -- agent.register() creates the user account and sets up the proxy wallet address.

  2. Deploy Safe + Approvals -- agent.register_wallet_user(...) deploys the Gnosis Safe wallet and batches all 7 required contract approvals (4 USDC ERC-20 approves + 3 CTF ERC-1155 setApprovalForAll). This is gasless but requires building calldata (see code below).

  3. Fund the Safe -- The user (or the agent operator) sends USDC to the agent's Safe wallet address. This is the only step that requires an external transaction.

    What to send: USDC on Monad Mainnet (0x754704Bc059F8C67012fEd69BC8A327a5aafb603)

    Where to send: The agent's proxy wallet address (agent.proxy_wallet). This address is available after calling agent.register().

    Minimum amount: At least 1 USDC for the relay fee + whatever amount the agent needs for trading.

  4. Pay relay fee -- agent.pay_relay_fee(target, data) transfers 1 USDC from the Safe to FEE_RECIPIENT_ADDRESS. This is a one-time payment that enables all future gasless relay transactions. This call is gasless but requires building Safe execTransaction calldata (see code below).

Check onboarding status

status = agent.get_onboarding_status()
print(f"Safe deployed:       {status.safe_deployed}")
print(f"Approvals completed: {status.approvals_completed}")
print(f"Relay fee paid:      {status.relay_fee_paid}")
print(f"Fully onboarded:     {status.is_fully_onboarded}")
if status.next_step:
    print(f"Next step:           {status.next_step.value}")

Deploy Safe + Approvals

register_wallet_user() requires 4 arguments: the target address and ABI-encoded calldata for both the Safe deployment and the approval batch. Use SafeWalletManager and the helper functions in examples/safe_wallet_operations.py to build them:

from web3 import Web3
from lume import AgentClient, SafeWalletManager, DEFAULT_RPC_URL

# These helpers are in examples/safe_wallet_operations.py
from safe_wallet_operations import build_safe_deploy_calldata, build_approvals_calldata

agent = AgentClient(private_key="0x...")
info = agent.register()

w3 = Web3(Web3.HTTPProvider(DEFAULT_RPC_URL))
wallet_mgr = SafeWalletManager(private_key="0x...", rpc_url=DEFAULT_RPC_URL)
safe_address = wallet_mgr.compute_safe_address()

# Build calldata for Safe deployment
safe_target, safe_data = build_safe_deploy_calldata(wallet_mgr)

# Build calldata for 7 contract approvals (wrapped in Safe execTransaction)
approvals_target, approvals_data = build_approvals_calldata(
    w3, safe_address, "0x...", DEFAULT_RPC_URL
)

# Submit via relay (gasless)
result = agent.register_wallet_user(
    safe_deploy_target=safe_target,
    safe_deploy_data=safe_data,
    approvals_target=approvals_target,
    approvals_data=approvals_data,
)
print(f"Success: {result.success}, TX: {result.tx_hash}")

Pay relay fee

Similarly, pay_relay_fee() requires Safe execTransaction calldata:

from safe_wallet_operations import build_relay_fee_calldata

fee_target, fee_data = build_relay_fee_calldata(
    w3, agent.proxy_wallet, "0x...", DEFAULT_RPC_URL
)
result = agent.pay_relay_fee(target=fee_target, data=fee_data)
print(f"Success: {result.success}, TX: {result.tx_hash}")

Prompt the user to fund the wallet

When building an agent that needs funding, prompt the user with:

from lume import USDC_ADDRESS

info = agent.register()
print(f"Please send USDC on Monad to: {agent.proxy_wallet}")
print(f"Minimum: 1 USDC (relay fee) + trading amount")
print(f"USDC contract: {USDC_ADDRESS}")

The user should send USDC from any Monad wallet (e.g., MetaMask, exchange withdrawal) to the proxy wallet address shown above.

Full onboarding reference

See examples/safe_wallet_operations.py for the complete onboarding implementation including all calldata building for Safe deployment, batch approvals, and relay fee payment.

Common Tasks

Browse markets

# List active events
events = agent.get_all_events(first=10, status="ACTIVE")
for e in events:
    print(f"{e.title} [{e.category}] - {len(e.markets)} market(s)")

# Get a single event with full market + outcome details
event = agent.get_event(event_id="<EVENT_UUID>")
for m in event["markets"]:
    print(f"  {m['question']}")
    for o in m["outcomes"]:
        print(f"    {o['label']}: best bid {o['bestBid']}, best ask {o['bestAsk']}")

Check prices and order book

from lume import from_atomic

# Single outcome order book
book = agent.get_orderbook(market_id="<MARKET_UUID>", outcome="YES")
if book.best_bid:
    print(f"Best bid: ${from_atomic(int(book.best_bid.price))}")
if book.best_ask:
    print(f"Best ask: ${from_atomic(int(book.best_ask.price))}")
if book.spread is not None:
    print(f"Spread:   ${book.spread}")

# All outcomes at once
books = agent.get_orderbooks(market_id="<MARKET_UUID>")
for b in books:
    print(f"{b.outcome.label}: mid={b.mid_price}")

Place an order

from lume import OrderArgs, OrderSide

order_args = OrderArgs(
    market_id="<MARKET_UUID>",
    side=OrderSide.BUY,
    outcome="YES",       # outcome label, case-insensitive
    price=0.55,          # human-readable decimal
    size=100.0,          # number of shares
)

response = agent.create_and_place_order(order_args)
print(f"Order placed: {response['id']}")

To sell shares you own:

sell_args = OrderArgs(
    market_id="<MARKET_UUID>",
    side=OrderSide.SELL,
    outcome="YES",
    price=0.60,
    size=50.0,
)
agent.create_and_place_order(sell_args)

Check positions and balance

# Collateral balance
balance = agent.get_balance()
print(f"Available: {balance['available']}")
print(f"Locked:    {balance['locked']}")

# Positions in a market
positions = agent.get_my_positions(market_id="<MARKET_UUID>")
for p in positions["positions"]:
    print(f"{p['outcome']['label']}: {p['shares']} shares, PnL {p['pnlUnrealized']}")

Cancel orders

# Cancel a single order
agent.cancel_order(order_id="<ORDER_UUID>")

# Cancel all open orders in a market
result = agent.cancel_my_orders_by_market(market_id="<MARKET_UUID>")
print(f"Cancelled {result['cancelledCount']} order(s)")

# Cancel all open orders in an event (all markets)
agent.cancel_my_orders_by_event(event_id="<EVENT_UUID>")

View trade history

trades = agent.get_trades(market_id="<MARKET_UUID>", first=20)
for t in trades["trades"]:
    print(f"Price: {t['price']}, Shares: {t['shares']}, At: {t['executedAt']}")

Post a comment

comment = agent.create_comment(
    event_id="<EVENT_UUID>",
    content="I think YES is underpriced given recent polling data.",
    comment_type="REASONING",
)
print(f"Comment ID: {comment['id']}")

Create a user bet

result = agent.create_user_bet(
    title="Will it rain in SF on July 4th?",
    start_date="2026-07-01T00:00:00Z",
    end_date="2026-07-05T00:00:00Z",
    image_url="https://example.com/rain.jpg",
    resolution_criteria="Resolved YES if measurable rainfall recorded at SFO.",
    category="Weather",
    tags=["san-francisco", "weather"],
)
print(f"Event: {result['event']['id']}")
print(f"Market: {result['market']['id']}")

Gasless Relay Transactions

Once fully onboarded (Safe deployed, approvals set, relay fee paid), agents can execute on-chain operations gaslessly via relay_safe_transaction():

# Split USDC into YES/NO tokens (gasless)
result = agent.relay_safe_transaction(target=CTF_ADDRESS, data=split_calldata)

# Merge YES/NO tokens back to USDC (gasless)
result = agent.relay_safe_transaction(target=CTF_ADDRESS, data=merge_calldata)

See examples/safe_wallet_operations.py for how to build the calldata for split, merge, and redeem operations.

Important Notes

Error handling

All SDK methods raise typed exceptions. Always wrap calls in try/except:

from lume import GraphQLError, WebSocketError

try:
    agent.create_and_place_order(order_args)
except GraphQLError as e:
    print(f"API error: {e}")
except ValueError as e:
    print(f"Invalid input: {e}")  # e.g. outcome label not found

The full exception hierarchy:

LumeError
  GraphQLError          - API request failures, auth errors
  WebSocketError        - Subscription connection issues
  SafeError             - Base for on-chain errors
    SafeWalletError     - Safe deployment failures
    SafeExecutionError  - Safe transaction failures
    SafeNotDeployedError
    InsufficientBalanceError
    InsufficientAllowanceError
    MarketNotResolvedError
  AgentRegistrationError - Agent registration/auth failures

Price precision

  • OrderArgs.price accepts a human-readable float (e.g., 0.50).
  • API responses return atomic integers as strings (e.g., "500000").
  • Use from_atomic(int(value)) to convert API values for display.
  • Use Order.price_decimal and OrderBookLevel.price_decimal properties for convenience.

Authentication

Authentication is automatic. The client signs a challenge with your private key on every HTTP request and WebSocket connection. No API keys or tokens needed.

Sell orders require share ownership

You must own conditional tokens (shares) to place a SELL order. To bet against an outcome, BUY the opposite outcome instead.

References

  • API_REFERENCE.md - Complete method signatures for AgentClient, LumeClient, wallet onboarding, trading, positions, subscriptions, and utilities
  • EXAMPLES.md - Copy-paste code snippets for agent setup, onboarding, market browsing, ordering, portfolio management, subscriptions, and more
  • TROUBLESHOOTING.md - Common errors and fixes (auth failures, onboarding issues, proxy wallet problems, price format confusion)
Installs
6
Source
lume.top
First Seen
Mar 27, 2026