orblivion
Trade on Robinhood Chain (chain 4663) through Orblivion, the trading layer for Orbio agents, from Python. Orblivion finds the route, builds the transaction and simulates it. Your agent's own key signs it, and Orblivion's relay sends it.
Nothing the server returns is signed until this SDK has decoded and checked it locally, and every mined transaction
is checked again against what was signed. The decoder and the checks are this package's own: the same rules, rule names
and error codes as the TypeScript SDK (@orblivion/sdk), proven against the same recorded answers.
Python 3.10 or later. Depends on eth-account and eth-abi only; HTTP uses the standard library.
Install
pip install orblivion
Quickstart
import os
from eth_account import Account
from orblivion import OrblivionClient
client = OrblivionClient(account=Account.from_key(os.environ["AGENT_KEY"]),
max_notional_usd=25) # the client refuses anything bigger, whatever the server says
client.sign_in() # optional: the first call signs in for you
quote = client.quote(sell="ETH", buy="USDG", amount_usd=10)
plan = client.plan(sell="ETH", buy="USDG", amount_usd=10) # every check, nothing signed
dry = client.trade(sell="ETH", buy="USDG", amount_usd=10, dry_run=True)
sent = client.trade(sell="ETH", buy="USDG", amount_usd=10) # checked, signed, sent, checked again
print(sent.tx_hash, sent.amount_out, sent.min_out, sent.fee)
The client talks to https://api.orblivion.com unless told otherwise: pass another server first, or leave it out
(OrblivionClient(account=...)) and set ORBLIVION_API_URL. Plain http is accepted only for 127.0.0.1,
localhost and ::1; anything else must be https.
headers={...} adds headers to every request to the Orblivion API (never to the RPC or the sequencer); their values
are never shown in an error or a repr.
Signing in
sign_in() asks the server for an EIP-4361 (Sign-In with Ethereum) message, checks it names this server's host, chain
4663, this wallet and a short expiry, signs it, and receives an API key bound to that wallet, kept in memory only.
Pass api_key= to reuse one; the client checks it is bound to its wallet first.
Quote, plan, trade
| Call | What it does |
|---|---|
quote(sell=, buy=, amount_usd= | amount_in=, slippage_bps=None, shield=False) |
The server's best route and what it discloses. Read-only; the server's word. |
plan(sell=, buy=, amount_usd= | amount_in=, ...) |
The trade's plan, checked exactly as trade checks it, with nothing signed. A sale of an ERC-20 token whose swap is built only around a Permit2 signature stops after the permit's checks (complete=False). |
trade(sell=, buy=, amount_usd= | amount_in=, slippage_bps=None, approval="budget", dry_run=False, accept_risks=None, shield=False, policies=None) |
Prepare, check, sign, send through the relay, wait for the receipt, check what was mined. With dry_run=True it stops after signing and sends nothing. |
search_tokens(q, tier=None, kind=None, limit=20, shield=False) |
Search the token registry. Symbols are copied by impersonators: pick a token by its address. |
inspect_token(token, shield=False) |
One token as a trade would see it, checked against the SDK's own reads of the chain, refusing nothing. |
wallet.balances(tokens=None, who=None) |
ETH and token balances, read from the chain. |
approvals.status(), approvals.revoke() |
The wallet's Permit2 approvals; take them back. |
Core tokens are named by symbol (ETH, WETH, USDG, cbBTC, ORBIO, NVDA, SPY); every other token by its
contract address. Amounts are int in the token's smallest unit (parse_token("1.5", "USDG")), or amount_usd.
Fast mode
For an agent that holds its own key and trades a major pair, fast() cuts the path from decision to relay to one call:
fast = client.fast().start() # keeps its Chainlink marks, nonce, base fee, allowances and fee tier warm
r = fast.trade("ETH", "USDG", amount_usd=10) # or client.fast_trade(...)
print(r.path, r.tx_hash, r.timings["decision_to_accepted_ms"])
The SDK builds the transaction itself on a pinned route (FAST_ROUTES: ETH, WETH, USDG, NVDA and SPY; never cbBTC,
which takes the normal path before anything is signed), with min_out from its own quote of that route (kept warm; the
tolerance at most the server's pair default), your fee tier in ETH, WETH or USDG by the fee currency rule, and the
output to your wallet; checks it with verify_fast (the server's own rules), signs it and sends it to
POST /api/v1/live/fast, which rebuilds the calldata and relays only an exact match.
- Anything stale, an ineligible pair (ORBIO, launchpad and long-tail tokens, a stock token after hours), a token you
haven't approved to Permit2 yet, a pool further under the Chainlink marks than its fee and the pair's band (75 bps
against USDG, 125 for a cross pair), or marks from your RPC more than 3% from the server's falls back to
trade()before anything is signed (r.path == "fallback",r.fallback_reason);fallback=Falseraises instead. - One
trade()never lets two transactions land. After a refusal the server answers before relaying, the SDK asks your own RPC about the signed transaction first: on chain, it is the result (r.note); its nonce taken by another transaction, the next one takes a fresh nonce; otherwise the retry or the fallback signs at the same nonce. A refusal that may follow a relay (NONCEwithout that proof,IN_FLIGHT,SUBMIT_REJECTED,SEQUENCER_UNREACHABLE, no answer) raises with the hash.trade(nonce=n)signs the normal path's first transaction atn(and refuses when the wallet's pending nonce isn'tn). tolerance_bpstightens the minimum (it can't exceed the server's cap);min_outsets your own when it is higher. The slippage table is pinned in the SDK (FAST_TOLERANCES); the server's published one can only tighten it.simulate=Falseskips the server's simulation of the exact signed transaction (one round trip to the node); a transaction that would revert then costs its gas.dry_run=Truehas the server check and simulate it, and relay nothing.- Run the agent in US East, near the chain's sequencer: from far away its round trip dominates every trade.
Risks and Dossier Shield
Any token with a route trades; the server discloses what it found in risks ([{code, severity, detail}]).
- Severe risks trade only when you accept them by name in
accept_risks:HONEYPOT,TRANSFER_TAX,LOOKALIKE,AMOUNT_HOOK,HIGH_PRICE_IMPACT,UNSCREENED,IMPERSONATOR. The SDK never accepts a risk for you, and accepting one loosens none of its price or minimum-output checks. - Dossier Shield (
shield=True): 10 bps on top of the trade's fee, Dossier's verdict on the token, andpolicies(block_impersonators, on by default,verified_only,block_risks).
r = client.trade(sell="ETH", buy="0x…", amount_usd=5, accept_risks=["TRANSFER_TAX"])
s = client.trade(sell="ETH", buy="0x…", amount_usd=5, shield=True, policies={"verified_only": True})
s.dossier # {"verdict": "verified", "url": "…"}
s.fee_bps, s.fee_class, s.fee_tier # 60, "other", 50
Fees
Orblivion's fee depends on the two tokens a trade swaps (fee schedule 2, pinned in constants):
- Major trades: both tokens are majors (
ETH,WETH,USDG,cbBTC,ORBIO, or a Robinhood stock token). They pay the wallet's volume tier: 25 bps, or 15 or 10 after enough 30-day volume. - Every other trade pays exactly 50 bps.
- Dossier Shield adds 10 bps to either.
The SDK works out the class itself, from its own pinned majors and, for a token named by address, its own read of the
chain (the stock tokens' beacon). It never takes the server's word for the class. A plan whose fee_class differs is
refused (FEE_CLASS). The plan's fee_bps must be one the class and Shield mode allow, fee_tier must be that fee
without Shield's 10, and the calldata must pay exactly fee_bps (FEE_BPS). The SDK can't know the wallet's volume, so
it accepts any tier up to the posted 25 for a major trade. max_fee_bps= on the client sets a hard cap on top (None, the
default, leaves the schedule to bound the fee). Every quote, plan and trade result carries fee_bps, fee_class and
fee_tier.
The default slippage covers the trade's posted fee (40 bps plus the fee, within the pair's tolerance plus the fee).
Sessions and hosted agents
owner = OrblivionClient(url, wallet_account)
executor = OrblivionClient(url, hot_account)
grant = owner.sessions.grant(executor=executor.address, max_trade_usd=20, max_day_usd=60, max_trades=10, hours=12,
allow_7702=True)
executor.sessions.trade(grant.session_id, sell="ETH", buy="USDG", amount_usd=5, session=grant)
owner.sessions.revoke(grant.session_id)
The owner's signed root delegation carries caveats the chain enforces (the router only, ETH per trade and per day, the
expiry, the number of trades, the executor as the only redeemer), and its salt binds the limits, the accepted risks,
Shield and the fees, so neither the server nor the executor can widen them. Every new grant is binding version 4, with
fees exactly the schedule's for its Shield mode (verify.fee_terms); the owner's SDK refuses any other version or
fees (GRANT_LIMITS). A session trade pays what the binding allows for the trade's own class, which the executor's SDK
and Orblivion's co-signer each read themselves. Sessions granted before version 4 keep their one fee. An executor trading without
the Grant can state the session's Shield mode with session_shield=. Otherwise its SDK holds the trade to the
schedule without Shield. client.hosted runs an agent's loop on the Orblivion server; a live hosted agent's executor
must carry the co-signer's attestation, which the SDK verifies before the owner signs anything.
An AI agent (brain="orbio_llm") decides each tick with a model on Orbio's gateway from the owner's own strategy prompt:
client.hosted.create(brain="orbio_llm", params=ai_params("Follow the trend; keep half in USDG.",
"anthropic/claude-haiku-5.5"), interval_s=900, pairs=["ETH-USDG"], limits=...): Orblivion's own Orbio credits pay
first (a fair daily share per owner wallet, cheap models, 15-minute ticks), then the owner's own Orbio balance.
hosted.ai_options() lists the templates, models and prices, the pool and this wallet's share; hosted.spend(id) its
model calls and cost. The owner's Orbio balance needs Sign in with Orbio: hosted.ai_connect() returns Orbio's consent
page, refused (AI_AUTHORIZE_URL) unless it is https://www.orbio.so/oauth/authorize with PKCE S256 asking for exactly
Orblivion's grant: its client id (pass client_id= to pin it; until Orbio registers the app, the one the server's AI
options name, which its answer must repeat), the server's own /orbio-callback.html as the redirect, and the scope
openid inference, each named once. Orblivion's redirect page finishes it. No answer carries an Orbio credential.
Sign in with Orbio isn't open on the public API yet.
Your account
page = client.me.trades(limit=25) # newest first; refused=True adds refused calls and unmined trades
for t in page.trades:
print(t.time, t.path, t.token_in.token, t.amount_in, t.token_out.token, t.amount_out, t.amount_out_kind, t.status)
older = client.me.trades(limit=25, before=page.next_before) if page.next_before else None
p = client.me.portfolio() # balances at independent marks; p.total_usd counts priced ones only
print(p.total_usd, p.fee_tier.tier_bps, p.fee_tier.volume_to_next_usd)
s = client.me.sessions() # active sessions and hosted agents in summary
The signed-in wallet's own trades through Orblivion (agent, session, hosted or fast; amount_out is what the wallet
received once mined, amount_out_kind "actual", else the plan's minimum, "minimum"), its balances read from the
chain and valued at the independent marks Orblivion's volume tier uses (a token without one is listed with its amount,
priced=False), its fee tier and the volume to the next, and its active sessions and hosted agents. Read-only: these
calls sign nothing and change nothing. The server answers for the key's own wallet only, and the SDK still checks every
answer: its shape (ACCOUNT_SHAPE) and that it is this client's wallet's (ACCOUNT_WALLET). Information only: nothing
in these answers feeds a signing decision, which always rests on the SDK's own reads. No fee figure is returned.
Token symbols and names are their deployer's word: show them as text.
What the SDK verifies
The same rules as the TypeScript SDK, by the same names: the router, chain, sender and transaction type; every Universal Router command decoded and re-encoded byte for byte; the value and what is pulled; the tokens and hops; every recipient (output only to the wallet, one fee payment only); the fee (its class, count, size, token, recipient); the minimum output against the quote, the simulation and Chainlink prices the SDK reads itself, or its own quote of the exact route for tokens without a feed; the size, the deadline and the gas; Permit2 permits and approvals; token records against the chain (lookalikes of core assets refused unless accepted); and, after mining, that what was mined is what was signed.
Pinned values
The chain, the contracts, the core tokens, the fee schedule and the session co-signer (constants.COSIGNER) are pinned;
the server's config must agree (ConfigMismatchError). Its fee must publish schedule 2 exactly: schedule,
major_symbols, major_tiers, other_bps, shield_add_bps and max_bps, and none of schedule 1's bps,
shield_bps or default_bps. The fee recipient is pinned the same way: Orblivion's platform wallet
(constants.FEE_RECIPIENT, 0x46eb64c06379ac0d8ea3db040974278199e1e6e9). The $ORBLIVION token's contract is pinned too
(constants.ORBLIVION_TOKEN).
Errors
VerificationError (VERIFICATION_FAILED, with rule, failures, checks: nothing was signed),
OrblivionApiError (the server's code), ConfigMismatchError, MinedMismatchError, TxRevertedError,
ConsentRequiredError, InsufficientFundsError, and OrblivionError with ACCOUNT_SHAPE or ACCOUNT_WALLET for an
account answer (client.me) of the wrong shape or for another wallet. error.retryable is true for rate limits,
unreachable nodes, receipt timeouts and 5xx answers. Every one is an OrblivionError; an API that can't be reached
is ORBLIVION_UNREACHABLE.
License
MIT. See LICENSE.
Metadata
Release files for orblivion 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| orblivion-0.1.0.tar.gz | 187.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| orblivion-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 386.3 kB
Release files / orblivion-0.1.0.tar.gz
| Download URL | orblivion-0.1.0.tar.gz |
|---|---|
| Size | 187.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
3948cf090d7381dedf7d0d931769da0b37d5466bcedcad8ddabb3b43decc07f8
|
|
BLAKE2b-256 checksum How to use checksums |
2034b7e8dfbca465e250ce42c9d12d277baa121b95241091d8656c4ba651a048
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.9
|
Release files / orblivion-0.1.0-py3-none-any.whl
| Download URL | orblivion-0.1.0-py3-none-any.whl |
|---|---|
| Size | 198.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
60495ba9f79859896977b8fd35070fce572d1864f5f726899a115c3dbedd8298
|
|
BLAKE2b-256 checksum How to use checksums |
2108e1c7782609146fee829d75cccdd8b37ef38b2f684348864cea4b22fcdddc
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.9
|