AUDIT4-3 fix: optional inline datum on wallet_send
wallet_send + wallet_send_unsigned now accept an optional datum_inline_cbor_hex field. When set, the recipient output carries the bytes as an inline datum — the right shape for locking funds at a script address with a datum the validator can read. Without this, sends to script addresses created un-spendable utxos (Babbage/Conway rejects spending script utxos that don't carry a datum). Surfaced 2026-05-04 audit-4 phase F2 when the always-succeeds Aiken validator's locked utxo couldn't be spent back due to NotAllowedSupplementalDatums + PPViewHashesDontMatch chain errors. Plumbed through: build_signed_payment_with_assets (added arg) build_unsigned_payment_with_assets (added arg) prepare_payment (added arg) build_staging_with_fee (added arg) output_with_assets (added arg) SendArgs / UnsignedSendArgs (new optional MCP field) Change outputs never get a datum — they go back to the wallet which has no validator to satisfy, so the field is wired only to the recipient output. Test lock_with_inline_datum_attaches_datum_to_output decodes the resulting tx CBOR and confirms the recipient output's datum_option is populated. Unblocks mainnet Plutus testing — the spend round trip can now build a lock that the spend side can satisfy.
This commit is contained in:
parent
2ff007ccaf
commit
0777e9e91d
2 changed files with 107 additions and 1 deletions
|
|
@ -123,6 +123,14 @@ pub struct SendArgs {
|
|||
/// (hex of raw bytes, 0-64 chars) + quantity.
|
||||
#[serde(default)]
|
||||
pub assets: Vec<McpAssetSpec>,
|
||||
/// Optional inline-datum CBOR (hex). When set, the recipient
|
||||
/// output carries the bytes as an inline datum — the right
|
||||
/// shape for locking funds at a script address with a datum
|
||||
/// the validator can read. Required for any spend-back path
|
||||
/// against Babbage/Conway-era validators (script utxos without
|
||||
/// a datum are un-spendable). Omit for normal sends.
|
||||
#[serde(default)]
|
||||
pub datum_inline_cbor_hex: Option<String>,
|
||||
/// Bypass the configured `max_send_lovelace` hard cap. Only
|
||||
/// pass `true` for an intentional, user-confirmed large send.
|
||||
#[serde(default)]
|
||||
|
|
@ -144,6 +152,9 @@ pub struct UnsignedSendArgs {
|
|||
/// Optional native assets to include in the payment output.
|
||||
#[serde(default)]
|
||||
pub assets: Vec<McpAssetSpec>,
|
||||
/// Optional inline-datum CBOR (hex). See [`SendArgs::datum_inline_cbor_hex`].
|
||||
#[serde(default)]
|
||||
pub datum_inline_cbor_hex: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, schemars::JsonSchema)]
|
||||
|
|
@ -411,6 +422,7 @@ impl WalletService {
|
|||
to_address,
|
||||
lovelace,
|
||||
assets,
|
||||
datum_inline_cbor_hex,
|
||||
force,
|
||||
}: SendArgs,
|
||||
) -> Result<String, String> {
|
||||
|
|
@ -456,6 +468,10 @@ impl WalletService {
|
|||
})
|
||||
.collect();
|
||||
let asset_specs: Vec<AssetSpec> = assets.into_iter().map(Into::into).collect();
|
||||
let datum_bytes = match datum_inline_cbor_hex.as_deref() {
|
||||
Some(s) => Some(hex_decode(s).map_err(|e| format!("decode datum: {e}"))?),
|
||||
None => None,
|
||||
};
|
||||
|
||||
let cbor = build_signed_payment_with_assets(
|
||||
&self.inner.payment_key,
|
||||
|
|
@ -465,6 +481,7 @@ impl WalletService {
|
|||
&to_address,
|
||||
lovelace,
|
||||
&asset_specs,
|
||||
datum_bytes.as_deref(),
|
||||
&ProtocolParams::default(),
|
||||
)
|
||||
.map_err(|e| format!("build/sign: {e}"))?;
|
||||
|
|
@ -505,6 +522,7 @@ impl WalletService {
|
|||
to_address,
|
||||
lovelace,
|
||||
assets,
|
||||
datum_inline_cbor_hex,
|
||||
}: UnsignedSendArgs,
|
||||
) -> Result<String, String> {
|
||||
if lovelace == 0 {
|
||||
|
|
@ -533,6 +551,10 @@ impl WalletService {
|
|||
})
|
||||
.collect();
|
||||
let asset_specs: Vec<AssetSpec> = assets.into_iter().map(Into::into).collect();
|
||||
let datum_bytes = match datum_inline_cbor_hex.as_deref() {
|
||||
Some(s) => Some(hex_decode(s).map_err(|e| format!("decode datum: {e}"))?),
|
||||
None => None,
|
||||
};
|
||||
|
||||
let unsigned = build_unsigned_payment_with_assets(
|
||||
self.inner.network,
|
||||
|
|
@ -541,6 +563,7 @@ impl WalletService {
|
|||
&to_address,
|
||||
lovelace,
|
||||
&asset_specs,
|
||||
datum_bytes.as_deref(),
|
||||
&ProtocolParams::default(),
|
||||
)
|
||||
.map_err(|e| format!("build: {e}"))?;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue