Skip to content

Contracts

SDK reference · Soroban (live on testnet)

ShadowKit ships six Cargo crates. The contracts hold no developer trust: GovVault verifies proofs and exposes an approval gate; AgentPolicy is the on-chain lock that refuses any off-spec agent action.

Holds no funds. Stores sealed votes and exposes no tally before close. Its ProposalClosed event is what the agent watcher subscribes to.

Public entrypoints

EntrypointReturnsDescription
init(env, admin, verifier, merkle_root, treasury_asset, quorum_cfg)→ ()Initialize once. Quorum default: min_voters=3, yes_must_exceed_no=true.
create_proposal(env, action_spec, cap, deadline)→ u32New sequential proposal id. cap bounds the action amount; deadline is a ledger-time unix second.
cast_vote(env, id, proof, pub_signals, sealed_ciphertext)→ ()Verify proof, check nullifier + proposalId binding + merkle root, store the sealed vote. Exposes no tally.
close_and_reveal(env, id, revealed_yes_w, revealed_no_w, decryptions)→ ()After deadline: re-aggregate submitted decryptions against stored ciphertexts → Approved | Rejected.
is_approved(env, id)→ boolTrue iff status == Approved (read by AgentPolicy during auth).
cap_of(env, id) / action_of(env, id)→ i128 / ActionSpecApproved spending cap and the approved ActionSpec (read by AgentPolicy).
proposal(env, id)→ ProposalViewFull read model. weighted_yes/no are None until revealed.
votes_cast(env, id)→ u32Participation count (safe — no direction).
set_executor(env, executor)→ ()Admin-auth: configure the AgentPolicy address allowed to call mark_executed.
mark_executed(env, id)→ ()Single-shot replay guard; only callable by the configured executor. Sets status → Executed.

Selected errors (the negative-test surface)

GovErrorKindMeaning
NullifierUsed = 7errorDouble-vote: this nullifier was already cast.
WrongProposalId = 8errorProof/nullifier bound to a different proposalId (replay).
InvalidProof = 9errorGroth16 verify returned false.
StaleMerkleRoot = 10errorPublic merkleRoot signal != stored snapshot root.
DeadlinePassed = 5 / DeadlineNotReached = 6errorcast_vote after deadline / close before deadline.
RevealMismatch = 13errorSubmitted decryptions inconsistent with the committed ciphertexts.
NotApproved = 15errorAction requires an Approved proposal.

Stateless BLS12-381 Groth16 verifier; the verification key is compiled in. The convenience verify(proof, pub_signals) loads the embedded VK and checks the proof.

// pub_signals order is BINDING:
// [merkleRoot, nullifier, proposalId, sealedCommitmentHash]
pub fn verify(env: Env, proof: Proof, pub_signals: Vec<Fr>) -> bool;

An OpenZeppelin smart-account custom policy (crate stellar-accounts, git tag v0.8.0-rc.1 — soroban-sdk 26). The treasury is this smart-account wallet. Its enforce() cross-reads GovVault and authorizes a swap only if every gate below passes — otherwise it panics with a typed PolicyError.

The enforce() gates

GateReject codeRule
(a) approvedNotApproved = 2GovVault.is_approved(proposal_id) must be true.
(b) not executedAlreadyExecuted = 3Proposal must not already be Executed (single-shot).
(c) targetWrongTarget = 4Context contract must equal approved_amm.
(d) asset_inWrongAsset = 5asset_in must equal treasury_asset AND action.asset_in.
(e) capOverCap = 6amount_in must be ≤ GovVault.cap_of(proposal_id).
(f) asset_outWrongAssetOut = 10asset_out must equal action.asset_out (no rerouting to an unapproved token).
single contextMultiCall = 8More than one Contract context in the auth batch is rejected.

FallbackAMM & SwapVenue — fallback-amm / swap-venue

Section titled “FallbackAMM & SwapVenue — fallback-amm / swap-venue”

SwapVenue is the venue-agnostic interface (swap + reserves) that every venue satisfies; AgentPolicy only ever authorizes swap on the configured venue. FallbackAMM is a constant-product USDC/XLM pool guaranteeing demo liquidity.

EntrypointReturnsDescription
init(env, asset_a, asset_b)→ ()Set the two pool assets (once).
add_liquidity(env, from, amount_a, amount_b)→ ()Deposit liquidity (from must auth); updates reserves.
swap(env, asset_in, amount_in, min_out, to)→ i128Constant-product (x·y=k) swap, 0.3% fee. Reverts SlippageExceeded if out < min_out. Implements SwapVenue.
reserves(env)→ (i128, i128)Current (reserve_a, reserve_b). Implements SwapVenue.