npx skillsauth add starchild-ai-agent/community-skills @4657/deriveInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Derive is a self-custodial onchain options & perpetuals DEX built on the OP Stack (Derive Chain / Lyra Finance). It supports options, perpetuals, and spot across BTC, ETH, SOL, and more.
| | REST | WebSocket |
|---|---|---|
| Mainnet | https://api.lyra.finance | wss://api.lyra.finance/ws |
| Testnet | https://api-demo.lyra.finance | wss://api-demo.lyra.finance/ws |
All endpoints speak JSON-RPC 2.0. Public endpoints need no auth. Private endpoints require a signed login.
The Lyra/Derive API blocks Region US. Starchild containers exit the US by default, so every request must be routed through sc-vpn (Malaysia or other non-US region).
PROXY_R = {"http": "http://my:[email protected]:8080", "https": "http://my:[email protected]:8080"}
PROXY_W = dict(http_proxy_host="sc-vpn.internal", http_proxy_port=8080,
http_proxy_auth=("my","x"), proxy_type="http")
requests.post(URL, json=..., proxies=PROXY_R) # REST
websocket.create_connection(WS_URL, **PROXY_W) # WebSocket
Without the proxy you get code 403 "You are in a restricted region that violates our terms of service."
⚠️ WARNING: The official derive_action_signing example ships with TESTNET constants. Using testnet values on mainnet returns code 14014 "Signature does not match data" with no other hint.
0xeB8d770ec18DB98Db922E9D83260A585b9F0DeAD)DOMAIN_SEPARATOR = "0xd96e5f90797da7ec8dc4e276260c7f3f87fedf68775fbe1ef116e996fc60441b"
ACTION_TYPEHASH = "0x4d7a9f27c403ff9c0f19bce61d76d82f9aa29f8d6d4b0c5474607d9770d1af17"
TRADE_MODULE_ADDRESS = "0xB8D20c2B7a1Ad2EE33Bc50eF10876eD3035b5e7b"
MATCHING_ADDRESS = "0xeB8d770ec18DB98Db922E9D83260A585b9F0DeAD"
DEPOSIT_MODULE = "0x9B3FE5E5a3bcEa5df4E08c41Ce89C4e3Ff01Ace3"
WITHDRAW_MODULE = "" # query via explorer when needed
DOMAIN_SEPARATOR = "0x9bcf4dc06df5d8bf23af818d5716491b995020f377d3b7b64c29ed14e3dd1105"
ACTION_TYPEHASH = "0x4d7a9f27c403ff9c0f19bce61d76d82f9aa29f8d6d4b0c5474607d9770d1af17"
TRADE_MODULE_ADDRESS = "0x87F2863866D85E3192a35A73b388BD625D83f2be"
import requests
# Find by name on the Lyra explorer
r = requests.get("https://explorer.lyra.finance/api/v2/search?q=TradeModule")
# Pull DOMAIN_SEPARATOR via JSON-RPC eth_call (selector for `domainSeparator()` = 0xf698da25)
import requests
payload = {"jsonrpc":"2.0","method":"eth_call","id":1,
"params":[{"to":"0xeB8d770ec18DB98Db922E9D83260A585b9F0DeAD","data":"0xf698da25"},"latest"]}
r = requests.post("https://rpc.lyra.finance", json=payload)
print(r.json()["result"]) # the canonical mainnet DOMAIN_SEPARATOR
public/loginfrom derive_action_signing import utils
from web3 import Web3
web3 = Web3()
login_params = utils.sign_ws_login(web3, DERIVE_WALLET, SESSION_KEY_PRIVATE_KEY)
# sends: { timestamp, wallet, signature }
Derive options often have very wide bid/ask spreads (30%+ of mark) because the book is mostly market-maker quotes, not organic flow. Crossing the spread (taking the ask on a buy) eats the full MM markup PLUS the taker fee.
Rule of thumb for any order with premium > $5:
(bid + ask) / 2 or near mark). Pays maker fee (0.0001) instead of taker (0.0003). Won't fill instantly but saves real money on illiquid strikes.Example post-only order:
payload = {
"instrument_name": "ETH-20260521-2000-P",
"direction": "buy",
"order_type": "limit",
"mmp": False,
"time_in_force": "gtc",
"is_atomic_signing": False,
# Note: post-only is controlled via order params, check Derive docs for current field name
**action.to_json(),
}
Options: {CURRENCY}-{YYYYMMDD}-{STRIKE}-{C|P} e.g. ETH-20250131-3000-C
Perps: {CURRENCY}-PERP e.g. BTC-PERP
Spot/ERC: {SYMBOL} e.g. ETH
import requests, json
BASE = "https://api.lyra.finance"
# All live options for ETH
r = requests.post(f"{BASE}/public/get_instruments",
json={"expired": False, "instrument_type": "option", "currency": "ETH"},
headers={"accept": "application/json", "content-type": "application/json"})
# Ticker with Greeks
r = requests.post(f"{BASE}/public/get_ticker",
json={"instrument_name": "ETH-PERP"},
headers={"accept": "application/json", "content-type": "application/json"})
# Order book
r = requests.post(f"{BASE}/public/get_orderbook",
json={"instrument_name": "ETH-20250131-3000-C", "depth": 10},
headers={"accept": "application/json", "content-type": "application/json"})
# All tickers for a currency
r = requests.post(f"{BASE}/public/get_tickers",
json={"currency": "ETH", "instrument_type": "option"},
headers={"accept": "application/json", "content-type": "application/json"})
After public/login over WebSocket:
# Positions
{"method": "private/get_positions", "params": {"subaccount_id": <id>, "currency": "ETH"}}
# Collaterals
{"method": "private/get_collaterals", "params": {"subaccount_id": <id>}}
# Open orders
{"method": "private/get_open_orders", "params": {"subaccount_id": <id>}}
# Order history
{"method": "private/get_order_history", "params": {"subaccount_id": <id>}}
# My trades
{"method": "private/get_trades", "params": {"subaccount_id": <id>}}
# Subaccounts
{"method": "private/get_subaccounts", "params": {"wallet": DERIVE_WALLET}}
Requires: derive_action_signing, web3, websocket-client (pip install derive_action_signing web3 websocket-client)
import json, requests
from decimal import Decimal
from websocket import create_connection
from web3 import Web3
from derive_action_signing import SignedAction, TradeModuleData, utils
# --- Config ---
DERIVE_WALLET = "0xYOUR_SMART_CONTRACT_WALLET"
SESSION_KEY_PRIV = "0xYOUR_SESSION_KEY_PRIVATE_KEY"
SUBACCOUNT_ID = 12345
# --- Protocol constants (MAINNET — verified on-chain) ---
DOMAIN_SEPARATOR = "0xd96e5f90797da7ec8dc4e276260c7f3f87fedf68775fbe1ef116e996fc60441b"
ACTION_TYPEHASH = "0x4d7a9f27c403ff9c0f19bce61d76d82f9aa29f8d6d4b0c5474607d9770d1af17"
TRADE_MODULE_ADDRESS = "0xB8D20c2B7a1Ad2EE33Bc50eF10876eD3035b5e7b"
WS_URL = "wss://api.lyra.finance/ws"
REST = "https://api.lyra.finance"
web3 = Web3()
session_wallet = web3.eth.account.from_key(SESSION_KEY_PRIV)
# 1. Get instrument details
resp = requests.post(f"{REST}/public/get_instruments",
json={"expired": False, "instrument_type": "option", "currency": "ETH"},
headers={"content-type": "application/json"})
instrument = resp.json()["result"][0] # pick the first live option
# 2. Build + sign action
action = SignedAction(
subaccount_id=SUBACCOUNT_ID,
owner=DERIVE_WALLET,
signer=session_wallet.address,
signature_expiry_sec=utils.MAX_INT_32,
nonce=utils.get_action_nonce(),
module_address=TRADE_MODULE_ADDRESS,
module_data=TradeModuleData(
asset_address=instrument["base_asset_address"],
sub_id=int(instrument["base_asset_sub_id"]),
limit_price=Decimal("100"), # USD price for option
amount=Decimal("1"), # contracts
max_fee=Decimal("1000"),
recipient_id=SUBACCOUNT_ID,
is_bid=True, # True=buy, False=sell
),
DOMAIN_SEPARATOR=DOMAIN_SEPARATOR,
ACTION_TYPEHASH=ACTION_TYPEHASH,
)
action.sign(session_wallet.key)
# 3. Connect, login, submit
ws = create_connection(WS_URL)
def ws_rpc(ws, method, params, id_str):
ws.send(json.dumps({"method": method, "params": params, "id": id_str}))
while True:
msg = json.loads(ws.recv())
if msg["id"] == id_str:
return msg
login_resp = ws_rpc(ws, "public/login",
utils.sign_ws_login(web3, DERIVE_WALLET, SESSION_KEY_PRIV),
"login")
assert "result" in login_resp, f"Login failed: {login_resp}"
order_resp = ws_rpc(ws, "private/order", {
"instrument_name": instrument["instrument_name"],
"direction": "buy",
"order_type": "limit",
"mmp": False,
"time_in_force": "gtc",
**action.to_json(),
}, "order1")
print(json.dumps(order_resp["result"]["order"], indent=2))
ws.close()
DERIVE_WALLET=0x... # Smart contract wallet on Derive Chain (NOT your EOA)
DERIVE_SESSION_KEY=0x... # Private key of your session key EOA
DERIVE_SUBACCOUNT_ID=12345 # Integer subaccount ID
To find your wallet: derive.xyz → Home → Developers → "Derive Wallet" To create a session key: derive.xyz → Settings → Session Keys → Create
import os
from dotenv import load_dotenv
load_dotenv("/data/workspace/.env")
DERIVE_WALLET = os.environ["DERIVE_WALLET"]
SESSION_KEY_PRIV = os.environ["DERIVE_SESSION_KEY"]
SUBACCOUNT_ID = int(os.environ["DERIVE_SUBACCOUNT_ID"])
BTC, ETH, SOL, HYPE, and others. Use public/get_currencies to get the live list.
The get_ticker and get_tickers responses include for options:
delta, gamma, vega, theta, rho — standard BSM Greeksiv — implied volatilitymark_price, index_price, best_bid_price, best_ask_pricePer account TPS limits — check with private/get_account. WebSocket is preferred for trading; REST for reading.
Use https://api-demo.lyra.finance (REST) and wss://api-demo.lyra.finance/ws (WS) for testing. The testnet has different protocol constants — look them up at docs.derive.xyz/reference/protocol-constants.
Working scripts go in skills/derive/scripts/. Share output in output/derive/.
development
OpenSea API integration for NFT and token discovery, marketplace intelligence, and order/transaction workflows. Use when working with OpenSea data or trading flows (e.g. collection stats, trending collections/tokens, NFT metadata, listings/offers, swap quotes, transaction receipt polling).
development
Generate a warm, healing parallel-universe fairy tale (~1000 words) plus 3 cohesive storybook illustrations, themed "if this person had never been born, what would the world miss." Output is a polished HTML storybook that can be previewed and published. Use when the user wants a personalized "if I had never been born" / "如果我没出生" tale for a real person — input is a name, age, and 3 key life events. Great for birthdays, memorials, encouragement gifts, or healing keepsakes.
development
Onboard a user to Phala Cloud and deploy a verifiable Starchild TEE agent — a minimal FastAPI runtime running inside an Intel TDX confidential VM, plus a published chat dashboard with attestation verification. Use when the user wants to "try TEE", "run an agent in a confidential VM", "deploy to Phala", or replicate the internal Starchild TEE test setup.
tools
Trade on Polymarket prediction markets (CLOB V2) from a Privy EOA wallet. Search markets, place/cancel orders, manage positions. No private key handling. Use when the user wants to bet on event outcomes (e.g. "buy YES at 0.65 on the ceasefire market", "what are my open positions", "close my Trump bet").