@@ -459,8 +459,21 @@ instance IsShelleyBasedEra era => FromJSON (TxOut CtxTx era) where
459459 ShelleyBasedEraConway -> parseBabbageOnwardsTxOut BabbageEraOnwardsConway o
460460 ShelleyBasedEraDijkstra -> parseBabbageOnwardsTxOut BabbageEraOnwardsDijkstra o
461461 where
462- -- Parse TxOut for Babbage+ eras
463- -- Handles both Alonzo-style (datumhash/datum) and Babbage-style (inlineDatumhash/inlineDatum) fields
462+ -- Parse TxOut for Babbage+ eras (Babbage, Conway, Dijkstra)
463+ --
464+ -- MOTIVATION: This unified helper eliminates ~100 lines of duplication that previously
465+ -- existed across the three Babbage+ era cases.
466+ --
467+ -- DESIGN: Uses a two-phase parsing strategy:
468+ -- 1. Parse Alonzo-style fields (datumhash/datum) via alonzoTxOutParser
469+ -- 2. Parse Babbage-style fields (inlineDatumhash/inlineDatum) via parseInlineDatum
470+ -- 3. Reconcile both via reconcileDatums, which validates no conflicting datums exist
471+ --
472+ -- This approach maintains backwards compatibility - old JSON with only Alonzo fields
473+ -- still parses correctly, while new JSON can use inline datums.
474+ --
475+ -- ASSUMPTION: BabbageEraOnwards will always cover exactly these three eras. If a new
476+ -- era is added, this code must be updated.
464477 parseBabbageOnwardsTxOut
465478 :: BabbageEraOnwards era
466479 -> Aeson. Object
@@ -472,7 +485,20 @@ instance IsShelleyBasedEra era => FromJSON (TxOut CtxTx era) where
472485 reconcileDatums w alonzoTxOut inlineDatum mReferenceScript
473486
474487 -- Parse inline datum fields from JSON object
475- -- Handles both inlineDatumhash and inlineDatum fields, validating they match
488+ --
489+ -- Handles both inlineDatumhash and inlineDatum fields, validating they match.
490+ --
491+ -- CRITICAL DISTINCTION: Babbage era uses scriptDataJsonToHashable (returns HashableScriptData)
492+ -- while Conway+ uses scriptDataFromJson (returns ScriptData). This difference exists because
493+ -- Babbage required preserving the original CBOR encoding for hash validation, while Conway+
494+ -- can reconstruct it.
495+ --
496+ -- VALIDATION: When both hash and datum are present, we verify the datum hashes to the
497+ -- provided hash. This catches malformed JSON where they don't match.
498+ --
499+ -- POTENTIAL ISSUE: The wildcard pattern (_ -> scriptDataFromJson) assumes all non-Babbage
500+ -- eras in BabbageEraOnwards use scriptDataFromJson. If a future era needs different handling,
501+ -- this must be updated to explicitly match all constructors.
476502 parseInlineDatum
477503 :: BabbageEraOnwards era
478504 -> Aeson. Object
@@ -500,7 +526,22 @@ instance IsShelleyBasedEra era => FromJSON (TxOut CtxTx era) where
500526 " Should not be possible to create a tx output with either an inline datum hash or an inline datum"
501527
502528 -- Reconcile Alonzo-style and Babbage-style datums and reference scripts
503- -- This handles the two-phase parsing where both old and new style fields may be present
529+ --
530+ -- This handles the two-phase parsing where both old and new style fields may be present.
531+ --
532+ -- BACKWARDS COMPATIBILITY: Accepts JSON with either:
533+ -- - Only Alonzo fields (datumhash/datum) - common in older transactions
534+ -- - Only Babbage fields (inlineDatumhash/inlineDatum) - modern format
535+ -- - Neither (TxOutDatumNone) - simple payment outputs
536+ --
537+ -- ERROR HANDLING: If *both* Alonzo and Babbage style datums are present, this is a
538+ -- malformed JSON and we fail with a detailed error message showing both datums.
539+ -- This should never happen in correctly formed JSON but protects against corruption.
540+ --
541+ -- EXHAUSTIVENESS: The eraName helper now matches directly on BabbageEraOnwards GADT
542+ -- constructors instead of converting to ShelleyBasedEra. This allows the compiler to
543+ -- verify exhaustiveness - if a new era is added to BabbageEraOnwards, this will fail
544+ -- to compile, forcing developers to update the code.
504545 reconcileDatums
505546 :: BabbageEraOnwards era
506547 -> TxOut CtxTx era
@@ -533,6 +574,9 @@ instance IsShelleyBasedEra era => FromJSON (TxOut CtxTx era) where
533574 Just anyScript -> return $ ReferenceScript w anyScript
534575 return $ TxOut addr v finalDat finalRefScript
535576 where
577+ -- Pattern match directly on GADT instead of converting to ShelleyBasedEra.
578+ -- This enables exhaustiveness checking - adding a new era to BabbageEraOnwards
579+ -- will cause a compile error here, preventing bugs from incomplete updates.
536580 eraName = case w of
537581 BabbageEraOnwardsBabbage -> " Babbage"
538582 BabbageEraOnwardsConway -> " Conway"
0 commit comments