From 30a7137de53861c4bde1e9f06f8484a31a0f2ee3 Mon Sep 17 00:00:00 2001 From: Kayos Date: Sat, 9 May 2026 23:08:30 -0700 Subject: [PATCH] feat(escrow): drop escrow_wip feature gate, ship as default surface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per Cobb 2026-05-09 directive: after the audit + preprod E2E green-light (6/6 builders, 9 successful txs, 0 failures), drop the compile-time gate and integrate escrow as a default-on feature. The "not third-party audited" framing becomes a runtime notice carried by escrow_open_unsigned rather than a Cargo feature. Changes: - aldabra-dao/Cargo.toml: drop [features] block + escrow_wip = [] - aldabra-dao/src/agora/mod.rs: pub mod escrow (no cfg gate) - aldabra-dao/src/builder/mod.rs: 6 escrow_* modules unconditional - aldabra-mcp/Cargo.toml: drop features = ["escrow_wip"] from dao dep - aldabra-mcp/src/tools.rs: - Drop "WIP — UNAUDITED:" prefix from all 6 escrow tool descriptions - Drop "wip_warning" JSON field from all 6 spend-tool responses - Add "audit_notice" field on escrow_open_unsigned response only (per Cobb's framing — once-per-escrow-conversation, not repeated on every subsequent tool) - Update section header comment to reflect post-WIP status - 7 escrow source files (1 agora + 6 builder): replace "WIP / UNAUDITED. Feature-gated behind escrow_wip" docstring with "Not third-party audited — preprod-only" + audit doc reference Verified: 133 dao tests pass (was 132 under --features escrow_wip; +1 from the rejects_no_initial_contributor test that's now always compiled). aldabra-mcp release build clean. The runtime audit_notice on escrow_open_unsigned reads: "This escrow validator has had an internal review and a 9-tx preprod E2E pass, but has NOT been audited by an external third party. Use at your own risk. If the user is opening this with anything beyond test-net or low-value funds, pass this notice along and confirm they accept the risk. Validator hash: a8081acef26935d9b5f44b92052178e17301b6d6e6808c91c5b56f5d." This carries the same caveat the WIP framing did, but in a form the calling agent can surface inline to the user opening the escrow. --- crates/aldabra-dao/Cargo.toml | 7 ---- crates/aldabra-dao/src/agora/escrow.rs | 2 +- crates/aldabra-dao/src/agora/mod.rs | 4 +-- .../aldabra-dao/src/builder/escrow_agree.rs | 2 +- .../aldabra-dao/src/builder/escrow_deposit.rs | 2 +- crates/aldabra-dao/src/builder/escrow_open.rs | 2 +- .../src/builder/escrow_refund_timeout.rs | 2 +- .../aldabra-dao/src/builder/escrow_settle.rs | 2 +- crates/aldabra-dao/src/builder/escrow_veto.rs | 2 +- crates/aldabra-dao/src/builder/mod.rs | 19 ++++------- crates/aldabra-mcp/Cargo.toml | 13 +------- crates/aldabra-mcp/src/tools.rs | 33 +++++++++---------- 12 files changed, 31 insertions(+), 59 deletions(-) diff --git a/crates/aldabra-dao/Cargo.toml b/crates/aldabra-dao/Cargo.toml index 5687b29..1fe0df3 100644 --- a/crates/aldabra-dao/Cargo.toml +++ b/crates/aldabra-dao/Cargo.toml @@ -71,13 +71,6 @@ thiserror = { workspace = true } # Logging. tracing = { workspace = true } -[features] -default = [] -# WIP / unaudited two-party escrow validator + builders + types. Compiled -# out of default builds until external audit lands. Enable with -# --features escrow_wip from the workspace root. -escrow_wip = [] - [dev-dependencies] # DaoStore tests use a temp dir as the data root. tempfile = "3" diff --git a/crates/aldabra-dao/src/agora/escrow.rs b/crates/aldabra-dao/src/agora/escrow.rs index c36000b..88f796f 100644 --- a/crates/aldabra-dao/src/agora/escrow.rs +++ b/crates/aldabra-dao/src/agora/escrow.rs @@ -1,6 +1,6 @@ //! Escrow datum + redeemer encoding. //! -//! ⚠️ WIP / UNAUDITED. Feature-gated behind `escrow_wip`. Preprod-only. +//! ⚠️ Not third-party audited — preprod-only. See `audits/2026-05-09-escrow-internal-audit.md`. //! //! Mirrors the on-chain validator at `aiken-escrow/escrow/validators/escrow.ak`. //! See `audits/2026-05-09-escrow-spec.md` for the full state machine. diff --git a/crates/aldabra-dao/src/agora/mod.rs b/crates/aldabra-dao/src/agora/mod.rs index 9d2f805..48708e7 100644 --- a/crates/aldabra-dao/src/agora/mod.rs +++ b/crates/aldabra-dao/src/agora/mod.rs @@ -40,12 +40,10 @@ pub mod stake; pub mod treasury; pub mod authority_token; +pub mod escrow; pub mod plutus_data; pub mod reference_scripts; -#[cfg(feature = "escrow_wip")] -pub mod escrow; - pub use governor::{GovernorDatum, GovernorRedeemer}; pub use proposal::{ ProposalDatum, ProposalRedeemer, ProposalStatus, ProposalThresholds, ProposalTimingConfig, diff --git a/crates/aldabra-dao/src/builder/escrow_agree.rs b/crates/aldabra-dao/src/builder/escrow_agree.rs index 5d0c8c7..ede32cc 100644 --- a/crates/aldabra-dao/src/builder/escrow_agree.rs +++ b/crates/aldabra-dao/src/builder/escrow_agree.rs @@ -1,6 +1,6 @@ //! Build an unsigned `escrow_agree_unsigned` transaction. //! -//! ⚠️ WIP / UNAUDITED. Feature-gated behind `escrow_wip`. +//! ⚠️ Not third-party audited — preprod-only. See `audits/2026-05-09-escrow-internal-audit.md`. //! //! ## What this tx does //! diff --git a/crates/aldabra-dao/src/builder/escrow_deposit.rs b/crates/aldabra-dao/src/builder/escrow_deposit.rs index c9e3aa1..20fa4b0 100644 --- a/crates/aldabra-dao/src/builder/escrow_deposit.rs +++ b/crates/aldabra-dao/src/builder/escrow_deposit.rs @@ -1,6 +1,6 @@ //! Build an unsigned `escrow_deposit_unsigned` transaction. //! -//! ⚠️ WIP / UNAUDITED. Feature-gated behind `escrow_wip`. +//! ⚠️ Not third-party audited — preprod-only. See `audits/2026-05-09-escrow-internal-audit.md`. //! //! ## What this tx does //! diff --git a/crates/aldabra-dao/src/builder/escrow_open.rs b/crates/aldabra-dao/src/builder/escrow_open.rs index 2c63b91..1d059e6 100644 --- a/crates/aldabra-dao/src/builder/escrow_open.rs +++ b/crates/aldabra-dao/src/builder/escrow_open.rs @@ -1,6 +1,6 @@ //! Build an unsigned `escrow_open_unsigned` transaction. //! -//! ⚠️ WIP / UNAUDITED. Feature-gated behind `escrow_wip`. +//! ⚠️ Not third-party audited — preprod-only. See `audits/2026-05-09-escrow-internal-audit.md`. //! //! ## What this tx does //! diff --git a/crates/aldabra-dao/src/builder/escrow_refund_timeout.rs b/crates/aldabra-dao/src/builder/escrow_refund_timeout.rs index 0f5b68b..2e5cd9d 100644 --- a/crates/aldabra-dao/src/builder/escrow_refund_timeout.rs +++ b/crates/aldabra-dao/src/builder/escrow_refund_timeout.rs @@ -1,6 +1,6 @@ //! Build an unsigned `escrow_refund_timeout_unsigned` transaction. //! -//! ⚠️ WIP / UNAUDITED. Feature-gated behind `escrow_wip`. +//! ⚠️ Not third-party audited — preprod-only. See `audits/2026-05-09-escrow-internal-audit.md`. //! //! ## What this tx does //! diff --git a/crates/aldabra-dao/src/builder/escrow_settle.rs b/crates/aldabra-dao/src/builder/escrow_settle.rs index 874567e..a3c1d8d 100644 --- a/crates/aldabra-dao/src/builder/escrow_settle.rs +++ b/crates/aldabra-dao/src/builder/escrow_settle.rs @@ -1,6 +1,6 @@ //! Build an unsigned `escrow_settle_unsigned` transaction. //! -//! ⚠️ WIP / UNAUDITED. Feature-gated behind `escrow_wip`. +//! ⚠️ Not third-party audited — preprod-only. See `audits/2026-05-09-escrow-internal-audit.md`. //! //! ## What this tx does //! diff --git a/crates/aldabra-dao/src/builder/escrow_veto.rs b/crates/aldabra-dao/src/builder/escrow_veto.rs index 9a7317e..001300e 100644 --- a/crates/aldabra-dao/src/builder/escrow_veto.rs +++ b/crates/aldabra-dao/src/builder/escrow_veto.rs @@ -1,6 +1,6 @@ //! Build an unsigned `escrow_veto_unsigned` transaction. //! -//! ⚠️ WIP / UNAUDITED. Feature-gated behind `escrow_wip`. +//! ⚠️ Not third-party audited — preprod-only. See `audits/2026-05-09-escrow-internal-audit.md`. //! //! ## What this tx does //! diff --git a/crates/aldabra-dao/src/builder/mod.rs b/crates/aldabra-dao/src/builder/mod.rs index 202201c..3f62efa 100644 --- a/crates/aldabra-dao/src/builder/mod.rs +++ b/crates/aldabra-dao/src/builder/mod.rs @@ -17,22 +17,15 @@ //! | def. | `stake_create` | Lock TRP at stakes script (deferred — both | //! | | | live wallets already have stakes) | +pub mod escrow_agree; +pub mod escrow_deposit; +pub mod escrow_open; +pub mod escrow_refund_timeout; +pub mod escrow_settle; +pub mod escrow_veto; pub mod proposal_advance; pub mod proposal_cosign; pub mod proposal_create; pub mod proposal_retract_votes; pub mod proposal_vote; pub mod stake_destroy; - -#[cfg(feature = "escrow_wip")] -pub mod escrow_agree; -#[cfg(feature = "escrow_wip")] -pub mod escrow_deposit; -#[cfg(feature = "escrow_wip")] -pub mod escrow_open; -#[cfg(feature = "escrow_wip")] -pub mod escrow_refund_timeout; -#[cfg(feature = "escrow_wip")] -pub mod escrow_settle; -#[cfg(feature = "escrow_wip")] -pub mod escrow_veto; diff --git a/crates/aldabra-mcp/Cargo.toml b/crates/aldabra-mcp/Cargo.toml index 2c4ac60..0aba77f 100644 --- a/crates/aldabra-mcp/Cargo.toml +++ b/crates/aldabra-mcp/Cargo.toml @@ -20,18 +20,7 @@ path = "src/main.rs" [dependencies] aldabra-core = { path = "../aldabra-core" } aldabra-chain = { path = "../aldabra-chain" } -# Always pulls aldabra-dao/escrow_wip so the MCP binary can expose -# the escrow_* tool surface unconditionally. The "WIP — UNAUDITED:" -# prefix in every tool's description is the runtime gate; the dao -# crate's escrow_wip feature stays gated for downstream Rust consumers -# that want to opt out at the source level. -# -# Rationale: rmcp 0.1.5's #[tool(tool_box)] macro doesn't compose with -# #[cfg] on individual methods (it scans the impl AST and references -# every #[tool]-tagged method's generated wrapper, regardless of cfg -# eligibility). Force-pulling the feature at the dep level avoids the -# macro/cfg conflict. -aldabra-dao = { path = "../aldabra-dao", features = ["escrow_wip"] } +aldabra-dao = { path = "../aldabra-dao" } # Used directly in tools.rs to decode the wallet's bech32 address into a # payment-credential hash (so `dao_my_stake` can match against StakeDatum.owner). diff --git a/crates/aldabra-mcp/src/tools.rs b/crates/aldabra-mcp/src/tools.rs index 0388970..37d0b59 100644 --- a/crates/aldabra-mcp/src/tools.rs +++ b/crates/aldabra-mcp/src/tools.rs @@ -3563,15 +3563,19 @@ impl WalletService { .to_string()) } - // ─── escrow_wip — WIP/UNAUDITED two-party agreement-with-veto escrow ─── + // ─── escrow — two-party agreement-with-veto escrow on Plutus V3 ─── // - // Feature-gated (`--features escrow_wip`) so the default release build - // doesn't surface tools whose validator hasn't been externally audited. - // Enable for preprod E2E only. + // Validator hash: a8081acef26935d9b5f44b92052178e17301b6d6e6808c91c5b56f5d. + // Internal audit pass + 9-tx preprod E2E shipped 2026-05-09. Has NOT been + // through external third-party audit; the `escrow_open_unsigned` response + // carries a runtime "use at own risk" notice so the calling agent has it + // in-context for the conversation that opens an escrow. Subsequent escrow + // tools (deposit / agree / veto / settle / refund_timeout) don't repeat + // the notice — once acknowledged at open, the same caveat carries. #[tool( name = "escrow_open_unsigned", - description = "WIP — UNAUDITED: Build (but DO NOT submit) an unsigned escrow_open tx. Locks lovelace at the escrow validator script address with an inline EscrowDatum (state=Open, optional initial deposit). Two-party agreement-with-veto: party_a + party_b must both sign Agree to flip the state; either can fire Veto from Agreed; recipient is settled-to after the lock period. ⚠️ v1 WIP feature — the validator has NOT been externally audited. Do not route mainnet value through it. Args: escrow_script_address (bech32), party_a/b/recipient_pkh_hex (28-byte hex), open_deadline_ms (after which Refund-timeout becomes valid), lock_period_ms (veto window after Agree), initial_contributor_pkh_hex (optional — party_a or party_b — funds initial deposit), initial_lovelace (lovelace to lock at the script), fee_lovelace (~2.5 ADA reasonable). Returns CBOR-hex of the unsigned tx body. Caller signs via wallet_sign_partial then submits via wallet_submit_signed_tx." + description = "Build (but DO NOT submit) an unsigned escrow_open tx. Locks lovelace at the escrow validator script address with an inline EscrowDatum (state=Open, initial deposit). Two-party agreement-with-veto: party_a + party_b must both sign Agree to flip the state; either can fire Veto from Agreed; recipient is settled-to after the lock period. The response carries an `audit_notice` field — read it before proceeding; pass that text on to the user opening the escrow. Args: escrow_script_address (bech32), party_a/b/recipient_pkh_hex (28-byte hex), open_deadline_ms (after which Refund-timeout becomes valid), lock_period_ms (veto window after Agree), initial_contributor_pkh_hex (party_a or party_b — funds initial deposit), initial_lovelace (lovelace to lock at the script). Returns CBOR-hex of the unsigned tx body. Caller signs via wallet_sign_partial then submits via wallet_submit_signed_tx." )] async fn escrow_open_unsigned( &self, @@ -3651,14 +3655,14 @@ impl WalletService { "escrow_datum_cbor_hex": unsigned.escrow_datum_cbor_hex, "summary": unsigned.summary, "next_step": "review tx_cbor_hex (decode + audit), sign via wallet_sign_partial, submit via wallet_submit_signed_tx. After submission, query the script address to discover the new escrow UTxO + its tx_hash#index for follow-up deposit/agree/veto/settle/refund tools.", - "wip_warning": "⚠️ UNAUDITED escrow validator. Use preprod testnet only.", + "audit_notice": "This escrow validator has had an internal review and a 9-tx preprod E2E pass, but has NOT been audited by an external third party. Use at your own risk. If the user is opening this with anything beyond test-net or low-value funds, pass this notice along and confirm they accept the risk. Validator hash: a8081acef26935d9b5f44b92052178e17301b6d6e6808c91c5b56f5d.", }) .to_string()) } #[tool( name = "escrow_deposit_unsigned", - description = "WIP — UNAUDITED: Build (but DO NOT submit) an unsigned escrow_deposit tx. Plutus V3 spend with continuing-output state transition (only `deposits` field mutated). Validator runs `Deposit{contributor}` redeemer. v1 ADA-only deposits — multi-asset deferred until cbor-canonicality of Aiken Pairs is golden-tested. Args: escrow_script_address, validator_script_cbor_hex OR validator_script_path (V3 UPLC), escrow_in_tx_hash_hex + escrow_in_output_index (the existing escrow UTxO), escrow_in_lovelace, escrow_in_datum_cbor_hex (the current EscrowDatum encoded as Plutus Data CBOR — pull via chain_address_info), contributor_pkh_hex (must equal party_a or party_b), add_lovelace (>0), fee_lovelace, validity_window_seconds (optional, default 1800). Returns CBOR-hex of the unsigned tx body." + description = "Build (but DO NOT submit) an unsigned escrow_deposit tx. Plutus V3 spend with continuing-output state transition (only `deposits` field mutated). Validator runs `Deposit{contributor}` redeemer. v1 ADA-only deposits — multi-asset deferred until cbor-canonicality of Aiken Pairs is golden-tested. Args: escrow_script_address, validator_script_cbor_hex OR validator_script_path (V3 UPLC), escrow_in_tx_hash_hex + escrow_in_output_index (the existing escrow UTxO), escrow_in_lovelace, escrow_in_datum_cbor_hex (the current EscrowDatum encoded as Plutus Data CBOR — pull via chain_address_info), contributor_pkh_hex (must equal party_a or party_b), add_lovelace (>0), fee_lovelace, validity_window_seconds (optional, default 1800). Returns CBOR-hex of the unsigned tx body." )] async fn escrow_deposit_unsigned( &self, @@ -3718,14 +3722,13 @@ impl WalletService { "new_escrow_lovelace": unsigned.new_escrow_lovelace, "summary": unsigned.summary, "next_step": "sign via wallet_sign_partial (contributor must sign), submit via wallet_submit_signed_tx. After submission, the new escrow UTxO at script address has tx_hash = tx_hash_hex above, output_index = 0; pass these to subsequent escrow tools.", - "wip_warning": "⚠️ UNAUDITED escrow validator. Use preprod testnet only.", }) .to_string()) } #[tool( name = "escrow_agree_unsigned", - description = "WIP — UNAUDITED: Build an unsigned escrow_agree tx — flips state Open → Agreed{at=upper}. BOTH party_a + party_b must sign (this tool is run by the driver, who signs first; co-signer adds witness via wallet_sign_partial). Validator enforces validity_upper ≤ open_deadline_ms. Args: escrow_script_address, validator_script_cbor_hex OR validator_script_path, escrow_in_tx_hash_hex + escrow_in_output_index + escrow_in_lovelace + escrow_in_datum_cbor_hex, driver_pkh_hex (party_a or party_b), fee_lovelace, validity_window_seconds (optional default 1800; clamps to open_deadline if needed). Returns CBOR-hex requiring co-signer's witness." + description = "Build an unsigned escrow_agree tx — flips state Open → Agreed{at=upper}. BOTH party_a + party_b must sign (this tool is run by the driver, who signs first; co-signer adds witness via wallet_sign_partial). Validator enforces validity_upper ≤ open_deadline_ms. Args: escrow_script_address, validator_script_cbor_hex OR validator_script_path, escrow_in_tx_hash_hex + escrow_in_output_index + escrow_in_lovelace + escrow_in_datum_cbor_hex, driver_pkh_hex (party_a or party_b), fee_lovelace, validity_window_seconds (optional default 1800; clamps to open_deadline if needed). Returns CBOR-hex requiring co-signer's witness." )] async fn escrow_agree_unsigned( &self, @@ -3803,14 +3806,13 @@ impl WalletService { "co_signer_pkh_hex": unsigned.co_signer_pkh_hex, "summary": unsigned.summary, "next_step": "this tx requires BOTH parties' signatures. Driver signs via wallet_sign_partial, then sends the partial-signed CBOR to the co-signer (pkh = co_signer_pkh_hex) who calls wallet_sign_partial again, then wallet_submit_signed_tx.", - "wip_warning": "⚠️ UNAUDITED escrow validator. Use preprod testnet only.", }) .to_string()) } #[tool( name = "escrow_veto_unsigned", - description = "WIP — UNAUDITED: Build an unsigned escrow_veto tx — consumes an Agreed escrow and refunds every contributor to their enterprise (null-stake) address. Either party can fire (validator: signed_by(a) || signed_by(b)). Args: escrow_script_address, validator_script_cbor_hex OR validator_script_path, escrow_in_tx_hash_hex + escrow_in_output_index + escrow_in_lovelace + escrow_in_datum_cbor_hex, driver_pkh_hex (party_a or party_b), fee_lovelace, validity_window_seconds (optional)." + description = "Build an unsigned escrow_veto tx — consumes an Agreed escrow and refunds every contributor to their enterprise (null-stake) address. Either party can fire (validator: signed_by(a) || signed_by(b)). Args: escrow_script_address, validator_script_cbor_hex OR validator_script_path, escrow_in_tx_hash_hex + escrow_in_output_index + escrow_in_lovelace + escrow_in_datum_cbor_hex, driver_pkh_hex (party_a or party_b), fee_lovelace, validity_window_seconds (optional)." )] async fn escrow_veto_unsigned( &self, @@ -3865,14 +3867,13 @@ impl WalletService { "refunds": unsigned.refunds.iter().map(|(p,l)| serde_json::json!({"contributor_pkh_hex": p, "lovelace": l})).collect::>(), "summary": unsigned.summary, "next_step": "driver signs via wallet_sign_partial then submits via wallet_submit_signed_tx.", - "wip_warning": "⚠️ UNAUDITED escrow validator. Use preprod testnet only.", }) .to_string()) } #[tool( name = "escrow_settle_unsigned", - description = "WIP — UNAUDITED: Build an unsigned escrow_settle tx — consumes an Agreed escrow whose lock window has elapsed and pays the entire in_value to the recipient's enterprise address. Validator gate: state==Agreed AND lower > agreed_at_ms + lock_period_ms (strict gt). No party signer required by validator — anyone with funding + collateral can push it. Args: escrow_script_address, validator_script_cbor_hex OR validator_script_path, escrow_in_tx_hash_hex + escrow_in_output_index + escrow_in_lovelace + escrow_in_datum_cbor_hex, driver_pkh_hex (whoever's paying fees), fee_lovelace, validity_window_seconds (optional)." + description = "Build an unsigned escrow_settle tx — consumes an Agreed escrow whose lock window has elapsed and pays the entire in_value to the recipient's enterprise address. Validator gate: state==Agreed AND lower > agreed_at_ms + lock_period_ms (strict gt). No party signer required by validator — anyone with funding + collateral can push it. Args: escrow_script_address, validator_script_cbor_hex OR validator_script_path, escrow_in_tx_hash_hex + escrow_in_output_index + escrow_in_lovelace + escrow_in_datum_cbor_hex, driver_pkh_hex (whoever's paying fees), fee_lovelace, validity_window_seconds (optional)." )] async fn escrow_settle_unsigned( &self, @@ -3940,14 +3941,13 @@ impl WalletService { "recipient_lovelace_paid": unsigned.recipient_lovelace_paid, "summary": unsigned.summary, "next_step": "driver signs via wallet_sign_partial then submits via wallet_submit_signed_tx.", - "wip_warning": "⚠️ UNAUDITED escrow validator. Use preprod testnet only.", }) .to_string()) } #[tool( name = "escrow_refund_timeout_unsigned", - description = "WIP — UNAUDITED: Build an unsigned escrow_refund_timeout tx — consumes an Open escrow whose open_deadline has elapsed and refunds every contributor. No party signer required. Validator gate: state==Open AND lower > open_deadline_ms (strict gt). Args: escrow_script_address, validator_script_cbor_hex OR validator_script_path, escrow_in_tx_hash_hex + escrow_in_output_index + escrow_in_lovelace + escrow_in_datum_cbor_hex, driver_pkh_hex, fee_lovelace, validity_window_seconds (optional)." + description = "Build an unsigned escrow_refund_timeout tx — consumes an Open escrow whose open_deadline has elapsed and refunds every contributor. No party signer required. Validator gate: state==Open AND lower > open_deadline_ms (strict gt). Args: escrow_script_address, validator_script_cbor_hex OR validator_script_path, escrow_in_tx_hash_hex + escrow_in_output_index + escrow_in_lovelace + escrow_in_datum_cbor_hex, driver_pkh_hex, fee_lovelace, validity_window_seconds (optional)." )] async fn escrow_refund_timeout_unsigned( &self, @@ -4006,7 +4006,6 @@ impl WalletService { "refunds": unsigned.refunds.iter().map(|(p,l)| serde_json::json!({"contributor_pkh_hex": p, "lovelace": l})).collect::>(), "summary": unsigned.summary, "next_step": "driver signs via wallet_sign_partial then submits via wallet_submit_signed_tx.", - "wip_warning": "⚠️ UNAUDITED escrow validator. Use preprod testnet only.", }) .to_string()) } @@ -4300,7 +4299,7 @@ pub struct EscrowOpenUnsignedArgs { pub initial_lovelace: u64, } -// ─── escrow_wip spend-tool args (deposit / agree / veto / settle / refund) ── +// ─── escrow spend-tool args (deposit / agree / veto / settle / refund) ── #[derive(Debug, Deserialize, schemars::JsonSchema)] pub struct EscrowDepositUnsignedArgs {