Metadata-Version: 2.4
Name: kashdao-protocol-sdk
Version: 0.3.0b2
Summary: Official non-custodial Python SDK for the Kash prediction-market protocol on Solana (mainnet-beta and devnet): exact quotes from the program's integer curve, IDL-driven instruction builders, and sign-send-confirm with a mandatory slippage bound. Mirrors @kashdao/protocol-sdk (TypeScript).
Project-URL: Homepage, https://github.com/KashDAO/protocol-sdk-python
Project-URL: Documentation, https://github.com/KashDAO/protocol-sdk-python#readme
Project-URL: Repository, https://github.com/KashDAO/protocol-sdk-python.git
Project-URL: Issues, https://github.com/KashDAO/protocol-sdk-python/issues
Project-URL: Changelog, https://github.com/KashDAO/protocol-sdk-python/blob/main/CHANGELOG.md
Author-email: KashDAO <engineering@kash.bot>
License: MIT License
        
        Copyright (c) 2025-2026 KashDAO
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
License-File: LICENSE
Keywords: amm,defi,kash,kashdao,market-making,prediction-markets,solana,solders
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Financial and Insurance Industry
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Office/Business :: Financial
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.10
Requires-Dist: httpx<1,>=0.27
Requires-Dist: pydantic<3,>=2.7
Requires-Dist: solders<0.29,>=0.28
Provides-Extra: dev
Requires-Dist: cyclonedx-bom>=4.5; extra == 'dev'
Requires-Dist: mypy>=1.13; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
Requires-Dist: pytest-httpx>=0.32; extra == 'dev'
Requires-Dist: pytest>=8.3; extra == 'dev'
Requires-Dist: ruff>=0.7; extra == 'dev'
Provides-Extra: solana
Description-Content-Type: text/markdown

# `kashdao-protocol-sdk` (Python)

Official non-custodial Python SDK for the [Kash](https://kash.bot) prediction-market protocol on **Solana** — for trading bots, market makers, AI agents and integrations written in Python.

[![PyPI version](https://img.shields.io/pypi/v/kashdao-protocol-sdk?include_prereleases)](https://pypi.org/project/kashdao-protocol-sdk/)
[![Python versions](https://img.shields.io/pypi/pyversions/kashdao-protocol-sdk)](https://pypi.org/project/kashdao-protocol-sdk/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE)
[![CI](https://github.com/KashDAO/protocol-sdk-python/actions/workflows/ci.yml/badge.svg)](https://github.com/KashDAO/protocol-sdk-python/actions/workflows/ci.yml)
[![mypy: strict](https://img.shields.io/badge/mypy-strict-blue.svg)](https://mypy.readthedocs.io/)
[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)

> **Status: 0.3.0b2 (beta). Solana-only.** Every Kash market is on
> Solana mainnet-beta, which is the default cluster; devnet is supported.
> 0.3.0 removed the Base (EVM) clients. If you still hold a position on
> Base, redeem it with the last EVM-capable release:
> `pip install 'kashdao-protocol-sdk==0.2.0b2'`. See the
> [CHANGELOG](./CHANGELOG.md) for the upgrade notes.

## Supported clusters

| Cluster               | 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) |

## When to use this package

| You are…                                                                      | Use                                                       |
| ----------------------------------------------------------------------------- | --------------------------------------------------------- |
| A **trader, market maker or agent** who signs its own Solana transactions     | This package — `create_solana_client`                     |
| A **research / data-science user** reading markets and quotes from a notebook | This package — reads and quotes need no signer            |
| A **TypeScript / web developer**                                              | `@kashdao/protocol-sdk` (TS), same surface in `camelCase` |
| Anyone who wants Kash's REST API (no RPC, no transaction building)            | `@kashdao/sdk` (REST)                                     |

## Install

```bash
pip install kashdao-protocol-sdk
```

Requires Python ≥ 3.10. End-user installs touch only the public PyPI
registry — no private repos, no auth tokens. Dependencies:
[`solders`](https://pypi.org/project/solders/) (keys, instructions,
versioned transactions), `httpx` (the RPC transport is plain JSON-RPC)
and `pydantic`.

`pip install 'kashdao-protocol-sdk[solana]'` still works: the `[solana]`
extra is kept, empty, so install lines written for 0.2.x resolve without
a warning. It adds nothing — Solana is the base install.

## Quickstart

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` (TypeScript) with
`snake_case` names.

```python
import asyncio
import os

from solders.keypair import Keypair

from kashdao_protocol_sdk 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 `Instruction`s 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`;
  `0` accepts anything) or `max_slippage_bps` below a fresh quote.
  Neither, or both, is a `KashConfigError` (`INVALID_SLIPPAGE`).
- **Signers.** `keypair_signer(keypair)` for bots and scripts. Anything
  with a `pubkey` and an async `sign_message(message) -> Signature`
  satisfies `SolanaSigner` (a KMS, an HSM, a remote signer). Pass
  `fee_payer=` to `send` to sponsor fees from a second signer.
- **Amounts.** USDC is an atomic `int` (6 decimals). Outcome balances
  are a WAD `int` (18 decimals) — the program stores each position as a
  `u128`, so 18 decimals is the native scale on Solana too.
- **Sell fee.** Each market charges its own `sell_fee_bps` (on
  `SolanaMarket`), and quotes and slippage floors use it.
- **First buy.** The program requires a position account before `buy`;
  `build_buy` opens it in the same transaction when the receiver has none.
- **Refused before signing.** `build_buy`/`build_sell` raise
  `KashValidationError` (with the program's error name on
  `program_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** — see [Error handling](#error-handling). 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 with `BlockhashNotFound`) —
    neither landed. A preflight reporting `AlreadyProcessed` means 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.
- **Other deployments.** `create_solana_client(cluster="devnet", rpc_url=...)`,
  or `cluster=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` is not exposed by this SDK.

Runnable examples: [`examples/solana/`](./examples/solana).

## Error handling

Every error inherits from `KashProtocolError` and carries a stable
`code` (`ErrorCode.*`), structured `context`, plus `is_retryable` /
`is_operational` flags. Mirrors the TypeScript `@kashdao/protocol-sdk`
hierarchy — same class names, same string-valued codes.

| Class                                | Raised when                                                                                          |
| ------------------------------------ | ---------------------------------------------------------------------------------------------------- |
| `KashConfigError`                    | Bad configuration or arguments (`INVALID_CONFIG`, `UNSUPPORTED_CHAIN`, `INVALID_SLIPPAGE`). Fix code |
| `KashValidationError`                | A value the program would refuse; `program_error` names the program's own error when it applies      |
| `KashChainError`                     | An RPC read failed, or the transaction landed and failed (`TX_REVERTED`). May be retryable           |
| `KashTransactionExpiredError`        | `TX_EXPIRED`: provably did not land — rebuild with a fresh quote and resend (a `KashChainError`)     |
| `KashTransactionOutcomeUnknownError` | `WAIT_RECEIPT_FAILED`: **may have landed** — look up `.signature` first (a `KashChainError`)         |
| `KashSimulationRevertedError`        | The RPC's preflight refused the transaction; nothing was sent                                        |
| `KashSignerError`                    | The signer raised. Never retryable                                                                   |

```python
from kashdao_protocol_sdk import (
    KashChainError,
    KashConfigError,
    KashProtocolError,
    KashSignerError,
    KashSimulationRevertedError,
    KashTransactionExpiredError,
    KashTransactionOutcomeUnknownError,
    KashValidationError,
)

try:
    result = await kash.trades.buy(
        market=12, outcome=0, amount_usdc=5_000_000, max_slippage_bps=100, signer=signer
    )
except KashTransactionOutcomeUnknownError as e:
    # The transaction MAY have landed. Never resend blindly: look it up first.
    print(f"check {e.signature} before doing anything else")
except KashTransactionExpiredError:
    ...  # did not land: rebuild with a fresh quote and resend
except (KashValidationError, KashSimulationRevertedError) as e:
    print(f"refused, nothing sent: {e}")
except KashSignerError:
    raise  # never retry a signer failure
except KashChainError as e:
    if e.is_retryable:
        ...  # back off and retry
except KashConfigError:
    raise  # misconfiguration — fix the code
```

`KashProtocolError.is_(value)` is a cross-version identity check (a
brand attribute), for errors that crossed a boundary where `isinstance`
cannot be trusted.

## 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`](https://www.npmjs.com/package/@kashdao/sdk).
- **Hold or generate keys.** Bring your own signer (a solders `Keypair`,
  a KMS, an HSM, a remote signer).
- **Auto-retry a send.** A send whose outcome is unknown is never
  retried: resending can double a trade. Reads raise retryable errors and
  leave the retry policy to you.
- **Telemetry.** Zero phone-home. The SDK never reaches Kash
  infrastructure; it talks only to the RPC you give it.

## Public surface

Everything is importable from `kashdao_protocol_sdk`. The implementation
lives in `kashdao_protocol_sdk.solana`, which exports the same names (the
0.2.x import path, so `from kashdao_protocol_sdk.solana import …` keeps
working). Groups:

- **Client**: `create_solana_client` → `SolanaClient` with `markets`,
  `account`, `protocol` and `trades`
- **Clusters**: `SOLANA_CLUSTERS`, `DEFAULT_SOLANA_CLUSTER`,
  `SOLANA_PROGRAM_IDENTITIES`, `get_solana_deployment`,
  `CustomSolanaDeployment`
- **Connection**: `SolanaConnection` (Protocol), `SolanaRpcConnection`
  (the httpx JSON-RPC implementation)
- **Signers**: `SolanaSigner` (Protocol), `keypair_signer` / `KeypairSigner`
- **Instruction builders** (no checks applied): `buy_instruction`,
  `sell_instruction`, `redeem_instruction`, `open_position_instruction`,
  `close_position_instruction`, `ensure_usdc_ata_instruction`; PDAs via
  `kash_market_pdas`
- **Transactions**: `SolanaComputeBudget`, `SolanaSendResult`,
  `SolanaSimulationResult`, `decode_program_error`
- **Errors**: `ErrorCode` and the classes in
  [Error handling](#error-handling)

## Architectural boundary

This is a **non-custodial** library. Consumers bring their own RPC URL
and their own signer. Kash never sees a private key, never relays a
transaction, never pays fees, and is never on the trade path. The
library reads accounts, builds instructions and the unsigned
transaction, and forwards the signed transaction to the consumer's RPC.

## 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`).

The `kash_market` IDL under `kashdao_protocol_sdk/solana/_vendor/` is
vendored at release time and shipped in the wheel; runtime loading goes
through `importlib.resources`, so consumers need no post-install fetch
step.

See [`RELEASING.md`](./RELEASING.md) for the full release runbook,
[`CONTRIBUTING.md`](./CONTRIBUTING.md) for the contributor workflow,
and [`SECURITY.md`](./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.
