This release is a pre-release and may not be stable for production use.
kashdao-protocol-sdk (Python)
Official Python SDK for the Kash prediction-market protocol — the canonical Hummingbot integration path and a first-class option for any Python trading bot, AI agent, or partner integration.
🚀 Solana mainnet and Base. New markets launch on Solana mainnet-beta — use
kashdao_protocol_sdk.solana(install the[solana]extra). Base mainnet (8453) and Base Sepolia (84532) stay fully supported for markets that live there until they resolve. See Supported chains.
Status: 0.2.0b2 (beta). Three clients ship complete:
- Solana —
create_solana_client. The Kash market program on mainnet-beta (default) or devnet; bring your own RPC + keypair.- Base EOA mode (vanilla EIP-1559) —
create_eoa_client. The canonical Hummingbot integration path; bring your own RPC + signer.- Base Smart Account mode (ERC-4337 v0.7) —
create_smart_account_client. SimpleAccount + bundler; bring your own RPC + signer + bundler URL.Cross-language parity with
@kashdao/protocol-sdk(TypeScript) is validated bytests/parity/— the same JSON fixtures drive both SDKs, so EIP-1559 serialization and UserOp v0.7 hashes are byte-equal by construction. A position opened via either SDK can be closed via the other.
Supported chains
| Chain | Identifier | Status |
|---|---|---|
| Solana mainnet-beta | mainnet-beta |
✅ Live — the default for create_solana_client |
| Solana devnet | devnet |
✅ Live |
| Custom Solana cluster | any | ✅ Via cluster=CustomSolanaDeployment(program_id=..., usdc_mint=...) (local validator) |
| Base mainnet | 8453 | ✅ Live |
| Base Sepolia | 84532 | ✅ Live |
| Custom EVM chain | any | ✅ Via custom_chain=CustomChain(...) (Anvil / forks / dev) |
When to use this package
| You are… | Mode | Notes |
|---|---|---|
| A trader, market maker or agent on Solana (where new markets launch) | Solana | create_solana_client — see Solana |
A Hummingbot strategy author with eth_account already in use |
EOA | Canonical path — see HUMMINGBOT_INTEGRATION.md |
| A Python market maker with existing tx-signing infra (HSM, Fireblocks, web3signer, AWS-KMS) | EOA | Plug your signer in via the EoaSignerAdapter Protocol |
| An AI agent runner in Python with a bundler relationship | SA | Adds gasless onboarding via paymaster + batched approve+trade |
| A research / data-science user evaluating the protocol from a Jupyter notebook | EOA | One create_eoa_client(...) call away from quotes + reads |
| A TypeScript / web developer building a retail integration | — | Use @kashdao/sdk (REST) or @kashdao/protocol-sdk (TS) instead |
Install
pip install 'kashdao-protocol-sdk[solana]' # Solana + Base
pip install kashdao-protocol-sdk # Base only — gains no Solana dependency
Requires Python ≥ 3.10. End-user installs touch only the public PyPI
registry — no private repos, no auth tokens. The [solana] extra adds
solders only; the Solana RPC
transport is plain JSON-RPC over httpx, which the base install
already carries. Importing kashdao_protocol_sdk never loads the
Solana half.
Solana — kashdao_protocol_sdk.solana
The Kash market program on Solana, non-custodially: bring an RPC URL (or
any SolanaConnection) and a signer. Mainnet-beta is the default
cluster — program Jr8Bd8efPfNYHW65vZrVzeLbYkzy1oo3QB3i8cYdDcy, USDC
EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v — and devnet is
supported. Mirrors @kashdao/protocol-sdk/solana (TypeScript) with
snake_case names.
import asyncio
import os
from solders.keypair import Keypair
from kashdao_protocol_sdk.solana import (
KashSimulationRevertedError,
KashValidationError,
create_solana_client,
keypair_signer,
)
async def main() -> None:
signer = keypair_signer(Keypair.from_base58_string(os.environ["SOLANA_SECRET_KEY"]))
async with create_solana_client(rpc_url=os.environ["SOLANA_RPC_URL"]) as kash:
# Read a market (by numeric id or by its account address).
market = await kash.markets.get(12)
print(market.status, market.probabilities_wad)
# Exact quote — the program's own integer curve.
quote = await kash.markets.quote_buy(market=12, outcome=0, amount_usdc=5_000_000)
print(quote.tokens_out_wad)
# Build, sign, send and confirm in one call. A slippage bound is mandatory.
try:
result = await kash.trades.buy(
market=12,
outcome=0,
amount_usdc=5_000_000, # 5 USDC, atomic (6dp)
max_slippage_bps=100,
signer=signer,
)
print(result.signature, result.plan.min_tokens_out_wad)
except KashValidationError as e:
print(f"refused before signing: {e} ({e.program_error})")
except KashSimulationRevertedError as e:
print(f"preflight refused: {e.context}")
asyncio.run(main())
| Method | What it does |
|---|---|
markets.get(ref) |
Decoded market + collateral, status (active/frozen/resolved/cancelled), prices, probabilities |
markets.quote_buy / quote_sell / quote_redeem |
Exact quotes; refusals carry the program's error name on KashValidationError.program_error |
account.position / positions / usdc_balance |
Positions are u128 WAD balances in a program account — not SPL tokens |
protocol.config / template / templates |
Program config (protocol fee, pause) and creation templates |
trades.build_buy / build_sell / build_redeem / build_redeem_cancelled / build_open_position / build_close_position |
A plan: solders Instructions plus the bounds it encodes |
trades.simulate(plan, payer=...) / trades.send(plan, signer=...) |
Simulate unsigned, or sign → send → confirm |
trades.buy / sell / redeem / redeem_cancelled / open_position / close_position |
Build + send in one call, the signer as trader |
- Slippage is mandatory. Every buy, sell and redeem takes exactly one
of an explicit floor (
min_tokens_out_wad/min_amount_out_usdc;0accepts anything) ormax_slippage_bpsbelow a fresh quote — the same option name the Base clients use. Neither, or both, is aKashConfigError(INVALID_SLIPPAGE). - Signers.
keypair_signer(keypair)for bots and scripts. Anything with apubkeyand an asyncsign_message(message) -> SignaturesatisfiesSolanaSigner(a KMS, an HSM, a remote signer). Passfee_payer=tosendto sponsor fees from a second signer. - Amounts. USDC is an atomic
int(6 decimals). Outcome balances are a WADint(18 decimals) — the program stores each position as au128, so 18 decimals is the native scale on Solana too. - Sell fee. Each market charges its own
sell_fee_bps(onSolanaMarket), and quotes and slippage floors use it. - First buy. The program requires a position account before
buy;build_buyopens it in the same transaction when the receiver has none. - Refused before signing.
build_buy/build_sellraiseKashValidationError(with the program's error name onprogram_error) when the program is paused (Paused), the market is not active (InvalidState) or frozen, or — for buys only — the market is halted (HaltedEntries; a holder can still sell). A market whose collateral account is missing or not the cluster's USDC is an error, never a zero balance. The instruction builders (buy_instruction, …) apply none of these checks. - Closing a position refunds its rent to whoever funded it:
plan.rent_recipient, which can differ from the owner who signs. - Errors are the same hierarchy as the Base clients, and every error
raised after signing carries
context["signature"], computed before the transaction is sent. Sending distinguishes:- a preflight revert (
KashSimulationRevertedError) and a send the RPC refused BEFORE forwarding it (TX_SEND_FAILED, retryable: JSON-RPC -32003, -32602, or a -32002 preflight withBlockhashNotFound) — neither landed. A preflight reportingAlreadyProcessedmeans the transaction is already on chain; the SDK goes on to confirm it; KashTransactionExpiredError(TX_EXPIRED, retryable): the blockhash expired and a status read from a node at or after the slot where the expiry was observed found no trace of the signature, so it did not land — rebuild with a fresh quote and resend;TX_REVERTED: it landed and failed on chain;KashTransactionOutcomeUnknownError(WAIT_RECEIPT_FAILED, not retryable, carries.signature): any other outcome — a timeout, a reset, any HTTP error status (429 included), an unreadable body, any other JSON-RPC code (-32603, -32005, vendor codes), or an unreadable confirmation. The transaction may have landed: look the signature up before doing anything else; rebuilding takes a fresh blockhash, which makes a new transaction, and sending both can double the trade.
- a preflight revert (
- Other deployments.
create_solana_client(cluster="devnet", rpc_url=...), orcluster=CustomSolanaDeployment(program_id=..., usdc_mint=...)for a local validator. Instructions always name the cluster's program id, never the IDL's embedded canonical id. kash_groups(competitions) is not exposed: it is paused on mainnet.
Runnable examples: examples/solana/.
Base (EVM)
Base markets keep trading until they resolve. The two Base modes below are unchanged by the Solana release.
Quickstart (EOA mode — canonical Hummingbot path)
import asyncio
import os
from eth_account import Account
from kashdao_protocol_sdk import (
BuildApproveParams,
BuildBuyParams,
MAX_UINT256,
create_eoa_client,
usdc,
viem_account_eoa_signer,
)
MARKET = "0x..." # the Market contract address you're trading
async def main() -> None:
account = Account.from_key(os.environ["KASH_PRIVATE_KEY"])
signer = viem_account_eoa_signer(account)
client = create_eoa_client(
chain_id=84532, # Base Sepolia
rpc=os.environ["BASE_SEPOLIA_RPC"],
signer=signer,
)
# First-time setup: approve the Market contract to spend USDC.
await client.trades.send.approve(
BuildApproveParams(
account=account.address,
spender=MARKET,
amount=MAX_UINT256,
),
)
# Per trade tick:
result = await client.trades.send.buy(
MARKET,
BuildBuyParams(
smart_account=account.address, # in EOA mode, this is the EOA itself
outcome=0,
amount_usdc=usdc(10), # 10 USDC
max_slippage_bps=50, # 0.5%
),
)
print(result.transaction_hash, result.success, result.gas_used)
asyncio.run(main())
That's it. No bundler URL, no SimpleAccount provisioning, no UserOp. Just an EOA, an RPC URL, and EIP-1559 transactions.
For Hummingbot users: see
HUMMINGBOT_INTEGRATION.md for the full
strategy-author guide.
Quickstart — smart-account mode
For consumers on AA stacks (Privy, Coinbase Smart Wallet, Pimlico, Alchemy AA), or who want gasless onboarding via paymaster:
import asyncio
import os
from eth_account import Account
from kashdao_protocol_sdk import (
BuildBuyParams,
BundlerOptions,
create_smart_account_client,
usdc,
viem_account_signer,
)
async def main() -> None:
account = Account.from_key(os.environ["KASH_PRIVATE_KEY"])
async with create_smart_account_client(
chain_id=84532,
rpc=os.environ["BASE_SEPOLIA_RPC"],
signer=viem_account_signer(account),
bundler=BundlerOptions(
provider="pimlico",
url=os.environ["KASH_BUNDLER_URL"],
),
) as client:
sa = await client.account.address() # deterministic, no deploy needed
result = await client.trades.send.buy(
os.environ["KASH_MARKET"],
BuildBuyParams(
smart_account=sa,
outcome=0,
amount_usdc=usdc(10),
max_slippage_bps=50,
),
)
print(result.user_op_hash, result.transaction_hash, result.success)
asyncio.run(main())
The full set of runnable examples (EOA, SA, Hummingbot, local Anvil)
lives in examples/.
Power users — explicit trade lifecycle
client.trades.send.{buy,sell,close_position,approve} is the all-in-one
ergonomic path: it builds, prepares (gas + fees + nonce), simulates,
signs, submits, and (by default) waits for inclusion. For full control
the lifecycle is exposed in three layers — pick the highest one that
fits:
| Layer | When you'd use it |
|---|---|
client.trades.send.<action>(...) |
Default. Hummingbot, AI agents, dashboards. |
client.trades.prepare_<action>(...) then submit |
You want explicit control of the signing step — e.g. log + audit before signing. |
client.trades.build_<action>(...) + hash_of(...) |
You want to construct the unsigned tx, hash it, route to a remote signer yourself. |
client.trades.hash_of(transaction) and the SA equivalent
client.trades.hash_of(user_op) are the canonical hash that the
chain's signature-recovery validates against — call this AFTER
populating gas + fees and BEFORE signing to avoid stale-hash footguns.
Submit-time STALE_SIGNED_TX / STALE_USEROP_HASH checks are the
backstop.
Real-time market events
async with client.markets.watch(market_address) as sub:
async for event in sub:
print(event.event_type, event.market, event.data)
WebSocket if your RPC URL starts with wss://, otherwise polling. Both
yield the same event shape. Events are bounded queues; a slow consumer
won't OOM the SDK — the queue's high-water mark is documented in the
watch config.
Error handling
Every error inherits from KashProtocolError and carries a stable
code, structured context, plus is_retryable / is_operational
flags. Mirrors the TypeScript @kashdao/protocol-sdk hierarchy
exactly — same class names, same string-valued ErrorCode constants.
from kashdao_protocol_sdk import (
KashChainError,
KashConfigError,
KashSignerError,
KashSimulationRevertedError,
KashAbortedError,
)
try:
await client.trades.send.buy(market, params)
except KashSimulationRevertedError as e:
# Pre-flight `eth_call` caught a revert before any signing happened.
# `revert_hint` decodes the on-chain custom error name when known.
print(f"would revert: {e.revert_hint or e}")
except KashSignerError as e:
# Signer-side failure. ALWAYS non-retryable — re-prompting MFA on
# retry is a footgun.
print(f"signer failed: {e}")
except KashChainError as e:
# Chain/RPC failure. May be retryable.
if e.is_retryable:
... # back off and retry
except KashConfigError as e:
# Misconfig. Never retryable. Fix the code.
raise
except KashAbortedError:
# The caller's `signal` fired. Deliberate; don't auto-retry.
raise
A full worked example lives in
examples/eoa/03_error_handling.py.
What this SDK deliberately does NOT do
- Kash-orchestrated trade routing. No Kash backend on the trade
path. If you want a REST surface that wraps the Kash public API, use
@kashdao/sdk— that path is also non-custodial (Kash never holds funds, never moves funds, never holds keys, never signs anything; the user's Privy-managed smart account is the signer). - Sponsor or bundle UserOps. Bring your own bundler URL.
- Hold or generate keys. Bring your own signer (eth_account, KMS, Fireblocks, hardware wallet).
- Background market scanners.
markets.watchonly fires while a consumer is iterating; idle subscriptions are torn down viaaclose. - Auto-retries on submit. Submit is single-attempt by design.
Reads (
prepare_*,simulate) are retryable viaRetryOptions. - Telemetry. Zero phone-home. The SDK never reaches Kash infrastructure.
Public surface
The SDK mirrors @kashdao/protocol-sdk (TypeScript) with snake_case
names. ~210 public exports across:
- Factories:
create_eoa_client,create_smart_account_client;create_solana_clientinkashdao_protocol_sdk.solana(the[solana]extra), with its ownmarkets/account/protocol/tradessurface, PDA derivations (kash_market_pdas) and IDL-driven instruction builders - Signers:
LocalEoaSigner,JsonRpcEoaSigner(EOA);LocalSigner,JsonRpcSigner(SA) - Bundlers (SA):
create_generic_bundler_clientplus presets forcreate_alchemy_bundler_client,create_pimlico_bundler_client,create_flashbots_bundler_client - Trade lifecycle:
client.trades.{build_*, prepare_*, simulate, hash_of, submit, send.*} - Market reads:
client.markets.{get, state, quote, watch} - Account reads:
client.account.{usdc_balance, position, gas_balance, usdc_allowance} - Allowance helpers:
encode_approve,MAX_UINT256,get_usdc_allowance - Unit conversion:
usdc,tokens,format_usdc,format_tokens - Fee estimation:
estimate_chain_feeswithEstimateFeesOptions(eth_feeHistory windowing, percentile, base multiplier, priority floor) - Custom-chain support:
CustomChain,resolve_custom_chainfor Anvil / Hardhat / Tenderly forks - Error hierarchy:
KashProtocolErrorand 9 subclasses (KashConfigError,KashChainError,KashBundlerError,KashSignerError,KashSimulationRevertedError,KashAbortedError,KashValidationError, and the two Solana send outcomes,KashTransactionOutcomeUnknownErrorandKashTransactionExpiredError, bothKashChainErrors); cross-class identity viaKashProtocolError.is_(value) - Lifecycle hooks:
KashProtocolHooksProtocol with 5 fire-and-forget callbacks for telemetry / structured logging - Cancellation: every async method takes a
signalparameter;throw_if_abortedandto_kash_abortedhelpers wrapasyncio.CancelledErrorintoKashAbortedError
Architectural boundary
This is a non-custodial library. Consumers bring:
- their own RPC URL,
- their own private key OR signer abstraction (
eth_account.Account, web3signer over RPC, hardware wallet, Fireblocks, AWS-KMS — anything exposing theEoaSignerAdapterProtocol), - (SA mode only) ERC-4337 bundler URL + smart-account address.
Kash never sees a private key, never relays a transaction or UserOp, never sponsors gas, and is never on the trade path. The library's job is to construct calldata + the unsigned tx, then forward signed artifacts to the consumer's RPC.
Why dual modes?
- EOA mode — vanilla EIP-1559 from a plain EOA. Best for market makers, Hummingbot strategies, AI agents, any consumer with their own EIP-1559 signing infrastructure (web3signer, Fireblocks, AWS-KMS, eth-account local key, hardware wallets).
- Smart Account mode — ERC-4337 v0.7 via SimpleAccount + bundler. Best for users on AA stacks (Privy embedded wallets, Coinbase Smart Wallet, Pimlico, Alchemy AA), or flows that benefit from gasless onboarding via paymaster, batched approve+trade, or session keys.
Both modes share the same markets / account-read surface and the same
discriminated TradingClient union.
Source repository
This repository is the canonical public source. PyPI releases are
published from tags here via OIDC trusted publishing
(.github/workflows/publish-pypi.yml).
ABIs under kashdao_protocol_sdk/shared/contracts/generated/ are
vendored at release time and shipped in the wheel; runtime loading
goes through importlib.resources, so consumers don't need any
post-install fetch step.
See RELEASING.md for the full release runbook,
CONTRIBUTING.md for the contributor workflow,
and SECURITY.md for the vulnerability-disclosure
policy.
License
MIT. The artifact published to PyPI is MIT-licensed; the development repo is private but the published library carries no usage restrictions.
Metadata
Release files for kashdao-protocol-sdk 0.2.0b2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| kashdao_protocol_sdk-0.2.0b2.tar.gz | 329.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| kashdao_protocol_sdk-0.2.0b2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 550.3 kB
Release files / kashdao_protocol_sdk-0.2.0b2.tar.gz
| Download URL | kashdao_protocol_sdk-0.2.0b2.tar.gz |
|---|---|
| Size | 329.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
b6d633edebf786f3fb0a4e6e17fd845885dc1630f4bc8f3613e968416a7948ed
|
|
BLAKE2b-256 checksum How to use checksums |
4e25da048508db80a7ae1eaf870e286afbcbee7a773079ad4a50cc1c312ca3ce
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 2, 2026.
Transparency logRelease files / kashdao_protocol_sdk-0.2.0b2-py3-none-any.whl
| Download URL | kashdao_protocol_sdk-0.2.0b2-py3-none-any.whl |
|---|---|
| Size | 220.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
046968c879793172bb238a3169b138aac64fb52510530aede114cbce86184289
|
|
BLAKE2b-256 checksum How to use checksums |
353b38b1d9741d6161bbd3e57e1d02b7e078486598865288d9109f8432ca37e1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 2, 2026.
Transparency log