Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
5036535
feat(shield-swap): package scaffold
iamalwaysuncomfortable Jul 13, 2026
6c8b788
feat(shield-swap): error taxonomy + Q64 tick math port
iamalwaysuncomfortable Jul 13, 2026
f0a8c4b
feat(abi): optional imports for generate_abi (import-using programs)
iamalwaysuncomfortable Jul 13, 2026
c7c78d2
feat(shield-swap): pinned ABI snapshot + committed aleo.codegen bindings
iamalwaysuncomfortable Jul 13, 2026
d3679c7
feat(shield-swap): pinned OpenAPI snapshot + generated API models
iamalwaysuncomfortable Jul 13, 2026
9352242
feat(shield-swap): pool/tick key derivations verified against TS vectors
iamalwaysuncomfortable Jul 13, 2026
63398d5
feat(shield-swap): blinded identity derivation, golden-vector verified
iamalwaysuncomfortable Jul 13, 2026
ceea490
feat(shield-swap): SwapHandle + semantic SlotView (Q64 price, tick ra…
iamalwaysuncomfortable Jul 13, 2026
86b60d1
feat(shield-swap): typed DEX REST client with tolerant model building
iamalwaysuncomfortable Jul 13, 2026
3e0f919
feat(shield-swap): core param resolution, nonces, imports cache, reco…
iamalwaysuncomfortable Jul 13, 2026
ecb97ab
feat(shield-swap): ShieldSwap client — typed reads, balances, DexCall…
iamalwaysuncomfortable Jul 13, 2026
e1fc20b
feat(shield-swap): claim_swap_output verb (prepare-time finalization …
iamalwaysuncomfortable Jul 13, 2026
511f1aa
feat(shield-swap): liquidity lifecycle verbs + tick insert hints
iamalwaysuncomfortable Jul 13, 2026
b126c58
feat(shield-swap): AsyncShieldSwap + AsyncApiClient (swap lifecycle)
iamalwaysuncomfortable Jul 13, 2026
58611ab
feat(shield-swap): agent tools, MCP server, live integration tiers, e…
iamalwaysuncomfortable Jul 13, 2026
d7aab7f
fix(shield-swap): review findings — process registration, transaction…
iamalwaysuncomfortable Jul 13, 2026
3e53ea4
test(shield-swap): full read-action live tier + DEX API auth
iamalwaysuncomfortable Jul 13, 2026
250b9cd
chore(shield-swap): rename package sdk-shield-swap -> shield-swap-sdk
iamalwaysuncomfortable Jul 14, 2026
2c11a93
feat(sdk): program deployment APIs + proofless devnode deployments
iamalwaysuncomfortable Jul 14, 2026
7680ab8
test(shield-swap): hermetic devnode AMM lifecycle tier
iamalwaysuncomfortable Jul 14, 2026
43c7e1c
docs(shield-swap): package README
iamalwaysuncomfortable Jul 14, 2026
44b4c78
chore: rename dists (aleo -> aleo-sdk, aleo-abi -> aleo-contract-abi-…
iamalwaysuncomfortable Jul 14, 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
4 changes: 2 additions & 2 deletions .github/workflows/sdk-wheels.yml
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ jobs:
- name: Smoke test wheel
shell: bash
run: |
pip install --find-links sdk/dist aleo
pip install --find-links sdk/dist aleo-sdk
python -c "
from aleo.mainnet import PrivateKey as MainnetPrivateKey
from aleo.testnet import PrivateKey as TestnetPrivateKey
Expand Down Expand Up @@ -185,7 +185,7 @@ jobs:
- name: Smoke test wheel
shell: bash
run: |
pip install --find-links sdk-abi/dist aleo-abi
pip install --find-links sdk-abi/dist aleo-contract-abi-generator
python -c "import aleo_abi; print(aleo_abi.generate_abi.__name__)"
- name: Upload wheel
uses: actions/upload-artifact@v4
Expand Down
16 changes: 8 additions & 8 deletions .github/workflows/sdk.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ jobs:
python ../.github/scripts/merge_testnet_so.py dist dist-testnet
- name: Install wheel + test deps
run: |
pip install --find-links dist aleo
pip install --find-links dist aleo-sdk
pip install pytest pytest-xdist httpx pynacl responses pytest-asyncio
# Fast, offline tests; -n auto parallelizes across cores.
- name: pytest (fast suite)
Expand Down Expand Up @@ -116,7 +116,7 @@ jobs:
python ../.github/scripts/merge_testnet_so.py dist dist-testnet
- name: Install wheel + test deps
run: |
pip install --find-links dist aleo
pip install --find-links dist aleo-sdk
pip install pytest httpx pynacl responses pytest-asyncio
# The two slow tests share one module-scoped proven execution; xdist
# would prove twice, so they run in a single worker on purpose.
Expand All @@ -129,7 +129,7 @@ jobs:
run: python -m pytest python/tests/test_testnet.py -v

test-abi:
name: Test (aleo-abi package)
name: Test (aleo-contract-abi-generator package)
runs-on: ubuntu-latest
defaults:
run:
Expand All @@ -155,10 +155,10 @@ jobs:
exit 1
fi
# Build the aleo two-network wheel (mainnet + testnet merged) plus the
# aleo-abi wheel. The testnet .so must be spliced into the aleo wheel
# BEFORE the aleo-abi wheel lands in dist, so the merge sees only the
# aleo-contract-abi-generator wheel. The testnet .so must be spliced into the aleo wheel
# BEFORE the aleo-contract-abi-generator wheel lands in dist, so the merge sees only the
# aleo wheel.
- name: Build wheels (aleo two-network + aleo-abi)
- name: Build wheels (aleo two-network + aleo-contract-abi-generator)
run: |
pip install maturin pytest requests
maturin build --release --features mainnet --manifest-path sdk/Cargo.toml --out dist
Expand All @@ -169,11 +169,11 @@ jobs:
maturin build --release --manifest-path sdk-abi/Cargo.toml --out dist
- name: Install wheels
run: |
pip install --find-links dist aleo aleo-abi
pip install --find-links dist aleo-sdk aleo-contract-abi-generator
# Run each suite from inside its package dir so pytest uses that
# package's own pytest.ini (sdk-abi/pytest.ini, sdk/pytest.ini) rather
# than walking up to the stale legacy root setup.cfg.
- name: pytest (aleo-abi package suite)
- name: pytest (aleo-contract-abi-generator package suite)
run: |
cd sdk-abi
python -m pytest python/tests -v
Expand Down
5 changes: 4 additions & 1 deletion sdk-abi/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 6 additions & 1 deletion sdk-abi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

[package]
name = "aleo-abi"
version = "0.1.0"
version = "0.2.0"
edition = "2024"
license = "GPL-3.0-or-later"
description = "Python bindings for ABI generation from Aleo bytecode (via Leo's leo-abi crate)"
Expand All @@ -20,3 +20,8 @@ pyo3 = { version = "0.20.0", features = ["extension-module", "abi3-py37", "anyho
serde_json = "1"
leo-abi = { git = "https://github.com/ProvableHQ/leo", rev = "ba2c01722a48f84b4d90b93aeaf8305b7c03dbce", package = "leo-abi", features = ["aleo-bytecode"] }
leo-ast = { git = "https://github.com/ProvableHQ/leo", rev = "ba2c01722a48f84b4d90b93aeaf8305b7c03dbce", package = "leo-ast" }
leo-disassembler = { git = "https://github.com/ProvableHQ/leo", rev = "ba2c01722a48f84b4d90b93aeaf8305b7c03dbce", package = "leo-disassembler" }
leo-span = { git = "https://github.com/ProvableHQ/leo", rev = "ba2c01722a48f84b4d90b93aeaf8305b7c03dbce", package = "leo-span" }
# Same tag + feature set leo pins, so `Process<N>`/`Program<N>` are the same
# types leo-disassembler's signatures expect.
snarkvm = { git = "https://github.com/ProvableHQ/snarkVM", tag = "v4.8.1", features = ["test_consensus_heights", "dev_skip_checks", "test_targets", "history"] }
93 changes: 93 additions & 0 deletions sdk-abi/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# aleo-contract-abi-generator

Generates a JSON ABI from deployed Aleo bytecode, and checks one ABI against
another for compatibility. Rust bindings around Leo's ABI generation with
full snarkVM validation — a program that does not validate does not get an
ABI. Everything runs locally; nothing touches the network.

The distribution is `aleo-contract-abi-generator`; the import module is
`aleo_abi`.

```python
import aleo_abi

abi_json = aleo_abi.generate_abi("simple.aleo", bytecode, "testnet", None)
violations = aleo_abi.check_compatibility(candidate_json, standard_json)
```

## Install

```bash
pip install aleo-contract-abi-generator
```

Most callers want the friendlier hook in the main SDK instead —
`aleo.abi.generate_abi` accepts a `Program` object or a raw bytecode string,
infers the program name, and returns a dict:

```python
from aleo import abi
from aleo.mainnet import Program

result = abi.generate_abi(Program.credits())
result["program"] # "credits.aleo"
```

The hook imports this package lazily and raises `ImportError` with the
install command if it is absent — the main SDK does not depend on it.

## Generating an ABI

`generate_abi(program_name, bytecode, network, imports)` returns a
pretty-printed JSON string describing the program: its `structs`, `records`,
`mappings`, and `functions`, each field carrying a structured type
(`{"Primitive": {"Int": "I32"}}`, record ownership, visibility).

snarkVM validation is **contextual**: a program that declares imports is
rejected unless those imports are supplied. Pass them as
`(program_id, bytecode)` pairs in topological order — dependencies before
dependents:

```python
abi_json = aleo_abi.generate_abi(
"shield_swap_v3.aleo", amm_bytecode, "testnet",
[("test_shield_swap_multisig_core.aleo", multisig_bytecode)],
)
```

`network` is `"mainnet"`, `"testnet"`, or `"canary"` — it selects the
validation rules, not a connection.

## Checking compatibility

`check_compatibility(candidate_abi_json, standard_abi_json)` returns a list
of violation strings — empty means the candidate satisfies the standard.
Use it to pin a deployed contract's surface and fail *your* CI when the
deployment drifts, instead of your consumers:

```python
violations = aleo_abi.check_compatibility(deployed_abi, pinned_abi)
assert not violations, "\n".join(violations)
```

This is how `shield-swap-sdk`'s live drift test guards its committed
bindings (`codegen/regen-abi.sh` regenerates the pin from the deployed
program).

## Downstream: generated Python bindings

The ABI JSON is the input format for the main SDK's `aleo.codegen`, which
emits typed dataclasses and entrypoint stubs from it:

```bash
python -m aleo.codegen --abi shield_swap.abi.json --out _generated.py
```

## Tests

```bash
cd sdk-abi && python -m pytest python/tests -v # hermetic — no network
```

Fixtures and expected ABIs are vendored from Leo's own test suite, so the
output shape is pinned to upstream.
5 changes: 3 additions & 2 deletions sdk-abi/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ requires = ["maturin>=1.0,<2.0"]
build-backend = "maturin"

[project]
name = "aleo-abi"
version = "0.1.0"
name = "aleo-contract-abi-generator"
version = "0.2.0"
description = "Python bindings for ABI generation from Aleo bytecode"
readme = "README.md"
license = {text = "GPL-3.0-or-later"}
requires-python = ">=3.7"
dependencies = []
Expand Down
2 changes: 1 addition & 1 deletion sdk-abi/python/tests/test_abi.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Copyright (C) 2024 Provable Inc.
# SPDX-License-Identifier: GPL-3.0-or-later
"""Tests for the aleo-abi package."""
"""Tests for the aleo-contract-abi-generator package."""

import json

Expand Down
54 changes: 51 additions & 3 deletions sdk-abi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,68 @@ fn parse_network(network: &str) -> anyhow::Result<leo_ast::NetworkName> {
}
}

/// Generate an ABI with snarkVM validation, loading `imports` into the
/// process first (in the given order) so import-using programs validate.
fn generate_with_imports<N: snarkvm::prelude::Network>(
program_name: &str,
bytecode: &str,
imports: &[(String, String)],
) -> anyhow::Result<leo_abi::Program> {
use std::str::FromStr;
leo_span::create_session_if_not_set_then(|_| {
let mut process = snarkvm::prelude::Process::<N>::load()
.map_err(|e| anyhow::anyhow!("failed to load snarkVM process: {e}"))?;
for (dep_name, dep_src) in imports {
let dep = snarkvm::prelude::Program::<N>::from_str(dep_src)
.map_err(|e| anyhow::anyhow!("import {dep_name} failed to parse: {e}"))?;
process
.lock()
.add_program(&dep)
.map_err(|e| anyhow::anyhow!("import {dep_name} failed snarkVM validation: {e}"))?;
}
let aleo = leo_disassembler::disassemble_from_str(program_name, bytecode, &mut process)
.map_err(|e| anyhow::anyhow!("{e}"))?;
Ok(leo_abi::aleo::generate(&aleo))
})
}

/// Generate an ABI JSON string from Aleo bytecode.
///
/// Args:
/// program_name: The program name (e.g. "token.aleo").
/// bytecode: The Aleo bytecode string.
/// network: One of "mainnet", "testnet", or "canary".
/// imports: Optional list of (program_id, bytecode) dependencies, in
/// topological order (dependencies before dependents). snarkVM's
/// validation is contextual: a program whose imports are not loaded
/// first is rejected, so import-using programs require this.
///
/// Returns:
/// A pretty-printed JSON string representing the program ABI.
#[pyfunction]
fn generate_abi(program_name: &str, bytecode: &str, network: &str) -> anyhow::Result<String> {
#[pyo3(signature = (program_name, bytecode, network, imports = None))]
fn generate_abi(
program_name: &str,
bytecode: &str,
network: &str,
imports: Option<Vec<(String, String)>>,
) -> anyhow::Result<String> {
let net = parse_network(network)?;
let abi = leo_abi::aleo::generate_from_bytecode(program_name, bytecode, net)
.map_err(|e| anyhow::anyhow!("{}", e))?;
let abi = match imports {
None => leo_abi::aleo::generate_from_bytecode(program_name, bytecode, net)
.map_err(|e| anyhow::anyhow!("{}", e))?,
Some(deps) => match net {
leo_ast::NetworkName::MainnetV0 => {
generate_with_imports::<snarkvm::prelude::MainnetV0>(program_name, bytecode, &deps)?
}
leo_ast::NetworkName::TestnetV0 => {
generate_with_imports::<snarkvm::prelude::TestnetV0>(program_name, bytecode, &deps)?
}
leo_ast::NetworkName::CanaryV0 => {
generate_with_imports::<snarkvm::prelude::CanaryV0>(program_name, bytecode, &deps)?
}
},
};
Ok(serde_json::to_string_pretty(&abi)?)
}

Expand Down
2 changes: 1 addition & 1 deletion sdk/docs/source/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ To use Aleo SDK, first install it using pip:

.. code-block:: console

(.venv) $ pip install aleo
(.venv) $ pip install aleo-sdk
4 changes: 2 additions & 2 deletions sdk/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "aleo"
description = "A Python SDK for zero-knowledge cryptography based on Aleo"
name = "aleo-sdk"
description = "Python SDK for building zero-knowledge apps and DeFi on the Aleo network"
version = "0.2.0"
readme = "Readme.md"
license = {file = "LICENSE.md"}
Expand Down
15 changes: 10 additions & 5 deletions sdk/python/aleo/_client_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,16 @@ def is_provable_host(url: str) -> bool:


def package_version() -> str:
try:
from importlib.metadata import version
return version("aleo")
except Exception:
return "0.0.0"
from importlib.metadata import version

# "aleo" is the pre-0.2 distribution name; keep it as a fallback so
# older installs still report their real version.
for dist in ("aleo-sdk", "aleo"):
try:
return version(dist)
except Exception:
continue
return "0.0.0"


def user_agent() -> str:
Expand Down
Loading
Loading