Metadata-Version: 2.4
Name: arete-sdk
Version: 0.17.0
Summary: Python SDK for Arete
Author: Arete Team
License-Expression: MIT
Project-URL: Homepage, https://arete.run
Project-URL: Repository, https://github.com/AreteA4/arete
Project-URL: Documentation, https://docs.arete.run
Project-URL: Issues, https://github.com/AreteA4/arete/issues
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: websockets>=12.0
Requires-Dist: httpx>=0.27
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
Provides-Extra: solana
Requires-Dist: solders<0.30,>=0.29; extra == "solana"
Dynamic: license-file

# arete-sdk

> **Work in Progress:** This SDK is under active development and has not yet been published to PyPI.

Python SDK for Arete — real-time Solana program data streaming. The Python SDK is an
idiomatic projection of the same core API exposed by `@usearete/sdk` (TypeScript),
`@usearete/react`, and the `arete-sdk` Rust crate: same nouns, same semantics, native
Python idiom. See `docs/internal/sdk-core-api.md` for the canonical surface.

## Installation

```bash
# Not yet published - install from source for development
pip install -e .
```

Requires Python 3.9+. Runtime dependencies: `websockets`, `httpx`.

## Quick start

```python
import arete
from my_generated_stack import ORE_STREAM_STACK  # generated by `a4 sdk create --python`

async def main():
    async with await arete.Arete.connect(
        ORE_STREAM_STACK,
        auth=arete.AuthConfig(publishable_key="a4_pk_..."),
    ) as a4:
        # Live stream of merged entities (patches applied, removals filtered)
        async for round in a4.views.ore_round.latest.use(take=10):
            print(round)
            break

        # Keyed state view, one-shot read
        round = await a4.views.ore_round.state.get(round_id=42)

        # Raw update stream with the full taxonomy (upsert | patch | remove | delete)
        async for update in a4.views.ore_round.latest.watch(filters={"state.status": "open"}):
            print(update.op, update.key)
            break
```

## Program SDKs

Raw builders are pure and work offline — no connection required. Instruction
parameters use the IDL wire names, and unknown parameters fail closed.

```python
from my_generated_stack import programs

async def build_and_send(a4, wallet_address):
    # Pure, offline instruction building (also on the connected client:
    # a4.programs.ore.raw.deploy.build(...))
    ix = programs.ore_deploy(
        amount=1_000_000,
        squares=3,
        signer=wallet_address,
        authority=wallet_address,
        round="11111111111111111111111111111111",
        entropyVar="11111111111111111111111111111111",
        entropyProgram=programs.ENTROPY_PROGRAM_ID,
    )

    # Typed PDA derivation
    miner, bump = programs.OrePdas.miner.derive(authority=wallet_address)

    # Release-addressed account reads over HTTP
    miner_account = await a4.programs.ore.accounts.miner.fetch(miner)

    # Execute built instructions through your wallet
    receipt = await a4.transaction([ix])
    return receipt
```

Stacks that ship semantic operations (via SDK extensions) also expose
`instructions` / `transactions` / `flows`, which prepare portable operations:

```python
async def execute_semantic(a4):
    prepared = await a4.programs.ore.instructions.deploy.prepare(amount=1_000_000)
    receipt = await a4.execute(prepared)
    if receipt.transaction.slot is not None:
        await a4.wait_for_processed_slot(receipt.transaction.slot)
```

Wallets implement the `arete.WalletAdapter` protocol (`async sign_and_send(...)`).
Execution outcomes follow the shared four-state model
(`confirmed | not-submitted | submitted-unknown | chain-failed`), and
`wait_for_processed_slot` bridges writes back to view state.

## Chain and transaction relay

```python
async def read_chain(a4, address):
    clock = await a4.chain.clock()
    lamports = await a4.chain.lamports(address)
    # One request per batch, up to 100 addresses; items align with the input.
    accounts = await a4.chain.accounts([address])
    blockhash = await a4.transactions.get_latest_blockhash()
    return clock, lamports, accounts, blockhash
```

## Optional solders adapter (`solana` extra)

The core SDK never imports a Solana library. The optional first-party adapter does,
and builds legacy, v0 and transaction-V1 (SIMD-0385) transactions through the relay:

```bash
pip install 'arete-sdk[solana]'   # solders >= 0.29, needs Python >= 3.10
```

```python
from solders.keypair import Keypair
from arete.adapters.solders import SoldersAdapterConfig, SoldersWalletAdapter
from arete.wallet import SendOptions, TransactionResourceOptions

wallet = SoldersWalletAdapter(SoldersAdapterConfig(keypair=Keypair(), transport=a4.transactions))

# Compute budget and priority fee are typed options, never hand-built
# ComputeBudget instructions (which the adapter rejects): V1 carries them inline
# in the message, legacy/v0 get the equivalent instructions prepended.
result = await a4.transaction([ix], wallet=wallet, send=SendOptions(
    transaction_version=1,
    resources=TransactionResourceOptions(priority_fee_lamports=10_000, heap_size=64 * 1024),
))
```

`priority_fee_lamports` (total lamports) is V1-only and
`compute_unit_price_micro_lamports` (per compute unit) is legacy/v0-only; the wrong
pairing is rejected rather than converted.

V1's `compute_unit_limit` and `loaded_accounts_data_size_limit` are always
resolved before signing, because an omitted V1 budget requests the *minimum*
rather than a generous default (SIMD-0385) — a message without them could only
fail on chain. An explicit value is used verbatim and never raised; an omitted
one is measured by simulating a provisional unsigned message that declares the
protocol maxima, then derived with headroom: the configured
`compute_unit_margin` on compute units (bounded by 1,400,000) and one 32 KiB
page of headroom on loaded data (bounded by 64 MiB). Only a metric the
simulation never reports is refused, naming the option to pass. For legacy/v0,
where an omitted ceiling means the runtime's own default, estimation stays
opt-in through `estimate_resources=True`.

`await wallet.inspect_transaction([ix])` returns fee, logs, consumed units and
loaded-accounts data size without signing, submitting or prompting; it builds
that same provisional message, so its metrics are what you pin the budgets
with. See `examples/solana_v1.py`. The base install and Python 3.9 support are
unaffected: the extra is required only to import `arete.adapters.solders`.

### Arete by default, direct RPC as an explicit escape hatch

`arete.rpc.RpcTransactionTransport` implements the same `TransactionTransport`
protocol the relay does, over a node's JSON-RPC endpoint, using the `httpx` the
SDK already carries — no Solana client dependency, no change to the base Python
minimum, and provider credentials kept out of Arete authentication:

```python
from arete.rpc import RpcTransactionTransport

rpc = RpcTransactionTransport("https://api.devnet.solana.com", headers={"x-api-key": key})

# Client-wide: Arete stays the default unless you inject this instead.
a4 = await Arete.connect(STACK, transactions=rpc)

# Adapter-level: `direct` wins even under a connected Arete client.
wallet = SoldersWalletAdapter(SoldersAdapterConfig(
    keypair=Keypair(), transport=rpc, transport_selection="direct",
))
```

| `transport_selection` | with an Arete client | standalone |
|---|---|---|
| `"auto"` (default) | the client's transport | `config.transport` |
| `"direct"` | `config.transport` | `config.transport`, required |

The backend is chosen once, before the operation: a failure on the selected one
never falls back to the other, and nothing is rebuilt, re-signed or resent
after an uncertain result.

`confirmation_timeout` is one deadline covering submission **and**
confirmation, including whatever request is in flight. A transport that takes
the transaction and then stops answering yields a `submitted-unknown` outcome
carrying the locally derived signature — never a hang, and never a resend.

## Sessions (multi-stack)

```python
async def stream_session(auth):
    session = await arete.create_session(stacks={"ore": ORE_STREAM_STACK}, auth=auth)
    async for round in session.stacks.ore.views.ore_round.latest.use():
        print(round)
        break
    await session.close()
```

## Development

```bash
pip install -e '.[dev]'
python -m pytest tests/ -q

# The solders adapter suite is collected only when the extra is installed
pip install -e '.[dev,solana]'
python -m pytest tests/test_solders_adapter.py -q
```

## License

MIT

## Links

- [Repository](https://github.com/AreteA4/arete)
- [Documentation](https://docs.arete.run)
- [Issues](https://github.com/AreteA4/arete/issues)
