Skip to content
Merged
Show file tree
Hide file tree
Changes from 21 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
3 changes: 3 additions & 0 deletions sdk-abi/Cargo.lock

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

5 changes: 5 additions & 0 deletions sdk-abi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"] }
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
12 changes: 10 additions & 2 deletions sdk/python/aleo/abi.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,21 @@
from typing import Any, Union


def generate_abi(program: object, network: str = "mainnet") -> dict[str, Any]:
def generate_abi(
program: object,
network: str = "mainnet",
imports: "list[tuple[str, str]] | None" = None,
) -> dict[str, Any]:
"""Generate an ABI dict for an Aleo program.

Args:
program: Either an aleo Program object (with .source and .id attributes)
or a raw bytecode string.
network: Network name: "mainnet", "testnet", or "canary".
imports: Optional ``(program_id, bytecode)`` dependencies in
topological order (dependencies before dependents). snarkVM
validation is contextual, so a program that declares imports
is rejected unless they are supplied here.

Returns:
A dict containing the ABI for the program.
Expand Down Expand Up @@ -51,7 +59,7 @@ def generate_abi(program: object, network: str = "mainnet") -> dict[str, Any]:
f"Expected a Program object or str, got {type(program).__name__}"
)

json_str = _aleo_abi.generate_abi(name, bytecode, network)
json_str = _aleo_abi.generate_abi(name, bytecode, network, imports)
return json.loads(json_str)


Expand Down
1 change: 1 addition & 0 deletions sdk/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ fn register(_py: Python, m: &PyModule) -> PyResult<()> {
m.add_class::<ComputeKey>()?;
m.add_class::<Credits>()?;
m.add_class::<DynamicRecord>()?;
m.add_class::<Deployment>()?;
m.add_class::<Execution>()?;
m.add_class::<ExecutionRequest>()?;
m.add_class::<Fee>()?;
Expand Down
126 changes: 126 additions & 0 deletions sdk/src/programs/deployment.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
// Copyright (C) 2019-2023 Aleo Systems Inc.
// This file is part of the Aleo SDK library.

// The Aleo SDK library is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// The Aleo SDK library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with the Aleo SDK library. If not, see <https://www.gnu.org/licenses/>.

use crate::{
types::{CertificateNative, DeploymentNative, VerifyingKeyNative},
Address, Field, Program,
};

use pyo3::prelude::*;

use std::str::FromStr;

// Dummy verifying key + certificate for devnode deployments (the devnode
// skips certificate verification). Copied from the wasm SDK's
// `buildDevnodeDeploymentTransaction`: the key encodes num_public_inputs=64
// to accommodate functions with many inputs/outputs.
const DEVNODE_VERIFIER_KEY: &str = "verifier1q9qqqqqqqqqqqqyvxgqqqqqqqqq87vsqqqqqqqqqhe7sqqqqqqqqqma4qqqqqqqqqq65yqqqqqqqqqqvqqqqqqqqqqqgtlaj49fmrk2d8slmselaj9tpucgxv6awu6yu4pfcn5xa0yy0tpxpc8wemasjvvxr9248vt3509vpk3u60ejyfd9xtvjmudpp7ljq2csk4yqz70ug3x8xp3xn3ul0yrrw0mvd2g8ju7rts50u3smue03gp99j88f0ky8h6fjlpvh58rmxv53mldmgrxa3fq6spsh8gt5whvsyu2rk4a2wmeyrgvvdf29pwp02srktxnvht3k6ff094usjtllggva2ym75xc4lzuqu9xx8ylfkm3qc7lf7ktk9uu9du5raukh828dzgq26hrarq5ajjl7pz7zk924kekjrp92r6jh9dpp05mxtuffwlmvew84dvnqrkre7lw29mkdzgdxwe7q8z0vnkv2vwwdraekw2va3plu7rkxhtnkuxvce0qkgxcxn5mtg9q2c3vxdf2r7jjse2g68dgvyh85q4mzfnvn07lletrpty3vypus00gfu9m47rzay4mh5w9f03z9zgzgzhkv0mupdqsk8naljqm9tc2qqzhf6yp3mnv2ey89xk7sw9pslzzlkndfd2upzmew4e4vnrkr556kexs9qrykkuhsr260mnrgh7uv0sp2meky0keeukaxgjdsnmy77kl48g3swcvqdjm50ejzr7x04vy7hn7anhd0xeetclxunnl7pd6e52qxdlr3nmutz4zr8f2xqa57a2zkl59a28w842cj4783zpy9hxw03k6vz4a3uu7sm072uqknpxjk8fyq4vxtqd08kd93c2mt40lj9ag35nm4rwcfjayejk57m9qqu83qnkrj3sz90pw808srmf705n2yu6gvqazpvu2mwm8x6mgtlsntxfhr0qas43rqxnccft36z4ygty86390t7vrt08derz8368z8ekn3yywxgp4uq24gm6e58tpp0lcvtpsm3nkwpnmzztx4qvkaf6vk38wg787h8mfpqqqqqqqqqqffkful";
const DEVNODE_CERTIFICATE: &str =
"certificate1qyqsqqqqqqqqqqxvwszp09v860w62s2l4g6eqf0kzppyax5we36957ywqm2dplzwvvlqg0kwlnmhzfatnax7uaqt7yqqqw0sc4u";

/// A program deployment: the program, its synthesized verifying keys and
/// certificates, and (V9+) the program checksum and owner address.
///
/// Produced by `Process.deploy`; consumed by `Transaction.from_deployment`.
#[pyclass(frozen)]
#[derive(Clone)]
pub struct Deployment(DeploymentNative);

#[pymethods]
impl Deployment {
/// Constructs a Deployment from a JSON string.
#[staticmethod]
fn from_json(json: &str) -> anyhow::Result<Self> {
DeploymentNative::from_str(json).map(Self)
}

/// Builds a deployment WITHOUT synthesizing circuit keys: every function
/// and record gets a shared dummy verifying key + certificate. Devnodes
/// accept it (they skip certificate verification); real networks reject
/// it. Mirrors the wasm SDK's `buildDevnodeDeploymentTransaction`
/// (V9+ checksum/owner and V14+ record keys are always set).
#[staticmethod]
#[pyo3(signature = (program, owner, edition = 0))]
fn from_program_unproven(
program: &Program,
owner: &Address,
edition: u16,
) -> anyhow::Result<Self> {
if program.functions().is_empty() {
anyhow::bail!(
"Attempted to create an empty deployment: {} has no functions",
program.id()
);
}
let vk = VerifyingKeyNative::from_str(DEVNODE_VERIFIER_KEY)?;
let cert = CertificateNative::from_str(DEVNODE_CERTIFICATE)?;
let mut verifying_keys =
Vec::with_capacity(program.functions().len() + program.records().len());
for function_name in program.functions().keys() {
verifying_keys.push((*function_name, (vk.clone(), cert.clone())));
}
for record_name in program.records().keys() {
verifying_keys.push((*record_name, (vk.clone(), cert.clone())));
}
let mut deployment =
DeploymentNative::new(edition, (**program).clone(), verifying_keys, None, None)?;
deployment.set_program_checksum_raw(Some(deployment.program().to_checksum()));
deployment.set_program_owner_raw(Some(**owner));
Ok(Self(deployment))
}

/// Returns the JSON string representation of the deployment.
fn to_json(&self) -> anyhow::Result<String> {
Ok(serde_json::to_string(&self.0)?)
}

/// Returns the deployment ID (the fee's `deployment_or_execution_id`).
fn deployment_id(&self) -> anyhow::Result<Field> {
self.0.to_deployment_id().map(Into::into)
}

/// Returns the program ID being deployed.
fn program_id(&self) -> String {
self.0.program_id().to_string()
}

/// Returns the number of functions in the deployed program.
fn num_functions(&self) -> usize {
self.0.program().functions().len()
}

fn __str__(&self) -> anyhow::Result<String> {
self.to_json()
}
}

impl From<DeploymentNative> for Deployment {
fn from(value: DeploymentNative) -> Self {
Self(value)
}
}

impl From<Deployment> for DeploymentNative {
fn from(value: Deployment) -> Self {
value.0
}
}

impl AsRef<DeploymentNative> for Deployment {
fn as_ref(&self) -> &DeploymentNative {
&self.0
}
}
19 changes: 18 additions & 1 deletion sdk/src/programs/execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with the Aleo SDK library. If not, see <https://www.gnu.org/licenses/>.

use crate::{programs::Transition, types::ExecutionNative, Field};
use crate::{programs::Transition, types::ExecutionNative, Authorization, Field};

use pyo3::prelude::*;
use snarkvm::prelude::{FromBytes, ToBytes};
Expand All @@ -34,6 +34,23 @@ impl Execution {
self.0.to_execution_id().map(Into::into)
}

/// Builds an UNPROVEN execution from an authorization — for devnodes
/// only (they skip proof verification); real networks reject it.
///
/// `state_root` is the node's latest global state root (`sr1…`).
#[staticmethod]
fn from_authorization_unproven(
authorization: &Authorization,
state_root: &str,
) -> anyhow::Result<Self> {
use snarkvm::prelude::Network;
use std::str::FromStr;

let root = <crate::types::CurrentNetwork as Network>::StateRoot::from_str(state_root)?;
let native: crate::types::AuthorizationNative = authorization.clone().into();
ExecutionNative::from(native.transitions().values().cloned(), root, None).map(Self)
}

/// Reads in an Execution from a JSON string.
#[staticmethod]
fn from_json(json: &str) -> anyhow::Result<Self> {
Expand Down
23 changes: 22 additions & 1 deletion sdk/src/programs/fee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with the Aleo SDK library. If not, see <https://www.gnu.org/licenses/>.

use crate::{types::FeeNative, Address, Transition};
use crate::{types::FeeNative, Address, Authorization, Transition};

use pyo3::prelude::*;
use snarkvm::prelude::{FromBytes, ToBytes};
Expand Down Expand Up @@ -50,6 +50,27 @@ impl Fee {
self.0.transition().clone().into()
}

/// Builds an UNPROVEN fee from a fee authorization — for devnodes only
/// (they skip proof verification); real networks reject it.
#[staticmethod]
fn from_authorization_unproven(
fee_authorization: &Authorization,
state_root: &str,
) -> anyhow::Result<Self> {
use snarkvm::prelude::Network;
use std::str::FromStr;

let root = <crate::types::CurrentNetwork as Network>::StateRoot::from_str(state_root)?;
let native: crate::types::AuthorizationNative = fee_authorization.clone().into();
let transition = native
.transitions()
.values()
.next()
.cloned()
.ok_or_else(|| anyhow::anyhow!("Fee authorization has no transitions"))?;
FeeNative::from(transition, root, None).map(Self)
}

/// Reads in a Fee from a JSON string.
#[staticmethod]
fn from_json(json: &str) -> anyhow::Result<Self> {
Expand Down
3 changes: 3 additions & 0 deletions sdk/src/programs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
mod authorization;
pub use authorization::Authorization;

mod deployment;
pub use deployment::Deployment;

mod dynamic_record;
pub use dynamic_record::DynamicRecord;

Expand Down
27 changes: 24 additions & 3 deletions sdk/src/programs/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,16 @@

use crate::{
types::{CurrentAleo, ProcessNative},
Authorization, Execution, Fee, Field, Identifier, PrivateKey, Program, ProgramID, ProvingKey,
RecordPlaintext, Response, Trace, Value, VerifyingKey,
Address, Authorization, Deployment, Execution, Fee, Field, Identifier, PrivateKey, Program,
ProgramID, ProvingKey, RecordPlaintext, Response, Trace, Value, VerifyingKey,
};

use indexmap::IndexMap;
use pyo3::prelude::*;
use rand::rngs::StdRng;
use snarkvm::algorithms::snark::varuna::VarunaVersion;
use snarkvm::console::network::ConsensusVersion;
use snarkvm::synthesizer::process::{execution_cost, InclusionVersion};
use snarkvm::synthesizer::process::{deployment_cost, execution_cost, InclusionVersion};

/// The Aleo process type.
#[pyclass]
Expand Down Expand Up @@ -202,4 +202,25 @@ impl Process {
fn execution_cost(&self, execution: &Execution) -> anyhow::Result<(u64, (u64, u64))> {
execution_cost(&self.0, &execution.clone().into(), ConsensusVersion::V17)
}

/// Synthesizes a deployment for the given program (V9+ semantics: the
/// program checksum and owner address are set on the deployment).
///
/// The program's imports must already be present in this process. Key
/// synthesis is expensive — expect seconds to minutes for large programs.
fn deploy(&self, program: &Program, owner: &Address) -> anyhow::Result<Deployment> {
let mut deployment = self
.0
.deploy::<CurrentAleo, _>(program, &mut rand::make_rng::<StdRng>())?;
deployment.set_program_checksum_raw(Some(deployment.program().to_checksum()));
deployment.set_program_owner_raw(Some(**owner));
Ok(deployment.into())
}

/// Returns the *minimum* cost in microcredits to publish the given deployment.
fn deployment_cost(&self, deployment: &Deployment) -> anyhow::Result<u64> {
let (minimum_cost, _) =
deployment_cost(&self.0, deployment.as_ref(), ConsensusVersion::V17)?;
Ok(minimum_cost)
}
}
Loading
Loading