Pull-mirror of github.com/Sulkta-Coop/aldabra. Canonical lives on GitHub; this is a LAN-fast read-only cache.
Find a file
Cobb 7d59ceffd2 plutus spend: fix all 4 chain-level bugs surfaced in preprod audit
PLUTUS-1 (HIGH) — value-not-conserved on happy path. collateral isn't
consumed unless script fails, so total_in counted lovelace that wasn't
actually available for outputs. now picks a SEPARATE ada-only funding
utxo as a regular input alongside the locked utxo; collateral stays
collateral. error message tells callers to "split a UTXO first or top
up" if a second ada-only utxo isn't available.

PLUTUS-2 (HIGH) — collateral containing native assets. chain forbids
that; our picker grabbed largest-overall. now filters available_utxos
to assets.is_empty() before picking, errors clearly if no ada-only
utxo ≥ 5 ADA exists.

PLUTUS-3 (HIGH) — fee underestimation. plutus tx fees are
size_fee + exunits_fee. only size_fee was being charged. new
ProtocolParams::ex_units_fee() does ceil(mem * priceMem) +
ceil(steps * priceStep). conway-era prices in defaults
(577/10000 mem, 721/10_000_000 steps). fee jumps from ~0.17 ADA →
~1.7 ADA for the default ExUnits budget — matches what chain demanded.

PLUTUS-4 (LOW, becomes blocking under the others) — script_data_hash
not computed. pallas-txbuilder only computes the body hash field when
language_view is set on staging. plutus v3 path now calls
.language_view(version, cost_model) when the caller-supplied
ProtocolParams::plutus_v3_cost_model is Some. mcp wallet_script_spend
populates with the canonical preprod V3 cost model from
plutus_cost_models::PLUTUS_V3_COST_MODEL_PREPROD (297 i64 params,
fetched from koios epoch_params 2026-05). when ProtocolParams has no
cost model, we skip language_view and the chain rejects with
PPViewHashesDontMatch — explicit-failure mode, no silent shipping
of broken txs.

new tests:
- ex_units_fee_matches_known_values: 14M mem * 0.0577 + 10B steps *
  7.21e-5 ≈ 1.529 ADA ± ceil-rounding. locks the conway price math.
- rejects_when_no_funding_input_separate_from_collateral: catches
  the PLUTUS-1 single-utxo case.
- rejects_when_collateral_candidate_has_assets: PLUTUS-2 ada-only.

verified on preprod against a real script-locked utxo (the placeholder
script we locked 5 tADA at earlier). chain rejection went from 5
distinct errors to 1 (MalformedScriptWitnesses — expected, our
placeholder UPLC isn't valid). structural body shape now passes
every chain-rule check; only the script bytecode itself fails to
compile, which is a test-env limitation (no aiken in our toolchain
yet) not a wallet-code limitation.

97 unit tests pass. ProtocolParams gained 5 new fields + ex_units_fee
helper; went from Copy to Clone (cost_model is a Vec).
2026-05-04 17:27:47 -07:00
crates plutus spend: fix all 4 chain-level bugs surfaced in preprod audit 2026-05-04 17:27:47 -07:00
docs rename: sulkta-wallet → aldabra (per Cobb 2026-05-04) 2026-05-04 10:11:23 -07:00
.dockerignore phase 1: full read path — bip39 + cip-3 + cip-1852 + koios + age-mnemonic + rmcp 2026-05-04 11:09:00 -07:00
.gitignore phase 1 scaffold: cargo workspace + 3 crates + roadmap + architecture 2026-05-04 10:02:32 -07:00
Cargo.lock audit fixes: all 9 findings resolved + wallet generation tooling 2026-05-04 14:52:08 -07:00
Cargo.toml audit fixes: all 9 findings resolved + wallet generation tooling 2026-05-04 14:52:08 -07:00
Dockerfile phase 1: full read path — bip39 + cip-3 + cip-1852 + koios + age-mnemonic + rmcp 2026-05-04 11:09:00 -07:00
LICENSE phase 1 scaffold: cargo workspace + 3 crates + roadmap + architecture 2026-05-04 10:02:32 -07:00
README.md rename: sulkta-wallet → aldabra (per Cobb 2026-05-04) 2026-05-04 10:11:23 -07:00
ROADMAP.md rename: sulkta-wallet → aldabra (per Cobb 2026-05-04) 2026-05-04 10:11:23 -07:00

aldabra

Rust-native Cardano lite wallet with an MCP-server interface — built for LLM-first usage (send, receive, mint, Plutus interaction).

Status: Phase 1 scaffold (2026-05-04). Compiles, structure in place, real wallet primitives still landing. See ROADMAP.md.

Why

The existing Cardano MCP servers are either read-only doc gateways (Jimmyh-world/Cardano_MCP) or built on Blockfrost (web3-mcp) which is a centralized API we deliberately don't depend on. Sulkta runs its own Koios + Ogmios endpoints on Rackham; we want a wallet that talks directly to those.

Also: it's the first Sulkta Rust project — useful as a workout for crafting-table's Rust toolchain (per Sulkta-Coop/lucy-infra spec-crafting-table.md).

Architecture

Three crates in a Cargo workspace:

Crate Responsibility
aldabra-core Pure crypto + types. Mnemonic → root key (CIP-3), root → payment + stake key (CIP-1852), address construction, signing. No I/O, no network. This is the security boundary.
aldabra-chain Pluggable backends for chain queries. ChainBackend trait, with Koios as the phase-1 implementation. Ogmios + submission paths in phase 2.
aldabra-mcp Binary. MCP server speaking stdio. Glues core + chain together, exposes tools to the LLM client.
            ┌─────────────────────────────┐
LLM client  │       aldabra-mcp (bin)      │  stdio
─────────►  │   tool handlers, lifecycle  │  ────►
            └──────────┬──────────────────┘
                       │
              ┌────────┴────────┐
              ▼                 ▼
       ┌──────────────┐  ┌──────────────┐
       │ aldabra-core  │  │ aldabra-chain │
       │ keys, sign   │  │ Koios/Ogmios │
       └──────────────┘  └──────────────┘

MCP tools (target)

Phase 1:

  • wallet.address — derived base address at account 0, index 0
  • wallet.balance — ADA + native asset balance at the wallet's address
  • wallet.utxos — list UTXOs

Phase 2:

  • wallet.send — build, sign, submit a payment (ADA or native)
  • wallet.tx_status — poll a submitted tx hash

Phase 3:

  • wallet.mint — mint a CIP-25 / CIP-68 native asset
  • wallet.policy.create — generate a policy script (timelock, multisig)

Phase 4:

  • wallet.script.attach — attach an inline datum + reference script
  • wallet.script.spend — spend a Plutus-locked UTXO with redeemer
  • wallet.stake.delegate — delegate to a pool

Build

# Local (requires rustc 1.75+)
cargo build --release

# Through crafting-table (preferred — validates the toolchain there)
crafting-table build aldabra

Run

# Direct invocation (smoke test only — does nothing useful in phase 1)
./target/release/aldabra

# As an MCP server registered with Claude Code:
# add to ~/.claude.json:
#   "aldabra": {
#     "command": "/path/to/aldabra",
#     "env": {
#       "ALDABRA_DATA": "/mnt/cache/appdata/aldabra"
#     }
#   }

Security model

  • Mnemonic source: interactive bootstrap on first run, paste once, encrypted at rest with age. Never written to disk in plaintext.
  • Derived keys: in-memory only, ZeroizeOnDrop on every container.
  • Network exposure: stdio MCP transport — never opens a TCP socket. Only the spawning client process can reach it.
  • Multi-network: mainnet by default, but --network preview / --network preprod for testing without real ADA.

See also