aldabra/aiken-escrow
Kayos 78ed92304e feat(escrow_wip): aiken validator + plutus.json blueprint
⚠ WIP — UNAUDITED. Plutus V3, Aiken v1.1.21. Preprod-only.

Five-redeemer two-party agreement-with-veto escrow validator. Mirrors the
off-chain codecs at crates/aldabra-dao/src/agora/escrow.rs.

Validator script hash: 223aa7ace4a98ff5b8f8988c1c07b846c046de1a2bc9e8dc77411486
Compiled UPLC size: 7902 bytes.

Datum: ProductIsData (Constr 0 [a, b, recipient, deadline, lock, state, deposits]).
Redeemer: Constr 0..4 (Deposit | Agree | Veto | Settle | Refund).
DepositEntry.value uses concrete Pairs<ByteArray, Pairs<ByteArray, Int>>
since cardano/assets.Value is opaque (datums require concrete types).
Pairs encode as Plutus Map at the dataType layer — matches the off-chain
EscrowValue codec's PlutusData::Map(KeyValuePairs) emission.

Build: cd aiken-escrow && aiken build (produces plutus.json blueprint).

Threat-model gaps explicitly documented in aiken-escrow/README.md:
- CBOR canonicality of Pairs serialisation (validator equality check)
- Stake-credential null'd on refund outputs (intentional — protects pool
  delegation privacy at cost of stake reward routing)
- No min-utxo enforcement on refund legs (off-chain builder's job)
- No multi-script-input cross-UTxO consistency

External audit gates mainnet deployment.
2026-05-09 11:38:45 -07:00
..
validators feat(escrow_wip): aiken validator + plutus.json blueprint 2026-05-09 11:38:45 -07:00
.gitignore feat(escrow_wip): aiken validator + plutus.json blueprint 2026-05-09 11:38:45 -07:00
aiken.toml feat(escrow_wip): aiken validator + plutus.json blueprint 2026-05-09 11:38:45 -07:00
plutus.json feat(escrow_wip): aiken validator + plutus.json blueprint 2026-05-09 11:38:45 -07:00
README.md feat(escrow_wip): aiken validator + plutus.json blueprint 2026-05-09 11:38:45 -07:00

aiken-escrow

⚠️ WIP — UNAUDITED. Preprod testing only. Do NOT route mainnet funds through this validator. No third-party security review has been performed.

Two-party agreement-with-veto escrow validator (Plutus V3, Aiken v1.1.21). The off-chain (Rust) side lives in crates/aldabra-dao behind the escrow_wip feature flag.

Spec

audits/2026-05-09-escrow-spec.md documents the state machine, datum shape, and redeemer invariants.

State machine:

   Open ──(both sign Agree)──▶ Agreed{at} ──(lock_period elapsed, no veto)──▶ Settle (→ recipient)
     │                              │
     │                              └──(A or B fires Veto)─────────────▶ Refund (per-contributor)
     │
     └──(open_deadline passed, no agreement)─────────────────────────▶ Refund (per-contributor)

Build

cd aiken-escrow
aiken check       # type check + tests
aiken build       # produces plutus.json blueprint

The blueprint at plutus.json is consumed by aldabra's escrow builders to construct script addresses + spending witnesses.

Threat model (out-of-scope for v1)

These are KNOWN gaps the validator does not protect against. They inform the WIP designation:

  • Datum CBOR canonicality. The Deposit redeemer compares cbor.serialise(expected) == cbor.serialise(new.deposits). If the Aiken stdlib's CBOR encoder is non-canonical for any input shape (e.g. map ordering), an attacker could submit a continuing output with the same logical content but byte-different and bypass the check. We mitigate by using List<Deposit> (not Map) which has deterministic order, but external review should re-confirm.
  • Stake credential preservation on refund outputs. Refund outputs are derived from contributor PKHs as null-stake base addresses. If a contributor's wallet uses a custom stake credential, refund value bypasses their stake-delegation pool. Acceptable v1 tradeoff; documented in spec.
  • Min-utxo per refund leg. Validator does not enforce min-utxo per refund output — assumes the off-chain builder has already ensured each deposit cleared min-utxo at deposit time. A pathological multi-asset deposit that splits below min-utxo on refund would brick the escrow until manual recovery.
  • Multi-script-input attack. If a single tx spends multiple escrow UTxOs simultaneously with overlapping signers, the per-UTxO validator runs independently. Cross-UTxO consistency is not enforced.

Status

  • Validator compiles (aiken build produces plutus.json).
  • Off-chain codecs in aldabra-dao::agora::escrow.
  • Off-chain unsigned-tx builders (5 paths).
  • MCP tool wrappers.
  • Preprod E2E (open → both deposit → agree → settle).
  • Preprod E2E (open → agree → veto).
  • Preprod E2E (open → refund-timeout).
  • External audit.
  • Mainnet release gate.