Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
fb3ad9c
feat(codegen): fixed-length Array ABI type support
iamalwaysuncomfortable Jul 28, 2026
3d5fbd5
feat(shield-swap): regenerate wire layer from deployed shield_swap.aleo
iamalwaysuncomfortable Jul 28, 2026
af1aaca
feat(shield-swap): Q128.128 tick math + U256 helpers
iamalwaysuncomfortable Jul 28, 2026
c357657
feat(shield-swap): raw-amount swap resolution, X128 limits, freezelis…
iamalwaysuncomfortable Jul 28, 2026
beef4b1
feat(shield-swap): X128 slot price, new default program for derivations
iamalwaysuncomfortable Jul 28, 2026
dec769c
feat(shield-swap): new-stack write verbs — U256 prices, proofs, withd…
iamalwaysuncomfortable Jul 28, 2026
b073bae
feat(shield-swap): automatic router dispatch for wrapped assets
iamalwaysuncomfortable Jul 28, 2026
755e086
feat(shield-swap): mirror new-stack verbs and routing in async client
iamalwaysuncomfortable Jul 28, 2026
94fb73c
feat(shield-swap): cut API layer over to the staging Shield Swap API
iamalwaysuncomfortable Jul 28, 2026
adc1700
docs(shield-swap): re-point docs, fixtures, and helpers at the new stack
iamalwaysuncomfortable Jul 28, 2026
6f3b03b
test(shield-swap): devnode + live cutover coverage for the new stack
iamalwaysuncomfortable Jul 28, 2026
7b2a234
fix(codegen): satisfy strict pyright on Array support
iamalwaysuncomfortable Jul 28, 2026
a646c55
fix(shield-swap): pin mcp extra below 2.0 pending API port
iamalwaysuncomfortable Jul 28, 2026
c18548b
test(shield-swap): pin router ABIs as drift guards with input-count p…
iamalwaysuncomfortable Jul 28, 2026
5cf449f
fix(shield-swap): adapt auth to staging session model
iamalwaysuncomfortable Jul 28, 2026
8cc4982
fix(shield-swap): accept referral codes in redeem_code
iamalwaysuncomfortable Jul 28, 2026
ef126b2
feat(shield-swap): pasted invites are referral codes; access codes se…
iamalwaysuncomfortable Jul 28, 2026
4e4dc0d
fix(shield-swap): cookie session outranks bearer; onboarding idempote…
iamalwaysuncomfortable Jul 28, 2026
073bec1
chore(shield-swap): rehearsal takes pasted referral codes, trades hel…
iamalwaysuncomfortable Jul 28, 2026
aa71c33
fix(shield-swap): mint hints walk the on-chain tick list
iamalwaysuncomfortable Jul 28, 2026
9fbec68
chore: bump aleo-sdk, aleo-contract-abi-generator, shield-swap-sdk to…
iamalwaysuncomfortable Jul 28, 2026
18730d2
chore: bump versions to 0.3.1
iamalwaysuncomfortable Jul 29, 2026
bc96c42
docs: voice.md docstrings for the undocumented public surface (#65)
iamalwaysuncomfortable Jul 30, 2026
28ed5ed
fix(shield-swap): DEX correctness, owned-position views, 0.4.0 (#67)
iamalwaysuncomfortable Aug 6, 2026
2b6779a
fix(shield-swap): authentication is the whole gate; referral codes op…
iamalwaysuncomfortable Sep 3, 2026
1a879c4
feat(shield-swap): sync with deployed contracts, 2026-09 API, and vei…
iamalwaysuncomfortable Sep 11, 2026
d26f939
feat(shield-swap): follow the 2026-09 DEX API route retirement; wrap …
iamalwaysuncomfortable Sep 11, 2026
e7fd0d7
fix(shield-swap): review fixes — async gallop, per-program balance is…
iamalwaysuncomfortable Sep 11, 2026
191c6ef
build: snarkvm 4.9.1 from crates.io; leo master c82f149e for sdk-abi
iamalwaysuncomfortable Sep 11, 2026
080d7ba
chore: bump aleo-sdk, aleo-contract-abi-generator, shield-swap-sdk to…
iamalwaysuncomfortable Sep 11, 2026
f85c298
fix: fee estimates use the chain head's consensus version; safer toke…
iamalwaysuncomfortable Sep 11, 2026
152f668
fix(stubs): declare Deployment in the network type stubs
iamalwaysuncomfortable Sep 11, 2026
d3e3398
fix: address Copilot review — position selection by token id, consume…
iamalwaysuncomfortable Sep 11, 2026
e95aa7b
fix(facade): consult the chain head for fee estimates only on the hos…
iamalwaysuncomfortable Sep 11, 2026
3252e3d
test(facade): hosted-host cases use api.provable.com on this branch
iamalwaysuncomfortable Sep 11, 2026
72fc002
feat: edge.provable.com/api is the default hosted API; no credentials…
iamalwaysuncomfortable Sep 11, 2026
a792a9d
test(e2e): live read sweep for both network clients
iamalwaysuncomfortable Sep 11, 2026
bfbe6a9
test(facade): the hosted-host cases of the fee-height test include th…
iamalwaysuncomfortable Sep 11, 2026
9266a8e
fix: address Copilot review — credentials never leave for the edge, a…
iamalwaysuncomfortable Sep 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 37 additions & 1 deletion .agents/voice.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,41 @@ Why it fails: filler ("easily", "powerfully", "seamless"); restates types; says
"a result" instead of what you get and what you do with it; no side-effect or
error information.

### Phrasings to avoid

Beyond the filler above, these are banned outright. Each one reads like guidance
while leaving the caller with nothing to act on — replace it with the concrete
instruction, or delete it.

- **"reach for"** — never use it. Say what to call and when.
- **"defensively"**, **"as appropriate"**, **"where necessary"** — name the
actual condition instead.

```python
# Bad — sounds like advice, gives none
"""… either may be ``None``, so reach for them defensively."""

# Good — states the check and what it protects
"""… treat both as optional — guard with ``if entry.token0_info`` before
reading a field rather than assuming a default."""
```

### Plain verbs

Say what the code does, not what it is like. A service or object does not
"speak", "know", "want", or "see" — it returns, accepts, requires, stores,
decrypts. Figurative verbs read as style and cost the reader a translation step.

```python
# Bad — figurative
"""The API speaks decimal amounts; the contract speaks base units."""
"""Every pool the indexer knows."""

# Good — plain
"""The API returns decimal amounts; the contract takes base units."""
"""Every pool the DEX lists."""
```

## Naming in prose and examples

- Use the Pythonic surface: properties (`key.address`, not `key.address()`),
Expand All @@ -71,4 +106,5 @@ error information.
This is a privacy chain. Do not document or add affordances that link
signatures to signer addresses (no `recover`-style verb). When a feature shares
secret material with a service (e.g. delegated record scanning shares the view
key), state that tradeoff plainly and point to the self-hosted alternative.
key), state that tradeoff plainly. Where an alternative exists, name the
supported extension point (e.g. assigning a custom ``RecordProvider``).
6 changes: 3 additions & 3 deletions .claude/skills/shield-swap/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
---
name: shield-swap
description: Use when the user wants to trade, LP, or build on the shield_swap AMM — setting up an account, redeeming an invite, getting the airdrop, swapping privately, managing liquidity positions, or collecting winnings via the aleo_shield_swap Python SDK.
description: Use when the user wants to trade, LP, or build on the shield_swap AMM — setting up an account, optionally crediting a referral code, getting the airdrop, swapping privately, managing liquidity positions, or collecting winnings via the aleo_shield_swap Python SDK.
---

Read `shield-swap-sdk/AGENTS.md` (generated from the SDK — always current)
and follow its Tier 1 lifecycle and conversation pattern. Write Python
against the SDK; don't re-implement flows the verbs already provide, and
against the SDK; don't re-implement flows the methods already provide, and
don't read SDK source unless AGENTS.md genuinely lacks the answer.
Preconditions are enforced in code — on error, read the exception message;
it names the verb that fixes it.
it names the method that fixes it.
29 changes: 18 additions & 11 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Aleo Python SDK — Agent Guide

Python SDK for Aleo: PyO3 bindings over **snarkvm v4.8.1** plus pure-Python
Python SDK for Aleo: PyO3 bindings over **snarkvm v4.9.1** plus pure-Python
client / record-scanner / delegated-proving layers and a Web3.py-style facade.
Two shipped packages:

Expand Down Expand Up @@ -61,15 +61,22 @@ addopts if you invoke pytest from the repo root.

## Delegated services (DPS + record scanner)

**Auth, proving, and scanning are all Provable *services* on `api.provable.com`,
hosted at the API ORIGIN — NOT under the read node's `/v2/{network}` base.** The
read/RPC endpoints live at `https://api.provable.com/v2/{network}/…`; the
services hang off the bare origin (`https://api.provable.com`) at their own path
prefixes. Each is confirmed working against live testnet (see
`tests/e2e/test_testnet_e2e.py`):
**Proving and scanning are Provable *services* hung off the hosted API's
SERVICE ROOT — NOT under the read node's `/v2/{network}` base.** Two hosts:

- **JWT auth** — origin, no prefix: `POST {origin}/jwts/{consumerId}`. Derive the
origin with `jwt_origin(base_url)` (`scheme://host`, path stripped).
- **`https://edge.provable.com/api` (the default, `DEFAULT_HOST`)** — open: no
API key, consumer id, or JWT for reads, `/prove`, or `/scanner`. Note the
`/api` path prefix: reads are `{root}/v2/{network}/…`, services `{root}/prove`,
`{root}/scanner`. Derive the root with `service_root(url)` (keeps the prefix;
strips a legacy `/v2[/{network}]` suffix) — NOT `jwt_origin`, which drops it.
- **`https://api.provable.com` (`LEGACY_HOST`)** — credentialed: the same
layout at the bare origin, with the prover and scanner behind
`api_key` + `consumer_id` JWTs. `requires_credentials(url)` is True only here;
everything below about JWTs applies only to this host.

Each is confirmed working against live testnet (see `tests/e2e/`):

- **JWT auth (legacy host only)** — `POST {root}/jwts/{consumerId}`.
- **Delegated proving** — `{origin}/prove/{network}` prefix:
- `GET {origin}/prove/{network}/pubkey` — ephemeral X25519 key + key id + a
`Set-Cookie` **affinity** session. The ephemeral private key lives only on
Expand All @@ -81,8 +88,8 @@ prefixes. Each is confirmed working against live testnet (see
- `POST {origin}/prove/{network}/prove/authorization` (or `/prove/request`) —
sealed-box `{key_id, ciphertext}`; JWT + the affinity cookie; SDK retries
500/503.
- **Record scanner** — `{origin}/scanner/{network}` prefix (env
`RECORD_SCANNER_URL=https://api.provable.com/scanner`).
- **Record scanner** — `{root}/scanner/{network}` prefix (env
`RECORD_SCANNER_URL=https://edge.provable.com/api/scanner`).

Documented endpoints (docs describe paths *relative to the service base*; the
base is the origin + service prefix above):
Expand Down
23 changes: 12 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ The SDK ships two layers:
- A **Web3.py-style facade** (`aleo.Aleo` / `aleo.AsyncAleo`) — a high-level, batteries-included client for connecting to a node, managing accounts, reading state, and building/proving/broadcasting transactions.
- **Low-level primitives** (`aleo.mainnet`, `aleo.testnet`) — direct Python bindings to Aleo's zero-knowledge cryptographic types, for when you need full control.

Built with snarkvm 4.8.1 (MainnetV0). For build instructions, see [sdk/Readme.md](./sdk/Readme.md).
Built with snarkvm 4.9.1 (MainnetV0). For build instructions, see [sdk/Readme.md](./sdk/Readme.md).

## Agent skills (trade on Shield Swap by chatting)

The [`shield-swap-sdk`](./shield-swap-sdk) package ships everything an AI
agent needs to drive the shield_swap AMM — set up an account, redeem an
invite code, get the airdrop, make private swaps, manage liquidity, and
collect earnings — from a single generated guide.
agent needs to drive the shield_swap AMM — set up an account, get the
airdrop, make private swaps, manage liquidity, and collect earnings — from
a single generated guide.

**Any agent (Claude Code, Codex, Cursor, custom):**

Expand All @@ -27,9 +27,10 @@ Most coding agents (Codex, Cursor, Claude Code, …) automatically read a
repo-root `AGENTS.md`, so after that one command just chat: *"set up a
shield-swap account and get tokens"*, *"find pools and start swapping"*.
(Equivalently, open with "run `python -m aleo_shield_swap` and follow that
guide", or paste the output into the agent's instructions.) The one thing
the agent will ask you for is an invite code; everything else — key
material, API credentials, airdrop — is handled by the SDK. Bring an existing account by exporting
guide", or paste the output into the agent's instructions.) The agent needs
nothing from you to get started — key material, API credentials, and the
airdrop are all handled by the SDK; a friend's referral code is optional.
Bring an existing account by exporting
`SHIELD_SWAP_PRIVATE_KEY` (or `SHIELD_SWAP_PRIVATE_KEY_FILE`) before the
first run — never paste a private key into the chat.

Expand All @@ -54,7 +55,7 @@ python -m aleo_shield_swap.mcp # stdio server with the lifecycle tools
from aleo import Aleo

# Connect (construction is offline — no I/O until you make a call)
aleo = Aleo(Aleo.HTTPProvider("https://api.provable.com/v2"))
aleo = Aleo(Aleo.HTTPProvider("https://edge.provable.com/api"))
print(aleo.network_name) # "mainnet"
print(aleo.network_id) # 0

Expand All @@ -78,7 +79,7 @@ The facade follows a clean top-to-bottom narrative: **connect → account → re
```python
from aleo import Aleo

aleo = Aleo(Aleo.HTTPProvider("https://api.provable.com/v2"))
aleo = Aleo(Aleo.HTTPProvider("https://edge.provable.com/api"))

# Optional: check reachability # requires a live node
if aleo.is_connected():
Expand Down Expand Up @@ -203,7 +204,7 @@ credits.functions.transfer_private(record, str(recipient.address), 1) \
.delegate(account)
```

`aleo.record_provider` is swappable: set it to your own object implementing the `RecordProvider` protocol (`get_unspent_credits_record` / `find`) — e.g. a self-hosted scanner — and the whole facade (including private-fee auto-sourcing) uses it, with no view-key sharing.
`aleo.record_provider` is swappable: set it to your own object implementing the `RecordProvider` protocol (`get_unspent_credits_record` / `find`), and the whole facade (including private-fee auto-sourcing) uses it, with no view-key sharing.

## Async (`AsyncAleo`)

Expand All @@ -214,7 +215,7 @@ import asyncio
from aleo import AsyncAleo

async def main():
aleo = AsyncAleo(AsyncAleo.HTTPProvider("https://api.provable.com/v2"))
aleo = AsyncAleo(AsyncAleo.HTTPProvider("https://edge.provable.com/api"))
print(aleo.network_name) # sync — no I/O

# Account ops are sync (purely local), even on AsyncAleo
Expand Down
Loading
Loading